我有以下查询(所有表都是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')
如何避免死锁?有没有办法改变查询以使其工作,或者我是否需要做其他事情?
当然,只有在多次运行此查询并且在多个线程中之后,才会始终发生错误。
答案 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);