脏读:
有两个事务 T1/T2 同时在执行,T1 事务有可能会读取到 T2 事务未提交的数据,但是未提交的事务 T2 可能会回滚,也就导致了 T1 事务读取到最终不一定存在的数据产生脏读的现象。
不可重复读:
设有两个事务 T1/T2 同时执行,事务 T1 在不同的时刻读取同一行数据的时候结果可能不一样,从而导致不可重复读数问题。
幻读:
有两个事务 T1/T2 同时执行,事务 T1 执行范围查询或者范围修改的过程中,事务 T2 插入了一条属于事务 T1 范围内的数据并且提交了,这时候在事务 T1 查询发现多出来了一条数据,或者在 T1 事务发现这条数据没有被修改,看起来像是产生了幻觉,这种现象称为幻读。
解决不可重复读问题:
MySQL的InnoDB引擎,在默认的REPEATABLE READ的隔离级别下,实现了可重复读,同时也解决了幻读问题。它使用Next-Key Lock算法实现了行锁,并且不允许读取已提交的数据,所以解决了不可重复读的问题。另外,该算法包含了间隙锁,会锁定一个范围,因此也解决了幻读的问题。
事务隔离级别
RR是如何解决不可重复读和脏读的:通过MVCC解决
RR 隔离级别实现原理,就是 MVCC(多版本并发控制),而 MVCC 是通过 ReadView+ Undo Log 实现的,Undo Log 保存了历史快照,Read View 可见性规则帮助判断当前版本的数据是否可见
Undo Log 版本链长这样:Read view 的几个重要属性
m_ids :当前系统中那些活跃(未提交)的读写事务 ID, 它数据结构为一个 List。
min_limit_id :表示在生成 Read View 时,当前系统中活跃的读写事务中最小的事务 id,即
m_ids 中的最小值。
max_limit_id :表示生成 Read View 时,系统中应该分配给下一个事务的 id 值。
creator_trx_id : 创建当前 Read View 的事务 ID
Read view 可见性规则如下:
-
如果数据事务 ID trx_id < min_limit_id ,表明生成该版本的事务在生成 ReadView 前,已经提交(因为事务 ID 的递增的),所以该版本可以被当前事务访问。
-
如果 trx_id>= max_limit_id ,表明生成该版本的事务在生成 Read View 后才生成,所以该版本不可以被当前事务访问。
-
如果 min_limit_id =<trx_id< max_limit_id ,需要分 3 种情况讨论
如果 m_ids 包含 trx_id ,则代表 Read View 生成时刻,这个事务还未提交,但是如果数
据的 trx_id 等于 creator_trx_id 的话,表明数据是自己生成的,因此是可见的。
3.2 如果 m_ids 包含 trx_id ,并且 trx_id 不等于 creator_trx_id ,则 Read View 生成时,
事务未提交,并且不是自己生产的,所以当前事务也是看不见的;
3.3 如果 m_ids 不包含 trx_id ,则说明你这个事务在 Read View 生成之前就已经提交了,
修改的结果,当前事务是能看见的
RR 如何解决不可重复读总结
查询一条记录,基于 MVCC,是怎样的流程:
-
获取事务自己的版本号,即事务 ID
-
获取 Read View
-
查询得到的数据,然后 Read View 中的事务版本号进行比较。
-
如果不符合 Read View 的可见性规则, 即需要 Undo log 中历史快照;
-
最后返回符合规则的数据