当前位置: 移动技术网 > IT编程>数据库>Mysql > MySQL 加锁和死锁解析

MySQL 加锁和死锁解析

2019年05月20日  | 移动技术网IT编程  | 我要评论
[toc] 产生死锁的必要条件 多个并发事务(2个或者以上) 每个事物都持有了锁(或者是已经在等待锁) 每个事务都需要再继续持有锁(为了完成事务逻辑,还必须更新更多的行) 事物之间产生加锁的循环等待,形成死锁 常规锁模式 LOCK_S(读锁,共享锁) LOCK_X(写锁,排它锁) 锁的属性 LOCK ...


产生死锁的必要条件

  • 多个并发事务(2个或者以上)
  • 每个事物都持有了锁(或者是已经在等待锁)
  • 每个事务都需要再继续持有锁(为了完成事务逻辑,还必须更新更多的行)
  • 事物之间产生加锁的循环等待,形成死锁

    常规锁模式

  • lock_s(读锁,共享锁)
  • lock_x(写锁,排它锁)

    锁的属性

  • lock _rec_not_gap(锁记录)
  • lock_gap(锁记录前的gap)
  • lock_ordinary(同时锁记录+记录前的gap,next key锁)
  • lock_insert_intetion(插入意向锁)

    锁组合(属性+模式)

    可以任意组合

    锁冲突矩阵

锁是加在那里的?

  • 根据主键查找-锁加在主键上
    如 begin;select * from tt_copy where id=4 for update;
    加锁情况

    index primary of table test.tt_copy trx id 1101588 lock_mode x locks rec but not gap

  • 根据普通索引查找-锁加在普通索引和主键上
    如 begin;select * from tt_copy force index(idx_a) where a=4 for update;
    加锁情况

    index idx_a of table test.tt_copy trx id 1101590 lock_mode x locks rec but not gap
    index primary of table test.tt_copy trx id 1101590 lock_mode x locks rec but not gap

操作与加锁的对照关系

以下没特殊说明都为rc隔离级别

insert

  • 无unique key,插入后 :无论rc或rr隔离级别都是对主键加 lock_x+lock_rec_not_gap
  • 有unique key

    插入前,唯一约束检查:lock_s+lock_ordinary
    插入前,插入的位置有gap锁:lock_insert_intetion
    插入后,新数据插入:lock_x+lock_rec_not_gap

delete

满足删除条件的所有记录:lock_x+lock_rec_not_gap

update

update操作分解

  • step 1:定位到 下一条满足查询条件的记录(查询过程,类似于select/delete)
  • step 2:删除当前定位到的记录(标记为删除状态)
  • step 3:拼装更新后项,根据更新后项定位到 新的插入位置
  • step 4:在新的插入位置,判断是否存在 unique 冲突( 存在unique key 时
  • step 5:插入更新后项(不存在unique冲突时)
  • step 6: 重复step 1 到step 5 的操作,直至扫描完整个查询范围

update操作分析

  • step 1,step 2:delete
  • step 3,step 4,step 5:insert

update

  • 无unique key:

    • 查询范围中的所有记录,lock_x + lock_rec_not_gap
  • 有unique key:

    • 查找满足条件的记录:查询范围内的所有记录, lock_x + lock_rec_not_gap
    • 更新后项存在唯一性冲突:冲突项上的加锁,lock_s + lock_ordinary
    • 更新后项不存在唯一性冲突: 更新位置后项加锁,lock_s + lock_gap (省略)
    • 实际更新操作:可看做插入了一条新纪录,lock_x + lock_rec_not_gap

gap锁

那些操作会加gap锁?

  • read committed (rc) ) :unique key 唯一约束检查;purge操作;
  • repeatable read (rc ):rc的基础上,所有需要加锁的索引范围扫描和索引查找(update/delete…)
  • 还有一种会加gap锁:rr隔离级别下,对有唯一索引的表执行insert on duplicate update操作,除了会对新插入的记录加x not gap外,还会对相邻记录加x gap

如何去掉gap锁?

change the transaction isolation level to read committed or enable the innodb_locks_unsafe_for_binlog system variable (which is now deprecated)

什么时候加next-key lock?

by default, innodb operates in repeatable read transaction isolation level. in this case, innodb uses next-key locks for searches and index scans, which prevents phantom rows

insert intention lock

an insert intention lock is a type of gap lock set by insert operations prior to(在...之前) row insertion.

总结

• 原则之一

  • 要分析一个死锁,必须深入业务,了解整个事务的逻辑(闭门无法造车)

• 原则之二`

  • gap锁很复杂,为了减少gap锁,减少gap导致的死锁,尽量选择read committed隔离级别(rc + row based binlog,基本上能够解决所有问题,无需使用repeatable read)
  • 适当的 减少unique 索引,能够减少gap锁导致的死锁(根据业务情况而定)

• 原则之三

  • 在mysql 中,以不同索引的过滤条件, 来操作相同的记录(update/delete ),很容易产生死
    锁。

• 原则之四

  • rc隔离级别下,如果死锁中出现next key(gap锁),说明表中一定存在unique索引
  • 多语句事务产生的死锁,确保每条语句操作记录的顺序性,能够极大减少死锁

本文大多数都整理自《死锁-何登成 - 管中窥豹——mysql(innodb)死锁分析之道》

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网