Оптимистическая блокировка
Оптимистическая блокировка (англ. optimistic locking) — это стратегия управления конкурентным доступом к данным, при которой операции чтения выполняются без захвата ресурсов (без установки блокировок), а проверка конфликтов осуществляется только на этапе записи (коммита транзакции). Данный подход основан на предположении, что конфликты между одновременными транзакциями происходят редко, и потому накладные расходы на блокировку в большинстве случаев избыточны. Противоположностью является пессимистическая блокировка, при которой ресурс блокируется с самого начала операции.
Концепция и принцип работы
Основная идея оптимистической блокировки заключается в том, чтобы не препятствовать одновременному доступу к данным, а проверять их целостность непосредственно перед фиксацией изменений. Технически это реализуется через механизм версионирования данных или временных меток.
Каждая запись или агрегат данных снабжается дополнительным полем, хранящим версию. При чтении данных считывается и текущая версия. Когда транзакция пытается записать изменения, она проверяет, совпадает ли версия данных в БД с версией, прочитанной на старте. Если версии совпадают, это означает, что другие транзакции не модифицировали данные после прочтения — запись разрешается, а версия увеличивается. Если версии различаются, это означает, что другой процесс уже изменил данные, и текущая транзакция должна быть отменена (rolled back) или выполнена повторно.
Типичные механизмы реализации
- Счётчик версий (версионное поле). Целочисленное поле, которое увеличивается на 1 при каждой успешной модификации записи.
- Метка времени (timestamp). Поле хранит время последнего обновления. Сравнение выполняется с точностью до установленного диапазона (например, до миллисекунды).
- Хэш-сумма (хеш значения полей). Вычисляется как криптографическая или простая контрольная сумма от всех или некоторых полей записи. При записи новая хэш-сумма сравнивается с той, что была вычислена при чтении.
Алгоритм в общем виде
- Считывание состояния. Транзакция A считывает данные и их версию (V1).
- Модификация. Транзакция A выполняет вычисления и изменяет данные в локальной памяти (клиентской или серверной). Блокировки на уровне СУБД не устанавливаются.
- Запись с проверкой. Транзакция A формирует SQL-запрос на обновление с условием
WHERE id = X AND version = V1. - Оценка результата. Если количество строк, затронутых запросом UPDATE, равно 1, это означает, что версия (V1) не изменилась — изменения фиксируются, версия увеличивается до V2. Если количество строк равно 0, это говорит о том, что другая транзакция (B) уже изменила запись и подняла версию до V2 или выше — возникает конфликт.
- Обработка конфликта. В случае обнаружения конфликта СУБД или приложение генерирует исключение. Пользователю или процессу предлагается повторить операцию (refetch + retry), либо отменить её.
Области применения
Оптимистическая блокировка наиболее эффективна в системах, где:
- Низкая конкуренция за данные. Конфликты одновременного доступа происходят редко; затраты на повторные попытки невелики.
- Операции чтения доминируют над операциями записи. Например, в информационно-справочных системах, каталогах продукции, веб-сайтах с большим количеством просмотров.
- Необходима высокая скорость ответа на чтение. Отсутствие необходимости захватывать блокировки на стадии чтения существенно снижает задержки.
- Приложение работает в ненадёжных сетях или с отключающимися клиентами. Например, в веб-приложениях с HTTP-сессиями, мобильных приложениях или системах с микросервисами, где ACM (или долгая блокировка БД) нежелательна.
Преимущества и недостатки
Преимущества:
- Высокая производительность при чтении. Отсутствие накладных расходов на захват и удержание блокировок.
- Отсутствие взаимных блокировок (deadlocks). Поскольку блокировки не удерживаются на время ожидания, ситуации двух транзакций, ожидающих освобождения ресурса друг друга, не возникает в классическом виде. Возможна лишь ситуация «livelock» (транзакции постоянно откатываются из-за конкуренции).
- Простота реализации на уровне приложения. Механизм проверки версии легко реализуется в коде или через ORM (Hibernate, Entity Framework, Eloquent).
Недостатки:
- Риск конфликтов при высокой конкуренции. Если множество транзакций одновременно пытаются изменить одну и ту же запись, это приведёт к частым откатам и повторным попыткам, что снижает общую производительность.
- Проблема «lost update» (потерянное обновление). Если механизм версионирования отсутствует или реализован некорректно (например, без атомарности), возможна потеря изменений одних транзакций из-за перезаписи другими.
- Необходимость повторных попыток для отказавших транзакций. Требует написания дополнительной логики в приложении (цикл retry с таймаутами), иначе пользовательский запрос может завершиться ошибкой.
- Ограниченная масштабируемость при частых записях в одни и те же данные. При многократных попытках записи (неудачных) нагрузка на СУБД растёт.
Сравнение с пессимистической блокировкой
| Характеристика | Оптимистическая блокировка | Пессимистическая блокировка |
|---|---|---|
| Момент блокировки | Только на этапе фиксации (commit) | Сразу при чтении или начале транзакции |
| Накладные расходы на чтение | Низкие | Высокие (захват и удержание блокировки) |
| Накладные расходы на запись | Средние (дополнительная проверка + возможная отмена) | Низкие (ресурс уже гарантированно не изменится) |
| Риск deadlock | Практически отсутствует | Есть (особенно при смешанных блокировках) |
| Применимость | Низкая конкуренция, много чтений | Высокая конкуренция, много записей, финансы |
| Сложность реализации | Средняя (версионирование + retry) | Высокая (правильный выбор уровней изоляции) |
Примеры использования
- Системы управления версиями (Git, Mercurial). При внесении изменений в файл сначала загружается его последняя версия, а при попытке вливания (merge) проверяется, не изменилась ли исходная версия в репозитории. Если да — возникает конфликт, требующий разрешения.
- Реляционные СУБД с уровнем изоляции REPEATABLE READ или SERIALIZABLE в сочетании с программной проверкой версии. Например, в PostgreSQL при использовании механизма
SELECT ... FOR UPDATE— это уже пессимистический подход, но сам сервер применяет оптимистический MVCC (Multi-Version Concurrency Control) для чтения. - Современные ORM-фреймворки:
- В Hibernate (Java) — через аннотацию
@Version. - В Entity Framework (C#) — через свойство
[Timestamp]илиIsConcurrencyToken. - В Ruby on Rails (
ActiveRecord) — черезlock_optimistically. - NoSQL-базы данных (например, Cassandra, MongoDB, Couchbase). Используют оптимистическую блокировку через механизмы CAS (Compare-and-Swap) или conditional updates на уровне документов.
Критика и ограничения
Основным ограничением оптимистической блокировки является невозможность её применения в системах, где требуется гарантированная атомарность операций при очень высокой конкуренции (например, в высоконагруженных интернет-магазинах при продаже ограниченных тиражей). В таких случаях частые откаты транзакций могут полностью парализовать запись. Кроме того, при оптимистическом подходе сложно контролировать долгие транзакции, состоящие из нескольких шагов, так как между чтением и записью проходит много времени, увеличивая окно конфликта.
Некоторые исследователи указывают на то, что оптимистическая блокировка может приводить к каскадному откату транзакций (cascading aborts), поскольку отмена одной транзакции может затронуть множество других, прочитавших её незафиксированные изменения. Однако в современных СУБД (PostgreSQL, Oracle) эта проблема решается на уровне архитектуры MVCC.
Интересные факты
- Термин «оптимистическая блокировка» был введён в научный оборот в 1979 году Дэном Розенкранцем (Dan Rosenkrantz) и его коллегами в контексте теории баз данных. Ранее сама концепция существовала в операционных системах (например, при журналировании файловых систем).
- В некоторых NoSQL-решениях (например, Riak) оптимистическая блокировка является не просто стратегией, а единственным доступным способом обеспечения согласованности данных (eventual consistency).
- В веб-приложениях, где HTTP-сессии могут быть разорваны, оптимистическая блокировка часто является единственным практичным решением, так как сервер не может удерживать блокировку БД на время паузы между запросами.
Источники
- Rosenkrantz, D. J., Stearns, R. E., & Lewis, P. M. (1979). System level concurrency control for distributed database systems. ACM Transactions on Database Systems (TODS).
- Gray, J., & Reuter, A. (1992). Transaction Processing: Concepts and Techniques. Morgan Kaufmann.
- Fowler, M. (2002). Patterns of Enterprise Application Architecture. Addison-Wesley.
- Silberschatz, A., Korth, H. F., & Sudarshan, S. (2020). Database System Concepts (7th ed.). McGraw-Hill.
- Официальная документация PostgreSQL (раздел «Concurrency Control»).
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →