如何避免mysql中的死锁

时间:2010-03-18 13:42:00

标签: mysql deadlock innodb

我有以下查询(所有表都是innoDB)

INSERT INTO busy_machines(machine) 
               SELECT machine FROM all_machines 
               WHERE machine NOT IN (SELECT machine FROM busy_machines) 
               and machine_name!='Main' 
               LIMIT 1

当我在线程中运行时会导致死锁,显然是因为内部选择,对吗?

我得到的错误是:

(1213, 'Deadlock found when trying to get lock; try restarting transaction')

如何避免死锁?有没有办法改变查询以使其工作,或者我是否需要做其他事情?

当然,只有在多次运行此查询并且在多个线程中之后,才会始终发生错误。

2 个答案:

答案 0 :(得分:12)

根据我的理解,select不会获得锁定,也不应该成为死锁的原因。

每次插入/更新/删除行时,都会获取锁定。为避免死锁,您必须确保并发事务不会按顺序更新行,从而导致死锁。一般来说,为了避免死锁,您必须始终以相同的顺序获取锁,即使在不同的交易中也是如此(例如,首先是表A,然后是表B)。

但是如果在一个事务中只插入一个表,则满足此条件,这通常不会导致死锁。你在交易中做了别的什么吗?

如果缺少索引,则会发生死锁。当插入/更新/删除行时,数据库需要检查关系约束,即确保关系一致。为此,数据库需要检查相关表中的外键。 可能导致获取其他锁定而不是修改的行。确保总是在外键(当然还有主键)上有索引,否则可能导致表锁而不是行锁。如果发生表锁定,则锁争用会更高,并且死锁的可能性会增加。

不确定你的情况究竟会发生什么,但也许有帮助。

答案 1 :(得分:5)

如果用外连接替换“NOT IN”,你可能会获得更好的性能。

您还可以将其分为两个查询,以避免在单个查询中插入和选择同一个表。

这样的事情:

           SELECT a.machine 
           into @machine
           FROM all_machines a
           LEFT OUTER JOIN busy_machines b on b.machine = a.machine
           WHERE a.machine_name!='Main' 
           and b.machine IS NULL 
           LIMIT 1;

           INSERT INTO busy_machines(machine) 
           VALUES (@machine);