Mysql锁机制之死锁问题

一.死锁问题

MyISAM中是不会产生死锁的,因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么全部等待。而在InnoDB中,锁是逐步获得的,就造成了死锁的可能

1.死锁问题一 ( 两个会话, 两条 SQL语句产生死锁)

  • 准备工作
create index index_id on t01;  # 删除之前的索引
alter table t01 modify id int primary ket;  # 将id字段设置成主键
create index index_id on t01(id);  # 创建聚集索引
create index index_name on t01(name);  # 创建辅助索引(非聚集索引)
desc t01;  # 查看表结构

image-20210228192015132

  • 开始试验

image-20210228194048769

image-20210228194354431

2.死锁问题二 (两个事务, 一条SQL语句引起的死锁)

img

第二个死锁问题,只有多个事务同时运行的情况下才可能出现,但隐蔽性极强,虽然每个Session都只有一条语句,仍旧会产生死锁。要分析这个死锁,首先必须用到本文前面提到的MySQL加锁的规则。针对Session 1,从name索引出发,读到的[hdc, 1],[hdc, 6]均满足条件,不仅会加name索引上的记录X锁,而且会加聚簇索引上的记录X锁,加锁顺序为先[1,hdc,100],后[6,hdc,10]。而Session 2,从pubtime索引出发,[10,6],[100,1]均满足过滤条件,同样也会加聚簇索引上的记录X锁,加锁顺序为[6,hdc,10],后[1,hdc,100]。发现没有,跟Session 1的加锁顺序正好相反,如果两个Session恰好都持有了第一把锁,请求加第二把锁,死锁就发生了

3.死锁总结

  • 死锁问题涉及到的问题
  • 在MySQL中, 行级锁并不是直接锁记录, 而是锁索引; 索引分为主键索引和非主键索引两种
  • 如果一条sql语句操作了主键索引, MySQL就会锁定这条主键索引
  • 如果一条语句操作了非主键索引, MySQL会先锁定该非主键索引, 再锁定相关的主键索引
  • 在update、delete操作时, MySQL不仅锁定WHERE条件扫描过的所有索引记录, 而且会锁定相邻的键值, 即所谓的next-key locking
  • 死锁产生的本质原理
  • 死锁的发生与否, 并不在于事务中有多少条SQL语句, 死锁的关键在于 : 两个(或以上)的Session加锁的顺序不一致
  • 而使用上面提到的, 分析MySQL每条SQL语句的加锁规则, 分析出每条语句的加锁顺序
  • 然后检查多个并发SQL间是否存在以相反的顺序加锁的情况, 就可以分析出各种潜在的死锁情况, 也可以分析出线上死锁发生的原因

4.如何避免死锁

发生死锁后, InnoDB一般都可以检测到, 并使一个事务释放锁回退, 另一个获取锁完成事务, 上面实验也证明了, 但也有多种方法可以避免死锁:

  • 如果不同程序会并发存取多个表, 尽量约定以相同的顺序访问表, 可以大大降低死锁机会
  • 在同一个事务中, 尽可能做到一次锁定所需要的所有资源, 减少死锁产生概率
  • 对于非常容易产生死锁的业务部分, 可以尝试使用升级锁定颗粒度, 通过表级锁定来减少死锁产生的概率
  • 在程序以批量方式处理数据的时候, 如果事先对数据排序, 保证每个线程按固定的顺序来处理记录, 也可以大大降低出现死锁的可能

猜你喜欢

转载自blog.csdn.net/songhaixing2/article/details/114282018