У меня есть таблица «клики», в которую я добавляю записи каждый раз, когда пользователь перемещается по специальному счетчику-скрипту click.php. Эта таблица не имеет столбца автоинкремента, она имеет transaction_id CHAR(32)
в качестве первичного ключа, который генерируется случайным образом перед вставкой новой записи. Каждая новая запись имеет столбец normalized=0
,
Каждые 3 минуты фоновый демон запускает транзакцию, читает все новые клики WHERE normalized=0
и группирует это к столу stats
, Единственный запрос записи для этой таблицы UPDATE clicks SET normalized=1 WHERE normalized=0
выполняется в конце всей обработки, а затем транзакции фиксируются.
Проблема заключается в том, что каждый раз, когда во время этой транзакции перемещается файл click.php, скрипт не может добавить новую запись в «клики» и завершается ошибкой:
SQLSTATE[40001]: Serialization failure:
1213 Deadlock found when trying to get lock; try restarting transaction
INSERT `clicks`
SET `transaction_id`='3520359d597ba05b635ff15feb334229',
`time`='2016-04-29 15:14:31',
...,
`normalized`='0'
Я знаю, что могу решить это, используя LOCK TABLES
Но я просто хочу знать, почему возникает этот тупик.
UPD:
Я вижу следующую причину в выводе SHOW ENGINE INNODB STATUS:
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 252763 page no 124 n bits 480 index `normalized`
of table `tds`.`clicks` trx id C1329EF lock_mode X locks gap before
rec insert intention waiting
(это должен быть комментарий, но он длинный и сложный)
Что такое режим изоляции транзакций?
Я никогда не сталкивался с этим раньше — в основном потому, что я избегаю использования транзакций с множественными утверждениями, например,
UPDATE clicks
SET normalized=2
WHERE normalized=0;
INSERT INTO stats (transaction_id, clicks)
SELECT transaction_id, COUNT(*)
FROM clicks
WHERE normalized=2
GROUP BY transaction_id;
UPDATE clicks
SET transaction_id=1
WHERE transaction_id=2;
Есть способы сделать это с помощью одного обновления таблицы кликов, если вы используете отдельный столбец и сгенерированный идентификатор для набора данных. И если вы запускаете это из cron, вам, вероятно, следует заключить их в цикл GET_LOCK () … RELEASE_LOCK (), чтобы предотвратить параллелизм. Хотя модель поддерживает параллелизм для большинства моделей транзакций, она немного ухудшает производительность, когда дела уже идут плохо.
Других решений пока нет …