如何避免mysql中的死锁

ola*_*ndo 10 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
Run Code Online (Sandbox Code Playgroud)

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

我得到的错误是:

(1213, 'Deadlock found when trying to get lock; try restarting transaction')
Run Code Online (Sandbox Code Playgroud)

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

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

ewe*_*nli 12

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

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

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

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

不确定在你的情况下究竟发生了什么,但也许它会有所帮助.

  • -1; MySQL 甚至不允许您在两列之间创建外键,除非两列都建立了索引,因此外键上缺少索引不可能解释任何事情。 (2认同)

Ike*_*ker 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);
Run Code Online (Sandbox Code Playgroud)