Уровни изоляции транзакций в Postgresql
Транзакции — это фундаментальное понятие во всех СУБД. Суть транзакции в том, что она объединяет последовательность действий в одну операцию «всё или ничего»..
Транза́кция (англ. transaction) — группа последовательных операций с базой данных, которая представляет собой логическую единицу работы с данными. Транзакция может быть выполнена либо целиком и успешно, соблюдая целостность данных и независимо от параллельно идущих других транзакций, либо не выполнена вообще, и тогда она не должна произвести никакого эффекта.
Таким образом, транзакцией называется множество операций, которые переводят базу данных из одного корректного состояния в другое корректное состояние (согласованность) при условии, что транзакция выполнена полностью (атомарность) и без помех со стороны других транзакций (изоляция). Это определение объединяет требования, стоящие за первыми тремя буквами акронима ACID: Atomicity , Consistency, Isolation. Они настолько тесно связаны друг с другом, что рассматривать их по отдельности просто нет смысла. На самом деле сложно отделить и требование долговечности (Durability), ведь при крахе системы в ней остаются изменения незафиксированных транзакций, а с ними приходится что-то делать, чтобы восстановить согласованность данных.
Стандарт SQL определяет четыре уровня изоляции транзакций:
Read Uncommitted
Read Committed
Repeatable Read
Serializable
Эти уровни определяются перечислением аномалий, которые допускаются или не допускаются при одновременном выполнении транзакций. Поэтому разговор об уровнях придется начать с аномалий.
Аномалии в стандарте SQL
Потерянное обновление (lost update)
Аномалия потерянного обновления (lost update) возникает, когда две транзакции читают одну и ту же строку таблицы, затем одна из них обновляет эту строку, после чего вторая обновляет эту же строку, не учитывая изменений, сделанных первой транзакцией.
Например, две транзакции собираются увеличить сумму на одном и том же счете на 100 ₽. Первая транзакция читает текущее значение (1000 ₽), затем вторая транзакция читает то же самое значение. Первая увеличивает сумму (получается 1100 ₽) и записывает в базу это новое значение. Вторая поступает так же: получает те же 1100 ₽ и записывает их. В результате клиент потерял 100 ₽.
Потерянное обновление не допускается стандартом ни на одном уровне изоляции.
Грязное чтение (Dirty read)
Аномалия грязного чтения (dirty read) возникает, когда транзакция читает еще не зафиксированные изменения, сделанные другой транзакцией. Например, первая транзакция переводит 100 ₽ на пустой счет клиента, но не фиксирует изменение. Другая транзакция читает состояние счета (обновленное, но не зафиксированное) и позволяет клиенту снять наличные — несмотря на то, что первая транзакция прерывается и отменяет свои изменения, так что никаких денег на счете клиента нет.
Грязное чтение допускается стандартом на уровне
Неповторяющееся чтение (Nonrepeatable read)
Аномалия неповторяющегося чтения (nonrepeatable read) возникает, когда транзакция читает одну и ту же строку два раза, а в промежутке между чтениями вторая транзакция изменяет (или удаляет) эту строку и фиксирует изменения. Тогда первая транзакция получит разные результаты.
Например, пусть правило согласованности запрещает отрицательные суммы на счетах клиентов. Первая транзакция собирается уменьшить сумму на счете на 100 ₽. Она проверяет текущее значение, получает 1000 ₽ и решает, что уменьшение возможно. В это время вторая транзакция уменьшает сумму на счете до нуля и фиксирует изменения. Если бы теперь первая транзакция повторно проверила сумму, она получила бы 0 ₽ (но она уже приняла решение уменьшить значение, и счет «уходит в минус»).
Неповторяющееся чтение допускается стандартом на уровнях Read Uncommitted и Read Committed.
Фантомное чтение (Phantom read)
Аномалия фантомного чтения (phantom read) возникает, когда одна транзакция 2 раза читает набор строк по одинаковому условию, а в промежутке между чтениями другая транзакция добавляет строки, удовлетворяющие этому условию, и фиксирует изменения. Тогда первая транзакция получит разные наборы строк. Например, пусть правило согласованности запрещает клиенту иметь более трех счетов. Первая транзакция собирается открыть новый счет, проверяет их текущее количество (скажем, два) и решает, что открытие возможно. В это время вторая транзакция тоже открывает клиенту новый счет и фиксирует изменения. Если бы теперь первая транзакция перепроверила количество, она получила бы три (но она уже выполняет открытие еще одного счета, и у клиента их оказывается четыре).
Фантомное чтение допускается стандартом на уровнях Read Uncommitted, Read Committed и Repeatable Read.
Отсутствие аномалий и Serializable
Стандарт определяет и уровень, на котором не допускаются никакие аномалии — это уровень Serializable. И это совсем не то же самое, что запрет на потерянное обновление и на грязное, неповторяющееся и фантомное чтение. Дело в том, что существует значительно больше известных аномалий, чем перечислено в стандарте, и еще неизвестное число пока неизвестных.
Уровень Serializable должен предотвращать любые аномалии. Это означает, что на таком уровне разработчику приложения не надо думать об изоляции. Если транзакции выполняют корректные последовательности операторов, работая в одиночку, данные останутся согласованными и при одновременной работе этих транзакций.
Из вышесказанного следует следующая картина
Уровень изоляции | Грязное чтение(Dirty Read) | Неповторяемое чтение(Nonrepeatable Read) | Фантомное чтение(Phantom Read) | Другие аномалии |
---|---|---|---|---|
Read uncommitted | Разрешено , но не реализовано Postgres | Возможно | Возможно | Возможно |
Read committed | Невозможно | Возможно | Возможно | Возможно |
Repeatable read | Невозможно | Невозможно | Разрешено , но не реализовано Postgres | Возможно |
Serializable | Невозможно | Невозможно | Невозможно | Невозможно |
Уровни изоляции транзакций в PostgreSQL.
В PostgreSQL вы можете запросить любой из четырех стандартных уровней изоляции транзакций, но внутренне реализованы только три различных уровня изоляции, т. е. режим PostgreSQL Read Uncommitted ведет себя как Read Committed. Это потому, что это единственный разумный способ сопоставить стандартные уровни изоляции с многоверсионной архитектурой управления параллелизмом PostgreSQL.
Read Committed
Read Committed — это уровень изоляции по умолчанию в PostgreSQL. Когда транзакция использует этот уровень изоляции, запрос SELECT (без предложения FOR UPDATE/SHARE) видит только данные, зафиксированные до начала запроса; он никогда не видит ни незафиксированные данные, ни изменения, зафиксированные во время выполнения запроса параллельными транзакциями.
Repeatable Read
Уровень изоляции Repeatable Read видит только данные, зафиксированные до начала транзакции; он никогда не видит ни незафиксированные данные, ни изменения, зафиксированные во время выполнения транзакции параллельными транзакциями. (Однако запрос видит последствия предыдущих обновлений, выполненных в рамках его собственной транзакции, даже если они еще не зафиксированы.)
Этот уровень отличается от Read Committed тем, что запрос в повторяемой транзакции чтения видит моментальный снимок в момент начала первого оператора, не управляющего транзакцией в транзакции, а не в момент начала текущего оператора в транзакции. Таким образом, последовательные команды SELECT в рамках одной транзакции видят одни и те же данные, т. е. не видят изменений, сделанных другими транзакциями, зафиксированными после запуска их собственной транзакции. Приложения, использующие этот уровень, должны быть готовы к повторным попыткам транзакций из-за сбоев сериализации.
Serializable
Уровень изоляции Serializable обеспечивает самую строгую изоляцию транзакций. Этот уровень эмулирует последовательное выполнение транзакций для всех зафиксированных транзакций; как если бы транзакции выполнялись одна за другой, последовательно, а не одновременно. Однако, как и уровень Repeatable Read, приложения, использующие этот уровень, должны быть готовы к повторным попыткам транзакций из-за сбоев сериализации. Фактически, этот уровень изоляции работает точно так же, как Repeatable Read, за исключением того, что он также отслеживает условия, которые могут привести к тому, что выполнение параллельного набора сериализуемых транзакций будет вести себя несовместимо со всеми возможными последовательными (по одному) выполнениями этих транзакций.
Заключение
Read Committed — это уровень изоляции по умолчанию в PostgreSQL
Стандарт SQL определяет 4 уровня изоляции транзакций, но Postgresql реализует только 3 уровня изоляции (Read Committed, Repeatable Read, Serializable).
Read Uncommitted в PostgreSQL ведет себя как Read Committed