MYSQL:简单查询的死锁


MYSQL: Deadlock with simple queries

我有一个表'clicks',每次用户浏览特殊计数器脚本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;

如果您为数据集使用单独的列和生成的标识符,可以通过对clicks表进行一次更新来实现这一点。如果你从cron运行这个,你可能应该把它们包装在GET_LOCK()中。。。RELEASE_LOCK()循环以防止并发。尽管该模型确实支持大多数事务模型的并发性,但当情况已经不好时,它的性能会受到一些影响。