MySQL 死锁

1、概念  

  死锁,指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,他们都将无法推进下去,导致产生死锁。即当两个事物都需要获得对方持有的锁才能够继续完成事物,导致双方都在等待,产生死锁。死锁的根本原因是有两个或多个事物之间加锁顺序的不一致导致的

2、死锁产生的条件:

  1. 互斥条件:一个资源每次只能被一个进程使用
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
  3. 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

案例:

MySQL 死锁

3、如何避免死锁:

  1. 为了减少死锁的可能性,请使用事务而不是LOCK TABLES语句;
  2. 保持插入或更新数据的事务足够小,以使其长时间不保持打开状态;因为大事物占用资源多耗时长,与其他事物冲突的概率也会变大。
  3. 当不同的事务更新多个表或大范围的行时,请SELECT ... FOR UPDATE在每个事务中使用相同的操作顺序(例如 );SELECT ... FOR UPDATE和 UPDATE ... WHERE 语句中使用的列上创建索引。如果不走索引会为表的每一行记录加锁,死锁的概率会大大增加。
  4. 死锁的可能性不受隔离级别的影响,因为隔离级别更改了读取操作的行为,而死锁则由于写入操作而发生

4、如何最小化和处理死锁

  InnoDB使用自动行级锁定。即使在仅插入或删除单行的事务中,也可能会遇到死锁。这是因为这些操作并不是真正的“ 原子 ”操作它们会自动对插入或删除的行的(可能是多个)索引记录设置锁定。

  您可以使用以下技术来处理死锁并减少发生死锁的可能性:

  • 在任何时候,发出 SHOW ENGINE INNODB STATUS命令以确定最近死锁的原因。这可以帮助您调整应用程序以避免死锁。

  • 如果频繁出现死锁警告引起关注,请通过启用innodb_print_all_deadlocks 配置选项来收集更广泛的调试信息 有关每个死锁的信息,而不仅仅是最新的死锁,都记录在MySQL 错误日志中完成调试后,请禁用此选项。

  • 如果由于死锁而失败,请始终准备重新发出事务。死锁并不危险。请再试一次。

  • 保持事务小巧且持续时间短,以使事务不易发生冲突。

  • 进行一系列相关更改后立即提交事务,以减少冲突的发生。特别是,不要长时间关闭未提交事务的交互式 mysql会话。

  • 如果您使用锁定读取SELECT ... FOR UPDATE或 SELECT ... FOR SHARE),请尝试使用较低的隔离级别,例如 READ COMMITTED

  • 修改事务中的多个表或同一表中的不同行集时,每次都要以一致的顺序执行这些操作。然后,事务形成定义明确的队列,并且不会死锁。例如,组织数据库操作到功能在应用程序中,或调用存储程序,而不是编码的多个相似序列 INSERTUPDATE以及 DELETE在不同的地方语句。

  • 将精选的索引添加到表中。这样,您的查询就需要扫描更少的索引记录,从而设置更少的锁。使用EXPLAIN SELECT以确定哪些索引MySQL认为最适合您的查询。

  • 使用较少的锁定。如果你能负担得起,以允许 SELECT从一个旧的快照返回数据,不要添加条款FOR UPDATEFOR SHARE给它。READ COMMITTED 这里使用隔离级别是件好事,因为同一事务中的每个一致性读取均从其自己的新快照读取。

  • 如果没有其他帮助,请使用表级锁序列化事务。LOCK TABLES与事务表(例如InnoDB 表)一起使用的正确方法 是,以SET autocommit = 0(not START TRANSACTION)后跟来开始事务LOCK TABLES,并且UNLOCK TABLES在明确提交事务之前不要调用它 例如,如果您需要写表 t1和从表中读取数据 t2,则可以执行以下操作:

    SET autocommit=0;
    LOCK TABLES t1 WRITE, t2 READ, ...;
    ... do something with tables t1 and t2 here ...
    COMMIT;
    UNLOCK TABLES;

    表级锁可防止对表的并发更新,从而避免死锁,但代价是对繁忙系统的响应速度较慢。

  • 序列化事务的另一种方法是创建一个仅包含一行的辅助“ 信号量 ”表。在访问其他表之前,让每个事务更新该行。这样,所有事务都以串行方式发生。请注意,InnoDB 即时死锁检测算法在这种情况下也适用,因为序列化锁是行级锁。对于MySQL表级锁,必须使用超时方法来解决死锁。