死锁DeadLock

死锁问题

Deadlock found when trying to get lock; try restarting transaction

发现在批量update时偶尔报Deadlock found when trying to get lock; try restarting transaction错误

原因:

根本原因是两个事物同时批量操作时,有操作相同的记录,但是两个事物操作相同记录的顺序不一样,导致两个两个事物都获取不到锁,互锁了
deadlock

正如阿里Java开发手册中说的一样:

【强制】对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造 成死锁。
说明:线程一需要对表 A、B、C 依次全部加锁后才可以进行更新操作,那么线程二的加锁顺序 也必须是 A、B、C,否则可能出现死锁。


解决方案:

  • 减小事务
  • 给字段排序,按照排序后顺序执行,让死锁变为锁等待。

减小事物:

减小在一个事物中的语句和执行时间,这样只是减小发生deadlock的概率,推荐给字段排序来解决。

给字段排序:

看到网上有这样解决的:
Change

1
DELETE FROM onlineusers WHERE datetime <= now() - INTERVAL 900 SECOND

TO

1
2
DELETE FROM onlineusers WHERE id IN (SELECT id FROM onlineusers
WHERE datetime <= now() - INTERVAL 900 SECOND order by id) u;

需要注意这样写可能会非常慢,因为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

分享到: