详解MySQL(InnoDB)如何处理死锁

一、什么是死锁
image.png
二、为什么会形成死锁

锁是需要事务结束后才释放的。

三、MySQL 的并发控制方式

一个是 MVCC,一个是两阶段锁协议。
为什么要并发控制呢?是因为多个用户同时操作 MySQL 的时候,为了提高并发性能并且要求如同多个用户的请求过来之后如同串行执行的一样(为了解决脏读、不可重复读、幻读)

四、两阶段锁协议(2PL)

官方定义:
两阶段锁协议是指所有事务必须分两个阶段对数据加锁和解锁,在对任何数据进行读、写操作之前,事务首先要获得对该数据的封锁;在释放一个封锁之后,事务不再申请和获得任何其他封锁。

对应到 MySQL 上分为两个阶段:

  • 扩展阶段(事务开始后,commit 之前):获取锁
  • 收缩阶段(commit 之后):释放锁
    就是说呢,只有遵循两段锁协议,才能实现 可串行化调度。

但是两阶段锁协议不要求事务必须一次将所有需要使用的数据加锁(innodb在需要的索引列数据才锁行),并且在加锁阶段没有顺序要求,所以这种并发控制方式会形成死锁。

五、MySQL 如何处理死锁?

MySQL有两种死锁处理方式:

  • 等待,直到超时(innodb_lock_wait_timeout=50s设置锁等待的时间,前提已经检测到锁的产生)。
  • 发起死锁检测,主动回滚一条事务,让其他事务继续执行,回滚代价最小的那一个事务(innodb_deadlock_detect=on)。
    由于性能原因,一般都是使用死锁检测来进行处理死锁。

死锁检测(默认开启)
死锁检测的原理是构建一个以事务为顶点、锁为边的有向图,判断有向图是否存在环,存在即有死锁。

回滚
检测到死锁之后,选择插入更新或者删除的行数最少的事务回滚,基于 INFORMATION_SCHEMA.INNODB_TRX 表中的 trx_weight 字段来判断。

六、如何避免发生死锁

收集死锁信息:

  • 利用命令 SHOW ENGINE INNODB STATUS查看死锁原因。
  • 调试阶段开启 innodb_print_all_deadlocks,收集所有死锁日志。

减少死锁:

  • 保证没有长事务。
  • 操作完之后立即提交事务,特别是在交互式命令行中。
  • 修改多个表或者多个行的时候,将修改的顺序保持一致。
  • 创建索引,可以使创建的锁更少,锁的颗粒更小。innodb行锁在索引列。
  • 如果上述都无法解决问题,那么尝试使用 lock tables t1, t2, t3 锁多张表

死锁解决:

SELECT * FROM information_schema.INNODB_TRX;
trx_rows_locked: 事务锁定行数
trx_rows_modified: 事务修改行数
#首先查询是否锁表
SHOW OPEN TABLES WHERE In_use > 0;
#查询进程,保证拥有超级管理员权限
SHOW PROCESSLIST;
#杀死进程
KILL 4503;
#查看正在锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
#查看等待锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

你可能感兴趣的:(详解MySQL(InnoDB)如何处理死锁)