原因
mysql作为OLTP系统,存在如下三种情况:
- 脏读
- session1:select where id = 1 为空
- 并发session2:insert id = 1 未提交
- session1:select where id = 1 不为空
- 不可重复读
- session1:select where id = 1,name = A
- 并发session2:update set name = B where id = 1 已提交
- session1:select where id = 1,name = B
- 幻读
- session1:select where id = 1 为空
- 并发session2:insert id = 1 已提交
- session1:insert id = 1,duplicate primary key
隔离
以上面三个现象隔离出四种隔离级别
脏读 不可重复读 幻读
Uncommit Read | Committed Read | Repeatable Read | Serializable
快照读 快照读+gap锁
实现
- UR到CR是通过快照读实现的,原因在于快照读的快照是mysql的实现是存在undolog中,而undolog存放的内容一定是其他已提交的事务的数据,所以可以保证CR。
- CR到RR也是通过快照读实现的,那为什么不是UR直接到了RR,而还有个CR呢?是因为快照读实现的机制不一样,或者说获取快照的时机不一样,UR到CR的获取的快照时机是每次执行select的时候都会获取最新的快照:
- 事务A select(快照1,不考虑其他事务的话,这里就是取表记录数据)
- 事务B update
- 事务A select(快照1,这里取的是undolog记录,但是undolog里面的数据就是第一次select的数据)
- 事务B commit
- 事务A select(快照2,这里就是取表记录数据)
- CR到RR的快照读是怎么处理的呢?事务创建的时候就创建快照,事务结束前一直使用该快照。
- 在RR级别,mysql采用了gap锁,消除了幻读
- 并发写(包括select …for update)的加锁方式如下:
- where条件包含唯一索引且命中,加行锁。
- 唯一索引没命中、普通索引命中、普通索引没命中等情况,都是加gap锁。
- 无索引,加表锁。