Как правильно реализовать оптимистическую блокировку в MySQL?
Наша команда пришла к выводу, что мы должны сделать № 4 ниже, иначе есть риск, что другой поток может обновить ту же версию записи, но мы хотели бы проверить, что это лучший способ сделать это.
- Создайте поле версии в таблице, для которой вы хотите использовать оптимистическую блокировку, например, столбец name = "версия"
- При выборе обязательно включите столбец версии и запишите версию
- При последующем обновлении записи оператор обновления должен выдать «где версия = X», где X - версия, которую мы получили в # 2, и установить поле версии во время этого оператора обновления на X + 1.
- Выполните
SELECT FOR UPDATE
запись, которую мы собираемся обновить, чтобы сериализовать, кто может вносить изменения в запись, которую мы пытаемся обновить.
Чтобы уточнить, мы пытаемся предотвратить перезапись друг друга двумя потоками, которые выбирают одну и ту же запись в одном и том же временном окне, где они захватывают одну и ту же версию записи, если они попытаются обновить запись одновременно. Мы считаем, что если мы не выполним # 4, есть вероятность, что, если оба потока одновременно введут свои соответствующие транзакции (но еще не выпустили свои обновления), когда они пойдут на обновление, второй поток будет использовать UPDATE. ... где версия = X будет работать со старыми данными.
Правильны ли мы, думая, что мы должны делать эту пессимистическую блокировку при обновлении, даже если мы используем поля версий / оптимистическую блокировку?
SELECT ... FOR UPDATE
оптимистическая блокировка, либо версионность строк, а не обе. Подробности смотрите в ответе.Ответы:
Ваш разработчик ошибается. Вам нужно либо
SELECT ... FOR UPDATE
или ряд версий, но не оба.Попробуйте и посмотрите. Открытые три MySQL сессии
(A)
,(B)
и(C)
в той же базе данных.В
(C)
номере:И в том,
(A)
и в другом(B)
выполнитеUPDATE
тестирование и настройку версии строки, изменивwinner
текст в каждом, чтобы вы могли увидеть, какой сеанс какой:Теперь
(C)
,UNLOCK TABLES;
чтобы снять блокировку.(A)
и(B)
будет гоняться за блокировку строк. Один из них победит и получит замок. Другой заблокирует замок. Победитель, получивший блокировку, приступит к смене ряда. Предполагая,(A)
что это победитель, теперь вы можете увидеть измененную строку (все еще незафиксированную, поэтому не видимую для других транзакций) с помощьюSELECT * FROM test WHERE id = 1
.Теперь
COMMIT
в сессии победителя, скажем(A)
.(B)
получит блокировку и продолжит обновление. Однако версия больше не совпадает, поэтому она не изменит строки, как указано в результате подсчета строк. Только один изUPDATE
них имел какой-либо эффект, и клиентское приложение может четко видеть, чтоUPDATE
удалось, а что - нет. Никакой дополнительной блокировки не требуется.Смотрите логи сессии на pastebin здесь . Я использовал и
mysql --prompt="A> "
т. Д., Чтобы было легко заметить разницу между сессиями. Я скопировал и вставил вывод с чередованием во временной последовательности, так что это не полностью необработанный вывод, и, возможно, я мог допустить ошибки при копировании и вставке. Проверьте сами, чтобы увидеть.Если бы вы не добавили поле строки версии, то вам нужно будет ,
SELECT ... FOR UPDATE
чтобы иметь возможность надежно обеспечить порядок.Если вы думаете об этом,
SELECT ... FOR UPDATE
это совершенно излишним , если вы сразу делаетеUPDATE
без данных повторного использования изSELECT
, или , если вы используете строку управления версиями. ВUPDATE
любом случае взломает замок. Если кто-то еще обновит строку между вашим чтением и последующей записью, ваша версия больше не будет совпадать, поэтому ваше обновление не удастся. Вот как работает оптимистичная блокировка.Целью
SELECT ... FOR UPDATE
является:SERIALIZABLE
изоляцию или управление версиями строк.Вам не нужно использовать как оптимистическую блокировку (управление версиями строк), так и
SELECT ... FOR UPDATE
. Используйте один или другой.источник
Никакие блокировки (не таблицы, не транзакции) не нужны или даже желательны:
источник