MySQL Transaction

MySQL 事务

事务原子性

事务隔离 Isolation

MySQL 中锁设计的一般思想

  • MySQL 中的锁 (InnoDB) 使用的是两阶段锁协议,也就是说锁是在需要的时候加上的,加锁后是在完成所有操作后一次性释放的

事务的一般问题和事务隔离级别

多个事务之间并发操作,存在几个一般问题:脏读、不可重复读、幻读以及丢失更新,不同的隔离级别可以不同程度的解决这些问题。

主要原因是在主从复制时,会引起主从数据不一致,statement 记录的的是执行语句,不同事务执行语句的顺序会严重影响在备库数据还原的状态。本质的原因是RC级别并不满足事务串行化执行要求,没有解决不可重复读和幻读。

幻读与Next-Key Locking

产生幻读的原因是,行锁只能锁住行,但新插入记录这个动作,要更新的是记录之间的“间隙”。因此,为了解决幻读问题,InnoDB 引入了新的锁,也就是间隙锁 (Gap Lock);间隙锁,锁的就是两个值之间的空隙

数据行是可以加上锁的实体,数据行之间的间隙,也是可以加上锁的实体;

行锁分读锁和写锁,读锁之间可以兼容,读写锁和写写锁都是互斥的,也就是说行锁之间是相互影响的;但是间隙锁不一样,跟间隙锁存在冲突关系的,是“往这个间隙中插入一个记录”这个操作。

几个注意点

  1. 间隙锁在可重复读隔离级别下才有效

  2. MySQL 中细粒度的锁有行锁、间隙锁、Next-Key Lock

  3. 一行数据可以加锁,数据之间的空隙也可以加锁,但是间隙锁之间不互斥,而是和插入数据这个操作互斥,也就是说加了间隙锁之后,往这个间隙中插入数据会被阻塞,直到间隙锁被释放了

  4. 锁是加在索引上的

  5. 如果用到覆盖索引,lock in share mode 只锁覆盖索引;for update 时,系统会认为接下来要更新数据,因此会顺便给主键索引上满足条件的行加上行锁;也就是说:如果要用 lock in share mode 来给行加读锁避免数据被更新的话,就必须得绕过覆盖索引的优化,在查询字段中加入索引中不存在的字段

加锁规则:

  1. 加锁的基本单位是 next-key lock, next-key lock 是前开后闭的区间

  2. 查找过程中访问到的对象才会加锁

  3. 索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁

  4. 索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁

  5. 唯一索引上的范围查询会访问到不满足条件的第一个值为止

一些优化结论:

  1. 在删除数据的时候尽量加 limit, 不仅可以控制删除数据的条数,让操作更安全,还可以减小加锁的范围

事务锁类型

  • 行锁

MySQL 的行锁有:行锁 (LOCK_REC_NOT_GAP)、间隙锁(LOCK_GAP)、Next-Key Lock

事务持久性

事务一致性

最后更新于