Уровни изоляции транзакций в Postgresql

03 Май 2023 , 6270

Транзакции — это фундаментальное понятие во всех СУБД. Суть транзакции в том, что она объединяет последовательность действий в одну операцию «всё или ничего»..

Транза́кция (англ. 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 ₽ на пустой счет клиента, но не фиксирует изменение. Другая транзакция читает состояние счета (обновленное, но не зафиксированное) и позволяет клиенту снять наличные — несмотря на то, что первая транзакция прерывается и отменяет свои изменения, так что никаких денег на счете клиента нет.

Грязное чтение допускается стандартом на уровне . Но Postgresql не реализует уровень Read Uncommitted. и поэтому с аномалией "грязное чтение" мы не будет встречаться на практике.

Неповторяющееся чтение (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

comments powered by Disqus

Подписка

Подпишитесь на наш список рассылки, чтобы получать обновления из блога

Рубрики

Теги