Выпуск блокировки mysql за пределами транзакции (рельсы)
Недавно мы видели много ошибок:
ActiveRecord::TransactionIsolationConflict: Transaction isolation conflict detected: Lock wait timeout exceeded; try restarting transaction
Невозможно понять причины этого. Но заметили в нашем коде одну вещь, которая пытается заблокировать запись вне транзакции:
acc = Account.lock.find acc_id
Выше код не находится внутри какой-либо транзакции и используется только для проверки того, что другая транзакция, которая также получает тот же замок, завершена или нет. Любые мысли о том, может ли это быть виновником?
Ответы
Ответ 1
В InnoDB используется блокировка уровня строки для лучшего concurrency при большой нагрузке на запись. Двигатель принимает некоторые меры предосторожности, чтобы избавиться от чтения phantom, одним из них является gap_lock, что может вызвать эту проблему. Используйте
SHOW ENGINE INNODB STATUS
чтобы проанализировать gap_lock.
Если это непросто, вы можете попробовать следующие параметры
-
Измените уровень ISOLATION
на READ COMMITTED
.
-
set innodb_locks_unsafe_for_binlog = 1
. Это отключит блокировки зазора, за исключением проверки ограничения внешнего ключа или проверки дубликатов ключей.
-
Используйте show innodb status
снова, чтобы проанализировать, что происходит. При необходимости оптимизируйте свой код, чтобы избежать блокировки
-
Если выше не работает, попробуйте увеличить lock_wait_timeout глобально
SET GLOBAL innodb_lock_wait_timeout = 120
;
Или для сеанса
SET innodb_lock_wait_timeout = 120;
-
Проверьте конфигурацию еще раз, если она еще не обновлена, а затем снова установите глобальные переменные
show variables like '%wait_timeout%';
show variables like '%tx_isolation%';
SELECT @@GLOBAL.tx_isolation, @@tx_isolation;
Ответ 2
Пожалуйста, проверьте, что настроено на вашем сервере MySQL для innodb_lock_wait_timeout
и lock_wait_timeout
.
Вы можете сделать это, выполнив следующую команду
show global variables like '%lock_wait_timeout';
Какой из этих параметров используется, зависит от вашей версии MySQL и движка таблицы
из документации:
# выберите * из учетных записей, где id = 1 для обновления
Account.lock.find(1)
for update
означает - получить блокировку для записи.
Вероятно, ваша транзакция занимает больше времени, чем указано в вашей БД, и запрос проверки, который пытается проверить исходную транзакцию, был завершен - получение этой ошибки по мере того, как время ожидания истекает.