死锁问题
Deadlock found when trying to get lock; try restarting transaction
发现在批量update时偶尔报Deadlock found when trying to get lock; try restarting transaction错误
原因:
根本原因是两个事物同时批量操作时,有操作相同的记录,但是两个事物操作相同记录的顺序不一样,导致两个两个事物都获取不到锁,互锁了
正如阿里Java开发手册中说的一样:
【强制】对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造 成死锁。
说明:线程一需要对表 A、B、C 依次全部加锁后才可以进行更新操作,那么线程二的加锁顺序 也必须是 A、B、C,否则可能出现死锁。
解决方案:
- 减小事务
- 给字段排序,按照排序后顺序执行,让死锁变为锁等待。
减小事物:
减小在一个事物中的语句和执行时间,这样只是减小发生deadlock的概率,推荐给字段排序来解决。
给字段排序:
看到网上有这样解决的:
Change
1 | DELETE FROM onlineusers WHERE datetime <= now() - INTERVAL 900 SECOND |
TO
1 | DELETE FROM onlineusers WHERE id IN (SELECT id FROM onlineusers |
需要注意这样写可能会非常慢,因为IN中的子查询可能会不走索引,全表扫描,如果表中的数据很多,那这样就会产生很大的问题
可以修改为join:
1
UPDATE tableName AS table1 JOIN (SELECT id FROM tableName WHERE AND id IN (3,1,2) ORDER BY id) AS table2 ON table1.id=table2.id SET column1=1;
或者可以修改为:
1
UPDATE tableNmae SET column1=1 WHERE AND id IN (3,1,2) ORDER BY id;
MySQL支持这种语法
或者可以在程序中把需要排序的字段排好序,然后再去执行SQL