Чтобы использовать SERIALIZABLE для гарантии действительно последовательных транзакций, каждая транзакция должна получить глобальную блокировку для всех таблиц в базе данных. Невозможно заранее узнать, какие данные будет пытаться прочитать или обновить ваша транзакция, поэтому единственной реальной гарантией является глобальная блокировка.
Как Oracle, так и MySQL имеют уровень изоляции транзакций, который они называют SERIALIZABLE, но они используют оптимистическую стратегию. Хотя каждый из них делает это по-своему, глобальная блокировка также не выполняется, как я описал выше.
MySQL реализует SERIALIZABLE простым способом: каждый SELECT
неявно SELECT...LOCK IN SHARE MODE
(известный как SELECT...FOR SHARE
в 8.0). Это означает, что если две сессии прочитают данные, а затем попытаются обновить их, как показано в примере дебетования баланса в статье, они вызовут взаимоблокировку, поскольку оба обновления будут ожидать, пока другая освободит свою общую блокировку чтения.
Oracle позволяет вам читать и обновлять данные, и оптимистически получает блокировки (т.е. во время чтения или обновления). Но если вы попытаетесь обновить данные, которые были изменены с момента начала транзакции, вы получите эту ошибку:
ORA-08177: can't serialize access for this transaction
Как в Oracle, так и в MySQL лучшее средство от уязвимости ACIDRain не имеет ничего общего с уровнем изоляции. Решение проблемы заключается в том, чтобы избежать условия гонки, используя явную блокировку чтения , используя опцию запроса FOR UPDATE
. Это обеспечивает эксклюзивный доступ к данным, начиная с момента их чтения.
Другим выходом из ситуации может быть выдача явных команд блокировки таблиц, таких как LOCK TABLES в MySQL или LOCK TABLE в Oracle.
Ссылки: