Изолированность транзакции означает что
Уровни изолированности транзакций для самых маленьких
Сегодня хотел бы довести крайне интересный, но часто покрытый тайнами для обычных смертных программистов раздел базы данных (БД) — уровни изолированности транзакций. Как показывает практика, многие люди, связанные с IT, в частности с работой с БД, слабо понимают зачем нужны эти уровни и как их можно использовать себе во благо.
Немного теории
Подготовка окружения
Для примеров была выбрана СУБД MySQL. PostgreSQL мог бы тоже использоваться, но он не поддерживает уровень изоляции read uncommitted, и использует вместо него уровень read committed. Да и как оказалось, разные СУБД по-разному воспринимают уровни изолированности. Могут иметь разнообразные нюансы в обеспечении изоляции, иметь дополнительные уровни или не иметь общеизвестных.
Создадим окружение с помощью готового образа MySQL с Docker Hub. И заполним базу данными.
Рассмотрим как работают уровни и их особенности.
Примеры будем выполнять на 2 параллельно исполняющихся транзакциях. Условно транзакцию в левом окне будем называть транзакция 1 (Т1), в правом окне — транзакция 2 (Т2).
Read uncommitted
Уровень, имеющий самую плохую согласованность данных, но самую высокую скорость выполнения транзакций. Название уровня говорит само за себя — каждая транзакция видит незафиксированные изменения другой транзакции (феномен грязного чтения). Посмотрим какое влияние оказывают друг на друга такие транзакции.
Шаг 1. Начинаем 2 параллельные транзакции.
Шаг 2. Смотрим какая информация имеется у нас в начале.
Шаг 3. Теперь выполняем операции INSERT, DELETE, UPDATE в Т1, и посмотрим, что теперь видит другая транзакция.
Т2 видит данные другой транзакции, которые еще не были зафиксированы.
Шаг 4. И Т2 может получить какие-то данные.
Шаг 5. При откате изменений Т1, данные полученные Т2 окажутся ошибочными.
На данном уровне нельзя использовать данные, на основе которых делаются важные для приложения выводы и критические решения т.к выводы эти могут быть далеки от реальности.
Данный уровень можно использовать, например, для примерных расчетов чего-либо. Результат COUNT(*) или MAX(*) можно использовать в каких-нибудь нестрогих отчетах.
Другой пример это режим отладки. Когда во время транзакции, вы хотите видеть, что происходит с базой.
Read committed
Для этого уровня параллельно исполняющиеся транзакции видят только зафиксированные изменения из других транзакций. Таким образом, данный уровень обеспечивает защиту от грязного чтения.
Шаг 1 и Шаг 2 аналогичны предыдущему примеру.
Шаг 3. Также выполним 3 простейшие операции с таблицей accounts (Т1) и сделаем полную выборку из этих таблиц в обеих транзакциях.
И увидим, что феномен грязного чтения в Т2 отсутствует.
Шаг 4. Зафиксируем изменения Т1 и проверим, что теперь видит Т2.
Теперь Т2 видит все, что сделала Т1. Это так называемые феномен неповторяющегося чтения, когда мы видим обновленные и удаленные строки (UPDATE, DELETE), и феномен чтения фантомов, когда мы видим добавленные записи (INSERT).
Repeatable read
Уровень, позволяющий предотвратить феномен неповторяющегося чтения. Т.е. мы не видим в исполняющейся транзакции измененные и удаленные записи другой транзакцией. Но все еще видим вставленные записи из другой транзакции. Чтение фантомов никуда не уходит.
Снова повторяем Шаг 1 и Шаг 2.
Шаг 3. В Т1 выполняем запросы INSERT, UPDATE и DELETE. После, в Т2 пытаемся обновить ту же самую строку, которую обновили в Т1.
И получаем lock: T2 будет ждать, пока T1 не зафиксирует изменения или не откатится.
Шаг 4. Зафиксируем изменения, которые сделала Т1. И прочитаем снова данные из таблицы accounts в Т2.
Как видно, феноменов неповторяющегося чтения и чтения фантомов не наблюдается. Как же так, ведь по умолчанию, repeatable read позволяет нам предотвратить только феномен неповторяющегося чтения?
На самом деле в MySQL отсутствует эффект чтения фантомов для уровня repeatable read. И в PostgreSQL от него тоже избавились для этого уровня. Хотя в классическом представлении этого уровня, мы должны наблюдать этот эффект.
Небольшой абстрактный пример — сервис генерации подарочных сертификатов (кодов) и их использования. Например, злоумышленник сгенерировал себе код сертификата и пытается его активировать, пытаясь послать несколько запросов подряд на активацию купона. В таком случае у нас запустится несколько параллельно исполняемых транзакций, работающих с одним и тем же купоном. И в некоторых ситуациях может возникнуть двойная или даже тройная активация купона (пользователь получит 2x/3x бонусов). При repeatable read в данном случае возникнет lock и активация пройдет единожды, а в предыдущих 2 уровнях возможна многократная активация. Подобную проблему можно также решить с помощью запроса SELECT FOR UPDATE, который также заблокирует обновляемую запись (купон).
Serializable
Уровень, при котором транзакции ведут себя как будто ничего более не существует, никакого влияния друг на друга нет. В классическом представлении этот уровень избавляет от эффекта чтения фантомов.
Шаг 1. Начинаем транзакции.
Шаг 2. Т2 читаем таблицу accounts, затем Т1 пытаемся обновить данные прочитанные Т2.
Получаем lock: мы не можем изменить данные в одной транзакции, прочитанные в другой.
Шаг 3. И INSERT и DELETE ведет нас к lock’у в Т1.
Пока Т2 не завершит свою работу, мы не сможем работать с данными, которые она прочитала. Мы получаем максимальную согласованность данных, никакие лишние данные не зафиксируются. Цена за это медленная скорость транзакций из-за частых lock’ов поэтому при плохой архитектуре приложения это может сыграть с Вами злую шутку.
Выводы
В большинстве приложений уровень изолированности редко меняется и используется значение по умолчанию (например, в MySQL это repeatable read, в PostgreSQL — read committed).
Но периодически возникают, задачи, в которых поиск лучшего баланса между высокой согласованностью данных или скоростью выполнения транзакций может помочь решить некоторую прикладную задачу.
Уровни изоляции транзакций
— в деталях, но простыми словами.
Если понять 50%, а запомнить хотя бы 10% этой статьи, то на интервью ответить не составит труда.
В Аквелоне мы часто готовимся (и готовим) к различным проектам и интервью. Иногда всплывают темы, которые хотелось бы знать точнее, детальнее. Именно из такого исследования принципов ACID, а именно изолированности, и родилась эта статья.
Примечание: побочные эффекты и уровни изоляции параллельных транзакций в этой статье описаны согласно стандарту ANSI SQL. Способы реализации взяты из MS SQL.
Зачем это надо, и как оно появилось?
При изучении любого феномена, важно осознать в каких условиях он возник, что было “вокруг него” — тогда будет ясна мотивация создателей и цели, которые они преследовали.
За точку отсчёта возьмём начало семидесятых, когда Рэймонд Бойс (англ. Raymond Boyce, 1947–1974) и Дональд Чемберлин (англ. Donald D. Chamberlin) (род. 1944), работая в IBM, создают язык запросов SEQUEL (тот самые “сиквел”), позднее переименованный в SQL и впервые стандартизированный в 1986 как “SQL-86”.
Далее — понеслось. Сама IBM в этот язык и реляционную модель не поверила, зато её взяла за основу компания “Relational Software”, ныне всем известная как Oracle.
К середине восьмидесятых технический прогресс обеспечил возможность повсеместного использования реляционных БД, а к началу девяностых (рискну сказать “и по сей день”) они уже доминировали на рынке масштабного хранения и обработки данных.
Ещё тогда эти ребята придумали концепции транзакций, их свойства (ACID), и так далее. Одна из секций стандарта SQL как раз посвящена уровням изоляции, и даёт ответы на два главных вопроса:
Побочные эффекты
Стандарт даёт описания пяти побочных эффектов, сначала идут простые, затем более сложные. Под “сложностью” побочного эффекта я имею ввиду “сложнее его отловить и исправить”. Например отсутствие LOST UPDATE обеспечить проще, чем отсутствие DIRTY READ. Для понимания этих эффектов, надо держать в уме, что речь всегда идёт о взаимодействии двух и более транзакций одновременно, а сам побочный эффект — это когда желаемый результат не совпал с реальным.
Потерянное обновление / Lost Update
Когда несколько транзакций что-то обновили в БД, но по итогам результат такой, будто отработала лишь часть транзакций.
Самый опасный побочный эффект, по сути, полное отсутствие изоляции транзакций — две транзакции читают одну ячейку, записывают изменённое значение (одна вычитает стоимость мороженого, другая плату за квартиру). В итоге в ячейке значение от второй транзакции, а первой как будто и не было.
Грязное чтение / Dirty Read
Когда ваша транзакция может прочитать данные, которые были добавлены/изменены другой транзакцией, пока та ещё не вызвала COMMIT этих данных (ещё другая транзакция могла что-то удалить, тогда ваша транзакция перестанет это видеть).
Тоже опасный эффект—одна транзакция читает ячейку, обновляет её, другая считывает обновлённое значение, изменяет его, в это время первая транзакция откатывается, а вторая записывает результат, будто обе транзакции отработали.
Неповторяющееся чтение / Non-Repeatable Read
Когда одинаковый запрос в одной транзакции может вернуть разные данные; NON-REPEATABLE READ касается лишь только набора данных, который однажды уже был прочитан в транзакции — это значит, если вы прочитали первые 10 строк таблицы А, то NON-REPEATABLE READ случится, если вы заново прочитаете первые 10 строк таблицы А и получите другой результат;
Это уже менее опасный эффект, но более сложный в понимании. Допустим, одна транзакция считает полную стоимость услуги на основе тарифов, а другая меняет параллельно эти тарифы. Non-repeatable read случится, если последовательность действий будет такова:
Фантомное чтение / Phantom Reads
Когда: одинаковый запрос может вернуть НОВЫЕ строки, которые были закомичены из другой транзакции. Отличие от NON-REPEATABLE READ в том, что строки, которые мы уже разок прочитали остаются такими же, соответствнно здесь нет NON-REPEATABLE READ. Но при этом, запрос который сначала вернул 5 строк, может вернуть эти же пять строк, плюс ещё две свежие строки из другой транзакции — это PHANTOM READ;
Это ещё менее опасный эффект. Возникнуть он может в похожем на предыдущий примере, но если первая транзакция ещё ищет скидки, которые можно применить. Если сначала она найдёт три скидки, то Phantom Read возникнет, когда, впоследствии, этот же запрос может вернуть те же три скидки плюс одну новую.
Уровни изоляции
Уровни изоляции тоже описаны в стандарте, и они гарантируют отсутствие разных (с каждым разом всё более сложных) побочных эффектов.
При этом, “простые” эффекты включают в себя все более сложные, то есть если есть шанс на DIRTY READ, то и всё что сложнее его — можно тоже спокойно получить.
В прошлом сообщении эти эффекты описаны как раз в порядке возрастания “сложности”:
Исходя из этой таблицы можно определить режимы изоляции транзакций. Например: режим REPEATABLE READ — это такой режим, где гарантируется отсутствие любых побочных эффектов, кроме фантомных чтений.
Чем сложнее побочный эффект, тем сложнее его избежать. Соответственно, чем сильнее уровень изоляции, тем меньше производительность БД — потому что транзакциям чаще приходится ждать друг-друга.
Подходы к реализации уровней изоляции
Существует два глобально различных подхода к реализации изолированности: блокирование и версионирование.
Версионирование (snapshot) означает, что транзакции будут работать со своей копией данных, не влияя друг на друга, но впоследствии несколько изменённых копий надо будет как-то слить в одну. Строгость версионирования регулирует моменты (когда) и размеры (сколько данных копировать) этих копий.
Блокирование (lock) означает, что одна транзакция будет ждать другую, чтобы избежать побочных эффектов, в зависимости от строгости уровней изоляции этих транзакций. Различные виды блокировок обеспечивают более гранулярное блокирование, например, только по строкам, или только на небольшой кусочек транзакции, а не на всю.
В описаниях блокировок часто всплывает понятие оптимистичной и пессимистичной блокировки. Это два высокоуровневых термина, которые обозначают следующее:
Далее, рассмотрим подходы к блокировке/версионированию в MS SQL.
Реализация уровней изоляции в MS SQL
Давайте посмотрим, как уровни изоляции реализованы в Microsoft SQL Server.
Примечание 1: здесь начинается слабодокументированная зона, и я могу ошибаться, но готов вносить исправления и улучшать.
Примечание 2: возможно, эта глава будет расширена описанием shared-, exclusive-, range-locks: что это такое, в каком режиме и как оно используется.
Read Uncommitted
Ничего не происходит в режиме read uncomitted. То есть ничего не блокируется и не создаются снэпшоты, транзакция просто читает всё что хочет. По смыслу, этот режим можно отнести к (очень) оптимистичному — блокировки редки и коротки.
Read Committed
Read committed (то есть отсутсвие dirty reads) обеспечивается блокировкой на запись данных (строк), которые мы пытаемся прочитать. Эта блокировка гарантирует, что мы подождём завершения транзакций, которые уже меняют наши данные, или заставим их подождать, пока мы будем читать. В итоге, мы точно прочитаем только данные, которые были закоммичены, избежав тем самым грязное чтение. Этот режим — типичный пример пессимистичной блокировки, так как мы блокируем данные на запись, даже если в них никто реально не пишет.
Read Committed Snapshot
Read commited snapshot (второй способ избежать dirty reads) обеспечивается версионированием блока данных, который мы читаем. Любое его параллельное изменение на затронет нашу версию, и нам достанутся данные на момент начала чтения. Таким образом грязное чтение отсутствует, и ещё отсутствуют какие-либо блокировки*. Этот режим скорее оптимистичный, так как заранее ничего не блокируется, и тут как раз может быть конфликт обновления.
Repeatable Read
Repeatable read (то есть отсутствие всего, кроме фантомных чтений) обеспечивается почти как read commited, за тем исключением, что блокировка на запись работает до конца транзакции, а не отдельной операции. Единожды “коснувшись” блока данных, транзакция блокирует его изменение до конца работы, что обеспечивает отсутствиуе dirty reads и non-repeatable reads. Это более пессимистичный вид блокировки, чем read committed, так как блокировка держится ещё дольше.
Serializable
Serializable (нет никаких побочных эффектов) обеспечивается блокировкой и на запись, и на чтение любого блока данных, с которым мы работаем. Блокируется даже вставка данных, которые могут попасть в блок, который мы прочитали. Таким образом, за счёт низкой конкурентности, обеспечивается отсутствие даже фантомных чтений. Это более пессимистичный вид блокировки, чем repeatable read, так как блокировка держится ещё дольше.
Snapshot
Snapshot обеспечивается созданием нашей отдельной версии данных, с которыми мы работаем, без блокировок*. Другие транзакции не будут иметь доступ к нашей версии, чем и обеспечивается отсутствие всего вплоть до фантомных чтений. Это оптимистичный вид блокировки, но менее оптимистичный, чем read committed snapshot, так как больше шансов получить конфликт обновления.
Примечания
Snapshot isolation transaction aborted due to update conflict. You cannot use snapshot isolation to access table ‘dbo.Table’ directly or indirectly in database ‘DB’ to update, delete, or insert the row that has been modified or deleted by another transaction. Retry the transaction or change the isolation level for the update/delete statement.
Заключение
Обо мне
Меня зовут Рустем, последние несколько лет работаю проект-менеджером в Аквелоне. Ещё у меня более 10-ти лет опыта разработки софта и других классных штук. Связаться со мной можно через twitter.com/rulikkk или подписаться на мой канал в tg: techno_ru.
Полезные ссылки
По этим ссылкам, а также пользуясь здравым смыслом, я и собрал статью:
MySQL: уровни изоляции транзакций
Добрый день, сообщество.
Кто-бы ни купил MySQL, она еще долго будет будоражить ресурсы Наших с Вами серверов — и это хорошо.
Что по Вашему покажет этот запрос?
А что покажет простейший SELECT во время выполнения текущей транзакции? Не ясно. Вот и придумали такие правила.
Первый READ UNCOMMITTED
Рассмотрим транзакцию выше. После INSERT данные сразу-же станут доступны для чтения. Тоесть еще до вызова COMMIT вне транзакции можно получить только что добавленные данные. В английской литературе это называется dirty read («грязное чтение»). Этот уровень редко используется на практике, да вообще редко кто меняет эти самые уровни.
Второй READ COMMTITED
В данном случае прочитать данные возможно только после вызова COMMIT. При чем внутри транзакции данные тоже будут еще не доступны.
Если рассмотреть транзакцию выше, то первый SELECT ничего не вернет, т.к. таблица у нас еще пустая и транзакция не подтверждена.
Третий REPEATABLE READ
Этот уровень используется по умолчанию в MySQL. Отличается от второго тем, что вновь добавленные данные уже будут доступны внутри транзакции, но не будут доступны до подтверждения извне.
Здесь может возникнуть теоретическая проблема «фантомного чтения». Когда внутри одной транзакции происходит чтение данных, другая транзакция в этот момент вставляет новые данные, а первая транзакция снова читает те-же самые данные.
И последний SERIALIZABLE
На данном уровне MySQL блокирует каждую строку над которой происходит какое либо действие, это исключает появление проблемы «фантомов». На самом деле смысла использовать этот уровень нет, т.к. InnoDB и менее популярный Falcon решают эту проблему.
Увидеть текущий уровень изоляции
Это всего лишь попытка вольного перевода самой обычной документации.
Спасибо.
Что будет в первой таблице, а что во второй?
В следующей статье я попытаюсь рассказать про Falcon и PBXT движки.
SQL-Ex blog
Новости сайта «Упражнения SQL», статьи и переводы
Уровни изоляции транзакций
Введение
Зачем нужна изоляция транзакций?
Установка правильного уровня изоляции для вашего приложения является весьма важным шагом при проектировании базы данных. Это не только помогает устранить блокировки таблиц при чтении, но также помогает ядру строить корректный план выполнения запроса, улучшая, тем самым, общую производительность.
Предположим, что вы работаете с базой данных OLTP, которая содержит таблицу с 50 миллионами записей. Существует множество сервисов, которые подключаются к базе данных для чтения и модификации данных. Еще есть панель управления, которая считывает данные из той же таблицы и представляет их рабочей группе для обработки. Важно отметить, что любое обновление или выборка в такой таблице повлечет за собой определенные затраты, поскольку данные огромны. Давайте рассмотрим сценарий, когда сервис находится в процессе обновления записей и в то же самое время срабатывает запрос панели управления. В этом случае, если уровень изоляции задан неверно, мы можем прочитать некоторые данные, которые, возможно, вообще не существуют. Это приведет к неверной интерпретации данных командой аналитиков и послужит принятию неверных решений.
Имеющиеся типы изоляции транзакций
SQL Server имеет 4 уровня изоляции.
1. READ UNCOMMITTED: означает, что транзакция в пределах текущей сессии может читать данные, которые модифицируются или удаляются другой транзакцией, но еще не зафиксированы. Этот уровень изоляции накладывает наименьшие ограничения, поскольку ядро базы данных не накладывает никаких разделяемых блокировок. В результате весьма вероятно, что транзакция прочитает данные, которые были вставлены, обновлены или удалены, но не будут зафиксированы в базе данных. Такой сценарий называется грязным чтением.
2. READ COMMITTED: Это установка по умолчанию для большинства запросов SQL Server. Она определяет, что транзакция в текущем сеансе не может читать данные, которые были модифицированы другой транзакцией. Тем самым при этой установке предотвращается грязное чтение.
3. REPEATABLE READ: С этой установкой транзакция не только может читать данные, которые зафиксированы другой модифицирующей транзакцией, но также накладывает ограничение, чтобы никакая другая транзакция не могла модифицировать данные, которые читаются, пока первая транзакция не завершит работу. Это устраняет проблему неповторяющихся чтений.
Изменение уровня изоляции в SSMS
Существует два способа установки изоляции транзакций в SSMS:
1. Использование GUI
2. Использование команд T-SQL
Использование GUI
1. Щелкнуть правой кнопкой мышки в окне запроса и выбрать «Query Options» (Параметры запроса).
2. Выбрать «Advances» (дополнительно) в группе «Execution» (выполнение) на левой панели.
3. Щелкнуть по выпадающему списку рядом с «SET TRANSACTION ISOLATION LEVEL».
4. Выбрать подходящий уровень изоляции из списка.
Использование команд T-SQL
Для установки каждого уровня изоляции есть отдельная команда. Все эти команды я показываю на скриншоте ниже. Эта установка применяется к текущей сессии и сохраняется до тех пор, пока не будет явно изменена.
Заключение
Обратные ссылки
Нет обратных ссылок
Комментарии
Показывать комментарии Как список | Древовидной структурой
Автор не разрешил комментировать эту запись
Уровни изоляции транзакций с примерами на PostgreSQL
Вступление
В стандарте SQL описывается четыре уровня изоляции транзакций — Read uncommited (Чтение незафиксированных данных), Read committed (Чтение зафиксированных данных), Repeatable read (Повторяемое чтение) и Serializable (Сериализуемость). В данной статье будет рассмотрен жизненный цикл четырёх параллельно выполняющихся транзакций с уровнями изоляции Read committed и Serializable.
Для уровня изоляции Read committed допустимы следующие особые условия чтения данных:
Неповторяемое чтение — транзакция повторно читает те же данные, что и раньше, и обнаруживает, что они были изменены другой транзакцией (которая завершилась после первого чтения).
Фантомное чтение — транзакция повторно выполняет запрос, возвращающий набор строк для некоторого условия, и обнаруживает, что набор строк, удовлетворяющих условию, изменился из-за транзакции, завершившейся за это время.
Что же касается Serializable, то данный уровень изоляции самый строгий, и не имеет феноменов чтения данных.
ACID или 4 свойства транзакций
Прежде чем приступим к рассмотрению уровней изоляции транзакции в паре слов вспомним об основных требованиях к транзакционной системе.
Atomicity (атомарность) — выражается в том, что транзакция должна быть выполнена в целом или не выполнена вовсе.
Consistency (согласованность) — гарантирует, что по мере выполнения транзакций, данные переходят из одного согласованного состояния в другое, то есть транзакция не может разрушить взаимной согласованности данных.
Isolation (изолированность) — локализация пользовательских процессов означает, что конкурирующие за доступ к БД транзакции физически обрабатываются последовательно, изолированно друг от друга, но для пользователей это выглядит, как будто они выполняются параллельно.
Durability (долговечность) — устойчивость к ошибкам — если транзакция завершена успешно, то те изменения в данных, которые были ею произведены, не могут быть потеряны ни при каких обстоятельствах.
Уровень изоляции Read Committed
По умолчанию в PostgreSQL уровень изоляции Read Committed. Такой уровень изоляции всегда позволяет видеть изменения внесённые успешно завершёнными транзакциями в оставшихся параллельно открытых транзакциях. В транзакции, работающей на этом уровне, запрос SELECT (без предложения FOR UPDATE/SHARE) видит только те данные, которые были зафиксированы до начала запроса; он никогда не увидит незафиксированных данных или изменений, внесённых в процессе выполнения запроса параллельными транзакциями. По сути запрос SELECT видит снимок базы данных в момент начала выполнения запроса. Однако SELECT видит результаты изменений, внесённых ранее в этой же транзакции, даже если они ещё не зафиксированы. Также заметьте, что два последовательных оператора SELECT могут видеть разные данные даже в рамках одной транзакции, если какие-то другие транзакции зафиксируют изменения после выполнения первого SELECT.
Суть уровня изоляции Read Committed показана на диаграмме 1.
Примечание: В таблице уже находится запись с первой версией данных (v1). Прошу воспринимать команды SELECT v1; — как команду возвращающую данные версии v1, а UPDATE v1 to v2; — как команду обновления данных с первой версии до второй.
Примечание. На диаграмме не показано действие запроса INSERT. В рамках данного уровня изоляции, строки добавленные, например в шаге 3, в Первой транзакции, были бы ВИДНЫ остальным транзакциям после завершения Первой транзакции.
Частичная изоляция транзакций, обеспечиваемая в режиме Read Committed, приемлема для множества приложений. Этот режим быстр и прост в использовании, однако он подходит не для всех случаев. Приложениям, выполняющим сложные запросы и изменения, могут потребоваться более строго согласованное представление данных, например Serializable.
Уровень изоляции Serializable
Изоляция уровня Serializable обеспечивает беспрепятственный доступ к базе данных транзакциям с SELECT запросами. Но для транзакций с запросами UPDATE и DELETE, уровень изоляции Serializable не допускает модификации одной и той же строки в рамках разных транзакций. При изоляции такого уровня все транзакции обрабатываются так, как будто они все запущены последовательно (одна за другой). Если две одновременные транзакции попытаются обновить одну и туже строку, то это будет не возможно. В таком случае PostgreSQL принудит транзакцию, вторую, да и все последующие, что пытались изменить строку к отмене (откату — ROLLBACK).
Суть уровня изоляции Serializable показана на диаграмме 2.
Примечание. На диаграмме не показано действие запроса INSERT. В рамках данного уровня изоляции, строки добавленные, например в шаге 3, в Первой транзакции, были бы НЕ ДОСТУПНЫ Второй, Третьей и Четвёртой транзакциям после завершения Первой транзакции. Также на диаграмме не показан результат ROLLBACK (Шаги 8 и 11). В случае если бы Вторая и Третья транзакции делали какие либо изменения над не заблокированными данными, то все эти изменения не были бы зафиксированы, так как транзакции завершаются неудачно (суть свойства — Atomicity).
Уровень изоляции Serializable гарантирует, что все затронутые в транзакции данные не будут изменены другими транзакциями. На этом уровне появление «фантомов» исключается, поэтому становятся возможными сложные конкурентные операции. На практике такой уровень изоляции требуется в учетных системах.
Для транзакций содержащих только SELECT запросы, использование уровня изоляции Serializable оправдывает себя тогда, когда вы не хотите видеть внесённые изменения параллельно завершёнными транзакциями в ходе работы текущей транзакции.
Аномалия сериализации (Потерянное обновление)
Ещё один феномен чтения данных, описывается тем, что результат успешной фиксации группы транзакций оказывается несогласованным при всевозможных вариантах исполнения этих транзакций по очереди.
Сориентируйте, пожалуйста, меня в комментариях, если я заблуждаюсь насчёт того, что аномалия сериализации и потерянное обновление связанные между собой феномены.
Документация на сайте PostgreSQL PRO пишет, что Read Committed допускает «Serialization Anomaly». Отечественная Wikipedia, не настаивая на то, что таблица относится именно к PostgreSQL, пишет, что Read Commited предотвращает аномалию сериализации. Английская Википедиа о таком феномене чтения данных умалчивает. Но немецкая Википедия приводит в своей версии таблицы феномен «Lost Updates», указывая на то, что Read Committed может быть не подвержен потере обновлений с дополнительной защитой через курсор (Cursor Stability). Украинская Википедия поддерживает русскоязычную версию статьи, испанская Википедия поддерживает английскую версию статьи. Англоязычная документация по PostgreSQL не отличается от документации с сайта PostgreSQL PRO.
Cursor Stability расширяет блокировочное поведение уровня READ COMMITED для SQL-курсоров, добавляя новую операцию чтения (Fetch) по курсору rc (означает read cursor, т.е. чтение по курсору) и требует, чтобы блокировка устанавливалась на текущем элементе курсора. Блокировка удерживается до тех пор, пока курсор не будет перемещен (пока не измениться его текущий элемент) или закрыт операцией фиксации. Естественно, транзакция, читающая по курсору, может изменить текущую строку (wc – запись по курсору), и в этом случае блокировка по записи этой строки будет сохраняться до тех пор, пока транзакция не зафиксируется, даже после передвижения курсора с последующей выборкой следующей строки.
Заключение
Понимание уровней изоляции транзакций является важным аспектом при обработке данных в любой многопользовательской СУБД. Уровни изоляции обладают четко определенными характеристиками и поведением. Более высокие уровни изоляции уменьшают возможности параллельной обработки данных и повышают риск взаимной блокировки процессов. Поэтому корректное использование уровней в зависимости от задач приложений всегда является выбором разработчика в зависимости от требований к обеспечению логической целостности данных, к скорости и к возможности параллельной многопользовательской обработки.