Это вытекает из этого связанного вопроса , где я хотел знать, как заставить две транзакции происходить последовательно в тривиальном случае (где обе работают только в одной строке). Я получил ответ - используйте SELECT ... FOR UPDATE
в качестве первой строки обе транзакции - но это приводит к проблеме: если первая транзакция никогда не фиксируется или не откатывается, то вторая транзакция будет заблокирована на неопределенный срок. innodb_lock_wait_timeout
Переменные задает число секунд , после чего клиент пытается сделать вторую сделку бы сказал « К сожалению, попробуйте еще раз» ... но насколько я могу сказать, что они не были бы попробовать еще раз до следующей перезагрузки сервера. Так:
- Наверняка должен быть способ форсировать,
ROLLBACK
если транзакция длится вечно? Должен ли я прибегать к использованию демона для уничтожения таких транзакций, и если да, то как бы выглядел этот демон? - Если соединение прерывается
wait_timeout
илиinteractive_timeout
выполняется транзакция в середине транзакции, выполняется ли откат транзакции? Есть ли способ проверить это с консоли?
Уточнение : innodb_lock_wait_timeout
задает количество секунд, в течение которых транзакция будет ожидать снятия блокировки, прежде чем отказаться; то, что я хочу, это способ заставить блокировку быть освобожденной.
Обновление 1 : Вот простой пример, который демонстрирует, почему innodb_lock_wait_timeout
этого недостаточно для того, чтобы вторая транзакция не была заблокирована первой:
START TRANSACTION;
SELECT SLEEP(55);
COMMIT;
При настройке по умолчанию innodb_lock_wait_timeout = 50
эта транзакция завершается без ошибок через 55 секунд. И если вы добавляете UPDATE
перед SLEEP
строкой, а затем инициируете вторую транзакцию от другого клиента, который пытается в SELECT ... FOR UPDATE
той же строке, это вторая транзакция, которая истекает, а не та, которая заснула.
То, что я ищу, - это способ положить конец спокойному сну этой транзакции.
Обновление 2 : В ответ на опасения hobodave о том, насколько реалистичен приведенный выше пример, приведем альтернативный сценарий: администратор БД подключается к работающему серверу и работает
START TRANSACTION
SELECT ... FOR UPDATE
где вторая строка блокирует строку, в которую приложение часто пишет. Затем администратор базы данных прерывается и уходит, забывая завершить транзакцию. Приложение останавливается, пока ряд не разблокируется. Я бы хотел минимизировать время, в течение которого приложение зависло в результате этой ошибки.
ROLLBACK
первую транзакцию, если она занимает большеn
секунды. Есть ли способ сделать это?MYSQL
нет конфигурации для предотвращения этого сценария. Потому что недопустимо зависание сервера из-за безответственности клиентов. Я не обнаружил затруднений в понимании вашего вопроса, и он так актуален.Ответы:
Кажется, что больше половины этой темы посвящено тому, как задавать вопросы по ServerFault. Я думаю, что вопрос имеет смысл и довольно прост: как автоматически откатить заблокированную транзакцию?
Одним из решений, если вы хотите разорвать все соединение, является установка wait_timeout / interactive_timeout. См. Https://stackoverflow.com/questions/9936699/mysql-rollback-on-transaction-with-lost-disconnected-connection .
источник
Поскольку ваш вопрос задается здесь на ServerFault, логично предположить, что вы ищете решение MySQL для проблемы MySQL, особенно в области знаний, в которых системный администратор и / или администратор базы данных будут иметь опыт. Таким образом, следующее В разделе рассматриваются ваши вопросы:
Нет не будет Я думаю, что вы не понимаете
innodb_lock_wait_timeout
. Это именно то, что вам нужно.Он вернется с ошибкой, как указано в руководстве:
innodb_lock_wait_timeout
несколько секунд.По умолчанию транзакция не будет откатываться. Ответственность за решение этой ошибки лежит на коде приложения: повторная попытка или откат.
Если вы хотите автоматический откат, это также объясняется в руководстве:
RE: Ваши многочисленные обновления и комментарии
Во-первых, вы заявили в своих комментариях, что вы «хотели» сказать, что вы хотите способ тайм-аута первой транзакции, которая блокируется на неопределенный срок. Это не очевидно из вашего исходного вопроса и конфликтует с «Если первая транзакция никогда не будет зафиксирована или откатана, то вторая транзакция будет заблокирована на неопределенный срок».
Тем не менее, я могу ответить и на этот вопрос. Протокол MySQL не имеет «тайм-аут запроса». Это означает, что вы не можете тайм-аут первой заблокированной транзакции. Вы должны подождать, пока он не закончится, или убить сеанс. Когда сеанс завершен, сервер автоматически откатит транзакцию.
Единственная альтернатива - использовать или написать библиотеку mysql, которая использует неблокирующий ввод / вывод, который позволит вашему приложению завершить выполнение потока / форка, выполнивших запрос через N секунд. Реализация и использование такой библиотеки выходят за рамки ServerFault. Это подходящий вопрос для StackOverflow .
Во-вторых, вы указали в своих комментариях следующее:
Это было совсем не очевидно в вашем первоначальном вопросе, и до сих пор нет. Это можно было увидеть только после того, как вы поделились этим довольно важным комментарием в комментарии.
Если это проблема, которую вы пытаетесь решить, то, боюсь, вы задали ее не на том форуме. Вы описали проблему программирования на уровне приложения, которая требует решения для программирования, которое MySQL не может предоставить, и выходит за рамки этого сообщества. Ваш последний ответ отвечает на вопрос «Как я могу предотвратить бесконечные циклы программы Ruby?». Этот вопрос не по теме для этого сообщества и должен быть задан в StackOverflow .
источник
innodb_lock_wait_timeout
), это не так в ситуации, которую я изложил: когда клиент зависает или падает, прежде чем он получит изменение для выполненияCOMMIT
илиROLLBACK
,COMMIT
илиROLLBACK
сообщение отправлено для разрешения первой транзакции? Hobodave, возможно, было бы полезно, если бы вы представили свой тестовый код.В аналогичной ситуации моя команда решила использовать pt-kill ( https://www.percona.com/doc/percona-toolkit/2.1/pt-kill.html ). Он может сообщать или уничтожать запросы, которые (среди прочего) выполняются слишком долго. Затем можно запускать его в кроне каждые X минут.
Проблема заключалась в том, что запрос с неожиданно долгим (например, неопределенным) временем выполнения заблокировал процедуру резервного копирования, которая пыталась очистить таблицы с блокировкой чтения, что, в свою очередь, заблокировало все другие запросы на «ожидание сброса таблиц», которое затем никогда не истекло. потому что они не ожидают блокировки, что привело к отсутствию ошибок в приложении, что в конечном итоге привело к тому, что администраторы не получали оповещений и не останавливали работу приложения.
Исправление, очевидно, заключалось в том, чтобы исправить исходный запрос, но во избежание непредвиденной ситуации в будущем было бы замечательно, если бы была возможность автоматически уничтожать (или сообщать о) транзакции / запросы, которые выполняются дольше, чем определенное время, какой автор вопроса, кажется, спрашивает. Это выполнимо с помощью pt-kill.
источник
Простым решением будет иметь хранимую процедуру для уничтожения запросов, которые занимают больше времени, чем требуется,
timeout
и использоватьinnodb_rollback_on_timeout
опцию вместе с ней.источник
После некоторого поиска в Google, похоже, нет прямого способа установить ограничение по времени для каждой транзакции. Вот лучшее решение, которое мне удалось найти:
Команда
дает вам таблицу со всеми командами, выполняемыми в настоящее время, и временем (в секундах) с момента их запуска. Когда клиент перестает давать команды, он описывается как
Sleep
команда, а вTime
столбце указывается количество секунд с момента выполнения последней команды. Таким образом, вы можете вручную ввестиKILL
все, что имеетTime
значение, скажем, 5 секунд, если вы уверены, что ни один запрос не должен занимать более 5 секунд в вашей базе данных. Например, если процесс с идентификатором 3 имеет значение 12 в своемTime
столбце, вы можете сделатьДокументация по синтаксису KILL предполагает, что транзакция, выполняемая потоком с этим идентификатором, будет откатываться (хотя я не проверял это, поэтому будьте осторожны).
Но как автоматизировать это и убивать все сверхурочные сценарии каждые 5 секунд? Первый комментарий на той же странице дает нам подсказку, но код написан на PHP; кажется , что мы не можем сделать
SELECT
наSHOW PROCESSLIST
из клиента MySQL. Тем не менее, предположим, что у нас есть демон PHP, который мы запускаем каждую$MAX_TIME
секунду, он может выглядеть примерно так:Обратите внимание, что это может иметь побочный эффект принудительного повторного подключения всех незанятых клиентских подключений, а не только тех, которые работают неправильно.
Если у кого-то есть лучший ответ, я хотел бы услышать это.
Изменить: Существует по крайней мере один серьезный риск использования
KILL
таким образом. Предположим, что вы используете скрипт выше дляKILL
каждого соединения, которое простаивает в течение 10 секунд. После 10 секунд простоя соединения, но до егоKILL
выдачи пользователь, чье соединение разрывается, выдаетSTART TRANSACTION
команду. Они тогдаKILL
ред. Они отправляют,UPDATE
а затем, подумав дважды, выдаютROLLBACK
. Однако из-заKILL
отката исходной транзакции обновление прошло немедленно и не может быть отменено! Смотрите этот пост для теста.источник
Как предложил hobodave в комментарии, в некоторых клиентах возможно (хотя это, очевидно, не
mysql
утилита командной строки) установить ограничение времени транзакции. Вот демонстрация использования ActiveRecord в Ruby:В этом примере транзакция истекает через 5 секунд и автоматически откатывается . ( Обновление в ответ на комментарий hobodave: если для ответа базы данных требуется более 5 секунд, транзакция будет откатана, как только это произойдет - не раньше.) Если вы хотите убедиться, что все ваши транзакции истекают через
n
несколько секунд, Вы можете создать оболочку вокруг ActiveRecord. Я предполагаю, что это также относится к самым популярным библиотекам в Java, .NET, Python и т. Д., Но я еще не тестировал их. (Если у вас есть, пожалуйста, оставьте комментарий к этому ответу.)Транзакции в ActiveRecord также имеют преимущество в том, что они безопасны при выдаче a
KILL
, в отличие от транзакций, выполняемых из командной строки. См. Https://dba.stackexchange.com/questions/1561/mysql-client-believes-theyre-in-a-transaction-gets-killed-wreaks-havoc .По-видимому, невозможно установить максимальное время транзакции на стороне сервера, за исключением сценария, подобного тому, который я опубликовал в моем другом ответе.
источник
Foo.create
команда заблокирована на 5 минут, тайм-аут не убьет ее. Это несовместимо с примером, приведенным в вашем вопросе. ASELECT ... FOR UPDATE
может легко блокироваться на длительные периоды времени, как и любой другой запрос.ActiveRecord::Base#find_by_sql
: gist.github.com/856100 Опять же, это потому, что AR использует блокирующий ввод / вывод.timeout
метод откатит транзакцию, но не до тех пор, пока база данных MySQL не ответит на запрос. Таким образом, этотtimeout
метод подходит для предотвращения длинных транзакций на стороне клиента или длинных транзакций, которые состоят из нескольких относительно коротких запросов, но не для длинных транзакций, в которых виновником является один запрос.