我有一个表格'点击',每次用户导航特殊计数器脚本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
答案 0 :(得分:0)
(这应该是一个评论,但它很长并且参与其中)
什么是事务隔离模式?
我之前从未遇到过这种情况 - 主要是因为我避免使用mutli语句事务,例如
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()循环中以防止并发。虽然该模型确实支持大多数事务模型的并发性,但是当事情已经糟糕时,它会有一点性能损失。