Оптимистическая и пессимистическая блокировка
Я понимаю разницу между оптимистической и пессимистической блокировками *. Может ли кто-нибудь объяснить мне, когда я буду использовать один из них вообще?
И меняется ли ответ на этот вопрос в зависимости от того, использую ли я хранимую процедуру для выполнения запроса?
* Но просто для проверки оптимистический означает "не блокировать таблицу во время чтения", а пессимистический означает "заблокировать таблицу во время чтения".
14 ответов
Оптимистическая блокировка - это стратегия, при которой вы читаете запись, записываете номер версии (другие методы для этого включают даты, временные метки или контрольные суммы / хэши) и проверяете, что версия не изменилась, прежде чем записывать запись обратно. Когда вы записываете запись обратно, вы фильтруете обновление версии, чтобы убедиться, что оно атомарное. (то есть не обновлялся между проверкой версии и записью записи на диск) и обновлением версии одним нажатием.
Если запись грязная (т. Е. Версия отличается от вашей), вы прерываете транзакцию, и пользователь может перезапустить ее.
Эта стратегия наиболее применима к системам большого объема и трехуровневым архитектурам, где вы не обязательно поддерживаете соединение с базой данных для вашего сеанса. В этой ситуации клиент фактически не может поддерживать блокировки базы данных, так как соединения берутся из пула, и вы не можете использовать одно и то же соединение для одного доступа к другому.
Пессимистическая блокировка - это когда вы блокируете запись для своего эксклюзивного использования до тех пор, пока не закончите с ней. Он имеет гораздо лучшую целостность, чем оптимистичная блокировка, но требует от вас осторожности при разработке приложения, чтобы избежать тупиковых ситуаций. Чтобы использовать пессимистическую блокировку, вам необходимо либо прямое соединение с базой данных (как это обычно бывает в двухуровневом клиент-серверном приложении), либо внешне доступный идентификатор транзакции, который можно использовать независимо от соединения.
В последнем случае вы открываете транзакцию с TxID, а затем повторно подключаетесь с использованием этого идентификатора. СУБД поддерживает блокировки и позволяет вам забрать сеанс обратно через TxID. Именно так работают распределенные транзакции, использующие протоколы двухфазной фиксации (такие как транзакции XA или COM+).
При разрешении конфликтов у вас есть два варианта:
- Вы можете попытаться избежать конфликта, и это то, что делает пессимистическая блокировка.
- Или вы можете допустить возникновение конфликта, но вам нужно обнаружить его при совершении транзакций, и именно это делает оптимистическая блокировка.
Теперь давайте рассмотрим следующую аномалию утерянного обновления:
Аномалия "Потерянное обновление" может произойти на уровне изоляции " Чтение зафиксировано".
На диаграмме выше мы видим, что Алиса считает, что может забрать у нее 40 account
но не понимает, что Боб только что изменил баланс аккаунта, и теперь на этом аккаунте осталось только 20.
Пессимистическая блокировка
Пессимистическая блокировка достигает этой цели за счет использования общей блокировки или блокировки чтения для учетной записи, поэтому Боб не может изменить учетную запись.
На схеме выше Алиса и Боб получат блокировку чтения на account
строка таблицы, которую прочитали оба пользователя. База данных получает эти блокировки на SQL Server при использовании Repeatable Read или Serializable.
Поскольку и Алиса, и Боб прочитали account
со значением PK 1
, ни один из них не может изменить его, пока один из пользователей не снимет блокировку чтения. Это связано с тем, что операция записи требует получения блокировки записи / монопольной блокировки, а блокировки совместного использования / чтения предотвращают блокировки записи / монопольной блокировки.
Только после того, как Алиса зафиксировала свою транзакцию и блокировка чтения была снята на account
ряд, Боб UPDATE
возобновит и применит изменение. Пока Алиса не снимет блокировку чтения, UPDATE Боба блокируется.
Дополнительные сведения о том, как платформы доступа к данным используют поддержку пессимистической блокировки базовой базы данных, см. В этой статье.
Оптимистическая блокировка
Оптимистическая блокировка допускает возникновение конфликта, но обнаруживает его при применении UPDATE Алисы по мере изменения версии.
На этот раз у нас есть дополнительный version
столбец. Вversion
столбец увеличивается каждый раз при выполнении UPDATE или DELETE, а также используется в предложении WHERE операторов UPDATE и DELETE. Чтобы это сработало, нам нужно выполнить SELECT и прочитать текущийversion
перед выполнением UPDATE или DELETE, поскольку в противном случае мы не знали бы, какое значение версии следует передать в предложение WHERE или увеличить.
Дополнительные сведения о том, как платформы доступа к данным реализуют оптимистичную блокировку, см. В этой статье.
Транзакции на уровне приложения
Системы реляционных баз данных появились в конце 70-х - начале 80-х годов, когда клиент, как правило, подключался к мэйнфрейму через терминал. Вот почему мы до сих пор видим, как системы баз данных определяют такие термины, как параметр SESSION.
В настоящее время через Интернет мы больше не выполняем операции чтения и записи в контексте одной и той же транзакции базы данных, и ACID уже недостаточно.
Например, рассмотрим следующий вариант использования:
Без оптимистической блокировки это потерянное обновление не могло быть обнаружено, даже если бы транзакции базы данных использовали Serializable. Это связано с тем, что чтение и запись выполняются в отдельных HTTP-запросах, следовательно, в разных транзакциях базы данных.
Таким образом, оптимистическая блокировка может помочь вам предотвратить потерю обновлений даже при использовании транзакций на уровне приложения, которые также включают время обдумывания пользователем.
Дополнительные сведения о транзакциях на уровне приложений или логических транзакциях можно найти в этой статье.
Вывод
Оптимистическая блокировка - очень полезный метод, и он отлично работает даже при использовании менее строгих уровней изоляции, таких как Read Committed, или когда операции чтения и записи выполняются в последующих транзакциях базы данных.
Обратной стороной оптимистической блокировки является то, что откат будет инициирован структурой доступа к данным при обнаружении OptimisticLockException
, поэтому теряется вся работа, которую мы сделали ранее выполняющейся в данный момент транзакцией.
Чем больше разногласий, тем больше конфликтов и больше вероятность прерывания транзакции. Откат может быть дорогостоящим для системы базы данных, поскольку ей необходимо отменить все текущие ожидающие изменения, которые могут включать как строки таблицы, так и записи индекса.
По этой причине пессимистическая блокировка может быть подходящей, когда конфликты случаются часто, поскольку она снижает вероятность отката транзакций.
Оптимистическая блокировка используется, когда вы не ожидаете много столкновений. Обычная операция обходится дешевле, но если коллизия ДЕЙСТВИТЕЛЬНО произойдет, вы заплатите более высокую цену за ее разрешение, поскольку транзакция отменяется.
Пессимистическая блокировка используется, когда ожидается столкновение. Транзакции, которые нарушают синхронизацию, просто блокируются.
Чтобы выбрать правильный механизм блокировки, вы должны оценить количество операций чтения и записи и соответственно спланировать
Оптимистичный предполагает, что ничего не изменится, пока вы читаете это.
Пессимистично предполагает, что что-то будет, и поэтому блокирует это.
Если не важно, что данные идеально читаются, используйте оптимизм. Вы можете получить странное "грязное" прочтение, но оно гораздо менее вероятно приведет к тупикам и тому подобному.
Большинство веб-приложений хорошо справляются с грязным чтением - в редких случаях данные не совсем соответствуют следующей перезагрузке.
Для точных операций с данными (как и во многих финансовых транзакциях) используйте пессимистический подход. Важно, чтобы данные были точно прочитаны, без каких-либо не показанных изменений - дополнительные затраты на блокировку того стоят.
Да, и сервер Microsoft SQL по умолчанию блокирует страницу - в основном, строку, которую вы читаете, и несколько по обе стороны. Блокировка строк более точна, но намного медленнее. Часто стоит установить транзакции на фиксацию или отсутствие блокировки, чтобы избежать тупиков при чтении.
В дополнение к тому, что уже было сказано, следует сказать, что оптимистическая блокировка имеет тенденцию улучшать параллелизм за счет предсказуемости. Пессимистическая блокировка имеет тенденцию уменьшать параллелизм, но является более предсказуемой.
Вы платите свои деньги и т. Д.
Я бы подумал еще об одном случае, когда пессимистическая блокировка была бы лучшим выбором.
Для оптимистической блокировки каждый участник модификации данных должен согласиться с использованием этого вида блокировки. Но если кто-то изменяет данные, не заботясь о столбце версии, это испортит саму идею оптимистической блокировки.
Есть в основном два самых популярных ответа. Первый в основном говорит
Для Optimistic требуется трехуровневая архитектура, в которой вы не обязательно поддерживаете соединение с базой данных для своего сеанса, тогда как Pessimistic Locking - это когда вы блокируете запись для своего эксклюзивного использования до тех пор, пока не закончите с ней. У него гораздо лучшая целостность, чем у оптимистичной блокировки, вам нужно либо прямое соединение с базой данных.
Оптимистическое (управление версиями) быстрее из-за отсутствия блокировок, но (пессимистическое) блокирование работает лучше, когда конкуренция высока, и лучше предотвращать работу, чем отбрасывать ее и начинать заново.
или же
Оптимистическая блокировка работает лучше всего при редких столкновениях
Как это написано на этой странице.
Я создал свой ответ, чтобы объяснить, как "сохранить соединение" связано с "низкими коллизиями".
Чтобы понять, какая стратегия лучше для вас, подумайте не о транзакциях в секунду, которые имеет ваша БД, а о продолжительности одной транзакции. Обычно вы открываете trasnaction, Performa Operation и закрываете транзакцию. Это короткая классическая транзакция, которую ANSI имела в виду, и она прекрасно подходит для блокировки. Но как реализовать систему бронирования билетов, когда многие клиенты бронируют одни и те же номера / места одновременно?
Вы просматриваете предложения, заполняете форму множеством доступных опций и актуальными ценами. Это занимает много времени, и варианты могут устареть, все недействительные цены между вами начали заполнять форму и нажать кнопку "Я согласен", потому что не было блокировки данных, к которым вы обращались, и кто-то другой, более проворный, вмешался меняются все цены и вам нужно перезапустить с новыми ценами.
Вместо этого вы можете заблокировать все параметры по мере их чтения. Это пессимистичный сценарий. Вы видите, почему это отстой. Ваша система может быть отключена одним клоуном, который просто начинает бронирование и курит. Никто не может зарезервировать что-либо, прежде чем он закончит. Ваш денежный поток падает до нуля. Вот почему оптимистические оговорки используются в реальности. Те, кто слишком долго бездельничает, должны возобновить бронирование по более высоким ценам.
При таком оптимистичном подходе вы должны записать все данные, которые вы прочитали (как в моем Repeated Read), и прийти к точке фиксации с вашей версией данных (я хочу купить акции по цене, указанной в этой цитате, а не по текущей цене).). На этом этапе создается транзакция ANSI, которая блокирует БД, проверяет, ничего не изменилось и фиксирует / прерывает вашу операцию. IMO, это эффективная эмуляция MVCC, которая также связана с Optimistic CC и предполагает, что ваша транзакция перезапускается в случае сбоя, то есть вы сделаете новое резервирование. Транзакция здесь включает в себя решения пользователя.
Я далек от понимания того, как реализовать MVCC вручную, но я думаю, что длительные транзакции с возможностью перезапуска - ключ к пониманию предмета. Поправь меня, если я где-нибудь не прав. Мой ответ был мотивирован этой главой Алексея Кузнецова.
В большинстве случаев оптимистичная блокировка более эффективна и обеспечивает более высокую производительность. При выборе между пессимистической и оптимистической блокировками учитывайте следующее:
Пессимистическая блокировка полезна, если существует много обновлений и относительно высока вероятность того, что пользователи попытаются обновить данные одновременно. Например, если каждая операция может обновлять большое количество записей за раз (банк может добавлять процентные доходы на каждую учетную запись в конце каждого месяца), и два приложения выполняют такие операции одновременно, они будут иметь конфликты,
Пессимистическая блокировка также больше подходит для приложений, которые содержат небольшие таблицы, которые часто обновляются. В случае этих так называемых горячих точек конфликты настолько вероятны, что оптимистическая блокировка тратит усилия на откат конфликтующих транзакций.
Оптимистическая блокировка полезна, если вероятность конфликтов очень мала - существует много записей, но относительно мало пользователей, или очень мало обновлений и в основном операций типа чтения.
Допустим, в приложении электронной коммерции пользователь хочет разместить заказ. Этот код будет выполняться несколькими потоками. Вpessimistic locking
, когда мы получаем данные из БД, мы блокируем их, чтобы ни один другой поток не мог их изменить. Мы обрабатываем данные, обновляем данные, а затем фиксируем данные. После этого отпускаем замок. Продолжительность блокировки здесь велика, мы заблокировали запись базы данных с самого начала до фиксации.
Вoptimistic locking
, мы получаем данные и обрабатываем данные без блокировки. Таким образом, несколько потоков могут выполнять код одновременно. Это ускорит. Пока мы обновляем, мы блокируем данные. Мы должны убедиться, что ни один другой поток не обновил эту запись. Например, если у нас было 100 предметов в инвентаре, и мы должны обновить его до 99 (потому что ваш код может бытьquantity=queantity-1
), но если другой поток уже использовал 1, это должно быть 98. У нас былоrace condition
здесь. В этом случае мы перезапускаем поток, чтобы выполнить тот же код с самого начала. Но это дорогостоящая операция, вы уже подошли к концу, но потом перезапустите. если бы у нас было несколько условий гонки, это не имело бы большого значения. Если бы состояние гонки было высоким, пришлось бы перезапустить много потоков. Мы можем работать в петле. В условиях гонки мы должны использовать «пессимистическую блокировку».
Один из вариантов использования оптимистической блокировки - это использование приложением базы данных, чтобы один из ваших потоков / хостов мог "требовать" выполнения задачи. Это техника, которая мне пригодилась на регулярной основе.
Лучший пример, который я могу придумать, - это очередь задач, реализованная с использованием базы данных, когда несколько потоков одновременно выполняют задачи. Если задача имеет статус "Доступно", "Утверждено", "Завершено", запрос БД может сказать что-то вроде "Установить статус =" Заявлено ", где статус =" Доступен ". Если несколько потоков пытаются изменить статус таким способом, все, кроме первого потока, потерпят неудачу из-за грязных данных.
Обратите внимание, что это вариант использования, включающий только оптимистическую блокировку. Таким образом, в качестве альтернативы высказыванию "Оптимистическая блокировка используется, когда вы не ожидаете много коллизий", она также может использоваться, когда вы ожидаете коллизии, но хотите, чтобы ровно одна транзакция была успешной.
Выше было сказано много хорошего об оптимистичной и пессимистической блокировке. Следует учитывать один важный момент:
При использовании оптимистической блокировки мы должны быть осторожны с тем, как приложение будет восстанавливаться после этих сбоев.
Это может привести к нарушению порядка обработки сообщений или потере обновлений, особенно в архитектурах, управляемых асинхронными сообщениями.
Сценарии отказов нужно продумывать.
Оптимистическая блокировка означает, что монопольная блокировка не используется при чтении строки, поэтому не предотвращается потеря обновления или искажение записи . Итак, используйте оптимистическую блокировку :
- Если потеряно обновление или перекос записи не происходит.
- Или, если нет проблем, даже если происходит потеря обновления или перекос записи .
Пессимистическая блокировка означает, что монопольная блокировка используется при чтении строки , что предотвращает потерю обновления или искажение записи . Итак, используйте пессимистическую блокировку :
- Если потеряно обновление или перекос записи .
- Или если есть какие-то проблемы, если происходит потеря обновления или перекос записи .
В MySQL и PostgreSQL вы можете использовать эксклюзивную блокировку с.
Вы можете проверить мой ответ о потерянном обновлении и написать примеры перекосов с оптимистичной блокировкой (без
SELECT FOR UPDATE
) и пессимистическая блокировка (с
SELECT FOR UPDATE)
в MySQL.
С практической точки зрения, при обновлении распределенной системы оптимистическая блокировка в БД может быть недостаточной для обеспечения согласованности, необходимой для всех частей распределенной системы.
Например, в приложениях, построенных на AWS, обычно данные хранятся как в БД (например, DynamoDB), так и в хранилище (например, S3). Если обновление коснется и DynamoDB, и S3, оптимистическая блокировка в DynamoDB все равно может привести к несогласованности данных в S3. В таких случаях, вероятно, безопаснее использовать пессимистическую блокировку, которая удерживается в DynamoDB до завершения обновления S3. Фактически, AWS предоставляет для этой цели библиотеку блокировок .
Оптимистическая блокировка и пессимистическая блокировка — две модели блокировки данных в базе данных.
Оптимистическая блокировка : когда запись блокируется только тогда, когда изменения фиксируются в базе данных.
Пессимистическая блокировка : когда запись блокируется во время ее редактирования.
Примечание . В обеих моделях блокировки данных блокировка снимается после фиксации изменений в базе данных.