我有一个“点击”表,每次用户浏览特殊的计数器脚本click.php时,我都会在其中添加记录。该表没有自动增量列,它具有transaction_id CHAR(32)作为主键,该键是在插入新记录之前随机生成的。每个新记录都有列normalized=0

每隔3分钟,后台守护程序会启动一个事务,读取所有新的单击WHERE normalized=0并将其分组到表stats中。该表的唯一写查询是在所有处理结束时执行UPDATE clicks SET normalized=1 WHERE normalized=0,然后提交事务。

问题在于,每次在此事务期间导航click.php时,脚本都无法将新记录添加到“ clicks”中,并因错误而失败:

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()循环中,以防止并发。尽管该模型确实支持大多数事务模型的并发性,但是当情况已经恶化时,它会降低性能。

关于php - MYSQL:简单查询导致死锁,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36939100/

10-12 02:17