InnoDB锁与死锁

2020-06-29  本文已影响0人  轻松的鱼

InnoDB 有哪些锁?其表现和意义是什么?增删改查分别加什么锁?死锁是怎么产生的?怎么分析和避免?本文对这些问题做了些整理,没有写的很细,适合当大纲或者总结来看。

1. IoonDB锁机制

1.1 锁生存周期

2PL(Two-Phase Locking)
二阶段锁,说的是锁操作分为两个阶段:加锁阶段与解锁阶段,并且保证加锁阶段与解锁阶段不相交。

1.2 锁模式

InnoDB实现标准的行级锁,其中有两种模式的锁:

通常有一种含糊的说法:对行加S锁,其他事务只能对该行读,不能修改;对行加X锁,其他事务对该行不可读、写。其实这是不准确的,且不说InnoDB中的“读”为快照读不加锁,其关系也完全颠倒混乱了。应该以这样的顺序来理解:

  1. 事务T1执行 select * from t1 where id=1 lock in share mode,会先获取id=1行的共享(S)锁,才允许读取id=1这一行的数据;
  2. 事务T2同样执行 select * from t1 where id=1 lock in share mode,也要先申请获取id=1行的S锁,因为S锁之间兼容,可以成功获取锁,得以查询数据;
  3. 事务T3执行 update t1 set a=99 where id=1,要先申请id=1行的X锁,因为X锁与S锁互斥,所以获取不成功,需要等待,则更新也就无法执行。

所以对某行加S锁,只能说该事务可以读取这一行;对某行加X锁,只能说该事务可以读取、修改这一行。而不能直接说不允许其他事务读或者修改这一行。

1.3 锁属性

InnoDB实现的是行级锁没错,但是一细分,其实还有其他属性的锁,下面一一介绍。

1.4 锁组合

上面详细介绍了锁模式和锁属性,这两者是可以组合的,比如:记录锁+LOCK_S、记录锁+LOCK_X

1.5 锁冲突矩阵
要分析死锁,一定要清楚锁与锁之间的冲突关系: 锁冲突矩阵
1.6 SQL与加锁

有了前面的认识,接下来要掌握的就是在InnoDB中各种SQL操作需要加的锁。先记住一个概念:同一个SQL,在不同的索引、不同的隔离级别下加的锁是不同的。下面我们分别介绍在RC隔离级别下增、删、改、查4种SQL操作的加锁行为。

2. 阅读死锁日志

MySQL默认是开启死锁检测的,一旦发生死锁,InnoDB会回滚其中一个事务,将锁解放。默认 show engine innodb status 会记录上一次的死锁日志,也可以设置innodb_print_all_deadlocks 将每一次死锁的日志记录到error log中。

死锁日志中列出了死锁发生的时间,以及导致死锁的事务信息(只显示两个事务,如果由多个事务导致的死锁也只显示两个),并显示出每个事务正在执行的 SQL 语句(事务执行多个SQL,只会记录正在执行的那个)、等待的锁以及持有的锁信息等。死锁日志的局限性:

  1. 不显示事务1已经持有了什么锁;
  2. 不显示事务所有的SQL,也就没法推测事务1已经持有了什么锁。

3. 得到完整的事务

分析死锁原因的第2步就是联系开发获取事务1、事务2的全部SQL,然后写出每个SQL加的锁,构造可能死锁的执行顺序,然后进行复现。每个事务中SQL的执行顺序是固定的,但是2个事务并发执行,就会有多种顺序组合,并不是都会触发死锁。举个例子:

  1. 同样2个事务,按以下顺序执行会死锁:
  2. 按以下顺序执行却不会死锁:

2个事务可以有n*m种顺序组合(n、m表示事务中的SQL数量),很难一一列举出来进行分析,所以我们需要记住一些死锁的常见原因:

4. 死锁案例

  1. 事务以相反的顺序操作相同的数据
  2. 事务以不同索引的过滤条件,来操作相同的记录
  3. 唯一键冲突,插入相同数据

出了开发层面避免上面这些常见的死锁逻辑,数据库层面可以设置隔离级别为READ-COMMITTED,减少Gap Lock 产生的死锁。

上一篇下一篇

猜你喜欢

热点阅读