原因

mysql作为OLTP系统,存在如下三种情况:

  1. 脏读
    1. session1:select where id = 1 为空
    2. 并发session2:insert id = 1 未提交
    3. session1:select where id = 1 不为空
  2. 不可重复读
    1. session1:select where id = 1,name = A
    2. 并发session2:update set name = B where id = 1 已提交
    3. session1:select where id = 1,name = B
  3. 幻读
    • session1:select where id = 1 为空
    • 并发session2:insert id = 1 已提交
    • session1:insert id = 1,duplicate primary key

隔离

以上面三个现象隔离出四种隔离级别

脏读 不可重复读 幻读
Uncommit Read | Committed Read | Repeatable Read | Serializable
快照读 快照读+gap锁

实现

  1. UR到CR是通过快照读实现的,原因在于快照读的快照是mysql的实现是存在undolog中,而undolog存放的内容一定是其他已提交的事务的数据,所以可以保证CR。
  2. CR到RR也是通过快照读实现的,那为什么不是UR直接到了RR,而还有个CR呢?是因为快照读实现的机制不一样,或者说获取快照的时机不一样,UR到CR的获取的快照时机是每次执行select的时候都会获取最新的快照:
    1. 事务A select(快照1,不考虑其他事务的话,这里就是取表记录数据)
    2. 事务B update
    3. 事务A select(快照1,这里取的是undolog记录,但是undolog里面的数据就是第一次select的数据)
    4. 事务B commit
    5. 事务A select(快照2,这里就是取表记录数据)
  3. CR到RR的快照读是怎么处理的呢?事务创建的时候就创建快照,事务结束前一直使用该快照。
  4. 在RR级别,mysql采用了gap锁,消除了幻读
  5. 并发写(包括select …for update)的加锁方式如下:
    1. where条件包含唯一索引且命中,加行锁。
    2. 唯一索引没命中、普通索引命中、普通索引没命中等情况,都是加gap锁。
    3. 无索引,加表锁。