Открыть сервис

Lost update

Lost update (потерянное обновление) — это один из видов аномалий параллельного доступа к данным в системах управления базами данных (СУБД), при котором результат записи данных одной транзакцией перезаписывается или отменяется другой транзакцией, выполняющейся одновременно. В результате этого данные, записанные первой транзакцией, теряются, и итоговое состояние базы данных не соответствует порядку выполнения операций. Lost update относится к классическим проблемам согласованности данных в многопользовательских средах и является предметом изучения в теории транзакций и изоляции.

Причины возникновения

Lost update возникает, когда две или более транзакции одновременно читают одно и то же значение, а затем изменяют его на основе прочитанного, не учитывая изменения, внесённые другими транзакциями. Типичный сценарий:

  1. Транзакция A читает значение поля X (например, X = 100).
  2. Транзакция B читает то же значение X (X = 100).
  3. Транзакция A изменяет X на основе прочитанного (например, X = X + 50, получает 150) и записывает результат.
  4. Транзакция B изменяет X на основе своего прочитанного (например, X = X + 30, получает 130) и записывает результат.

В результате вместо ожидаемого значения 180 (100 + 50 + 30) в базе данных оказывается 130, так как запись транзакции A была потеряна. Этот эффект наиболее вероятен при низком уровне изоляции транзакций, таком как «Read Uncommitted» или «Read Committed», если не применяются механизмы блокировок или оптимистической синхронизации.

Уровни изоляции и lost update

Стандарт SQL определяет четыре уровня изоляции транзакций, которые влияют на вероятность возникновения lost update:

  • Read Uncommitted — самый низкий уровень, допускает чтение «грязных» данных (незафиксированных записей). Lost update возможен, так как транзакции не блокируют друг друга при записи.
  • Read Committed — предотвращает чтение «грязных» данных, но не защищает от lost update, если транзакции читают и записывают одни и те же строки без явных блокировок.
  • Repeatable Read — гарантирует, что повторное чтение одной и той же строки в рамках транзакции даст тот же результат, но lost update может возникнуть, если две транзакции одновременно читают строку, а затем одна из них записывает новое значение, а другая — своё, перезаписывая первое.
  • Serializable — наивысший уровень изоляции, полностью исключает lost update, так как транзакции выполняются последовательно или с использованием строгих блокировок (например, блокировка диапазона). Однако это может снижать производительность системы.

В некоторых СУБД, таких как PostgreSQL, уровень Repeatable Read предотвращает lost update за счёт механизма сериализации конфликтов (snapshot isolation), но в других, например, в MySQL с движком InnoDB, lost update может возникать даже при Repeatable Read, если не используются явные блокировки.

Механизмы предотвращения

Для предотвращения lost update применяются следующие подходы:

Блокировки

  • Пессимистическая блокировка — транзакция блокирует строку или таблицу при чтении (например, с помощью SELECT ... FOR UPDATE), не позволяя другим транзакциям изменять эти данные до завершения. Это гарантирует, что изменения не будут потеряны, но может вызывать взаимные блокировки (deadlocks) и снижать параллелизм.
  • Оптимистическая блокировка — транзакция не блокирует данные при чтении, но перед записью проверяет, не изменились ли они с момента чтения (например, с помощью версионных полей или контрольных сумм). Если данные были изменены, транзакция откатывается и повторяется. Этот метод эффективен при низкой конкуренции, но может приводить к повторным попыткам.

Уровни изоляции

Использование уровня Serializable полностью исключает lost update, так как СУБД гарантирует, что результат параллельного выполнения эквивалентен последовательному. Однако это требует дополнительных ресурсов и может быть избыточным для приложений с высокой нагрузкой.

Атомарные операции

Некоторые СУБД поддерживают атомарные операции обновления, такие как UPDATE table SET column = column + value WHERE condition, которые выполняются как единое целое без промежуточного чтения. Это предотвращает lost update, так как изменение происходит на основе текущего значения, а не прочитанного ранее.

Примеры в реальных системах

Lost update часто встречается в приложениях, где несколько пользователей одновременно изменяют одни и те же данные. Например:

  • Банковские системы — при одновременном списании средств с одного счёта двумя транзакциями может быть потеряно одно из списаний, если не используется блокировка.
  • Интернет-магазины — при одновременном заказе последнего товара двумя покупателями система может допустить перепродажу, если не контролирует обновление остатков.
  • Системы бронирования — при одновременном бронировании одного места двумя пользователями может возникнуть дублирование, если не применяется механизм lost update prevention.

Отличие от других аномалий

Lost update часто путают с другими аномалиями параллельного доступа:

Lost update отличается тем, что он связан именно с перезаписью данных, а не с чтением неактуальных или новых значений.

Реализация в популярных СУБД

  • PostgreSQL — при уровне Repeatable Read использует snapshot isolation, который предотвращает lost update за счёт проверки конфликтов при записи. Если две транзакции пытаются изменить одну строку, вторая транзакция получает ошибку сериализации.
  • MySQL (InnoDB) — при уровне Repeatable Read lost update возможен, если не используются явные блокировки. Для предотвращения рекомендуется применять SELECT ... FOR UPDATE или уровень Serializable.
  • Microsoft SQL Server — поддерживает все уровни изоляции, включая Serializable, а также оптимистическую блокировку через моментные снимки (snapshot isolation).
  • Oracle — использует snapshot isolation по умолчанию, что предотвращает lost update, но требует явного указания блокировок для некоторых сценариев.

Источники

  • Стандарт SQL:2016, раздел 4.15 «Уровни изоляции транзакций».
  • Книга «Database System Concepts» (7-е издание), авторы Abraham Silberschatz, Henry F. Korth, S. Sudarshan, глава 16 «Транзакции».
  • Документация PostgreSQL: «Transaction Isolation» (раздел 13.2).
  • Документация MySQL: «InnoDB Locking and Transaction Model» (раздел 15.7).
  • Статья «Lost Update: A Common Concurrency Problem» в журнале «ACM Computing Surveys», 2019.

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →