Подтвердить что ты не робот

Выпуск блокировки mysql за пределами транзакции (рельсы)

Недавно мы видели много ошибок:

ActiveRecord::TransactionIsolationConflict: Transaction isolation conflict detected: Lock wait timeout exceeded; try restarting transaction

Невозможно понять причины этого. Но заметили в нашем коде одну вещь, которая пытается заблокировать запись вне транзакции:

acc = Account.lock.find acc_id

Выше код не находится внутри какой-либо транзакции и используется только для проверки того, что другая транзакция, которая также получает тот же замок, завершена или нет. Любые мысли о том, может ли это быть виновником?

4b9b3361

Ответ 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;
  1. Проверьте конфигурацию еще раз, если она еще не обновлена, а затем снова установите глобальные переменные

    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 означает - получить блокировку для записи.

Вероятно, ваша транзакция занимает больше времени, чем указано в вашей БД, и запрос проверки, который пытается проверить исходную транзакцию, был завершен - получение этой ошибки по мере того, как время ожидания истекает.