17.7.5.3 Способы минимизации и обработки тупиковых ситуаций
В этом разделе рассматривается информация о тупиковых ситуациях в транзакционных базах данных, представленная в разделе 17.7.5.2, «Обнаружение тупиковых ситуаций». В нём объясняется, как организовать операции с базой данных, чтобы минимизировать тупиковые ситуации, и как обрабатывать последующие ошибки в приложениях.
Тупиковые ситуации — это классическая проблема транзакционных баз данных, но они не опасны, пока не возникают так часто, что определённые транзакции не могут выполняться вообще. Обычно необходимо писать приложения, которые всегда готовы повторно выполнить транзакцию, если она была отменена из-за тупиковой ситуации.
InnoDB использует автоматическое блокирование на уровне строк. Тупиковые ситуации могут возникать даже в тех случаях, когда транзакции просто вставляют или удаляют одну строку. Это происходит потому, что такие операции не являются «атомарными»; они автоматически устанавливают блокировки на (возможно, несколько) записей индекса строки, которая вставляется или удаляется.
Вы можете справиться с тупиковыми ситуациями и снизить вероятность их возникновения с помощью следующих методов:
В любое время используйте
SHOW ENGINE INNODB STATUS, чтобы определить причину последней тупиковой ситуации. Это может помочь вам настроить приложение для предотвращения тупиковых ситуаций.Если частые предупреждения о тупиковых ситуациях вызывают беспокойство, соберите более подробную отладочную информацию, включив переменную
innodb_print_all_deadlocks. Информация о каждой тупиковой ситуации, а не только о последней, записывается в MySQL. Отключите эту опцию после завершения отладки.Всегда будьте готовы повторно выполнить транзакцию, если она завершилась неудачно из-за тупиковой ситуации. Тупиковые ситуации не опасны. Просто попробуйте ещё раз.
Держите транзакции небольшими и кратковременными, чтобы снизить вероятность коллизий.
Подтверждайте транзакции сразу после внесения набора связанных изменений, чтобы снизить вероятность коллизий. В частности, не оставляйте сеанс интерактивного mysql открытым долгое время с незафиксированной транзакцией.
Если вы используете
SELECT ... FOR UPDATEилиSELECT ... FOR SHARE), попробуйте использовать более низкий уровень изоляции, такой какREAD COMMITTED.При модификации нескольких таблиц в рамках одной транзакции или различных наборов строк в одной таблице выполняйте эти операции в согласованном порядке каждый раз. Тогда транзакции образуют хорошо определённые очереди и не приведут к тупиковой ситуации. Например, организуйте операции с базой данных в виде функций в вашем приложении или вызывайте хранимые процедуры вместо кодирования нескольких похожих последовательностей
INSERT,UPDATEиDELETEоператоров в разных местах.Добавьте хорошо подобранные индексы в свои таблицы, чтобы запросы сканировали меньше записей индекса и устанавливали меньше блокировок. Используйте
EXPLAIN SELECT, чтобы определить, какие индексы MySQL считает наиболее подходящими для ваших запросов.Используйте меньше блокировок. Если вы можете позволить себе разрешить
SELECTвозвращать данные из старой копии, не добавляйте к немуFOR UPDATEилиFOR SHAREклаузу. Использование уровня изоляцииREAD COMMITTEDв этом случае хорошо, потому что каждый согласованный чтение в рамках одной транзакции считывает из своей свежей копии.-
Если ничего не помогает, сериализуйте свои транзакции с помощью блокировок на уровне таблицы. Правильный способ использования
LOCK TABLESс транзакционными таблицами, такими какInnoDBтаблицы, заключается в запуске транзакции с помощьюSET autocommit = 0(а неSTART TRANSACTION) и последующим использованиемLOCK TABLES, и не вызыватьUNLOCK TABLES, пока явно не подтвердите транзакцию. Например, если вам нужно записать в таблицуt1и считать из таблицыt2, вы можете сделать это так:SET autocommit=0; LOCK TABLES t1 WRITE, t2 READ, ...;
... do something with tables t1 and t2 here ...COMMIT; UNLOCK TABLES;Блокировки на уровне таблицы предотвращают одновременные обновления таблицы, предотвращая тупиковые ситуации за счёт снижения отзывчивости загруженной системы.
Другой способ сериализации транзакций — создание вспомогательной таблицы «семафор», содержащей только одну строку. Пусть каждая транзакция обновляет эту строку перед доступом к другим таблицам. Таким образом, все транзакции происходят последовательно. Обратите внимание, что алгоритм обнаружения тупиковой ситуации
InnoDBтакже работает в этом случае, поскольку блокировка сериализации — это блокировка на уровне строк. В случае блокировок MySQL на уровне таблиц для решения тупиковых ситуаций необходимо использовать метод таймаута.
© 2025 Oracle
Licensed under the GPLv2 License.