MySQL八股学习记录4事务的实现from小林coding
- 事务的概念与特性
- 并行事务引发的问题
- 脏读
- 不可重复读
- 幻读
- MySQL的应对策略
- InnoDB引擎可重复读详解
- ReadView在MVCC中的工作方式
- 两种隔离级别通过MVCC实现
- 幻读被完全解决了吗
事务的概念与特性
概念:一个操作要么执行成功,要么回滚到执行前的状态
特性:ACID
原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成
一致性(Consistency):事务操作前和操作后,满足用户层的约束性
隔离性(Isolation):数据库允许多个事务同时对数据进行读写,隔离性可以保证多个事务同时读写的数据一致性
持久性(Durability):事务结束后,对事务的修改就是永久的,即使系统故障也不会丢失
事务四大特性的保证
持久性:redo log(重做日志)
原子性:undo log(回滚日志)
隔离性:MVCC(多版本并发控制)或锁机制实现的
一致性:通过持久 + 原子性 + 隔离性 保证
并行事务引发的问题
脏读
一个事务读到另一个未提交事务修改后的数据称为脏读
上图
场景:若A发生了回滚,那么B得到的数据是错误的数据
不可重复读
一个事务内对同一个数据的两次读取,读取到的数据不相同,称为不可重复读
场景:若A读取了数据,并且B提交了事务,那么A再次读取的数据就会与A上次读到的数据不一样
幻读
一个事务内多次查询Count,若两次的记录数量并不相同,那么发生了幻读现象
场景:事务B查询数据,之后事务A插入一条数据,并且提交,这个时候B再去读数据就会发现两次读取到的条数不一致,就像发生了幻觉一样
MySQL的应对策略
由于上述问题的存在,MySQL提出了四种隔离级别分别是
- 读未提交:一个事务未提交,他的结果就能被其他事务看到
- 读已提交:一个事务提交后,结果才能被其他事务看到
- 可重复读:一个事务过程中看到的数据,与这个事务启动时看到的数据是一样的,InnoDB的默认隔离级别
- 串行化:对记录加上读写锁,多个事务读写必串行
InnoDB引擎可重复读详解
InnoDB引擎很大程度上的避免了幻读现象但并未完全避免方案有两种
- 针对快照读(普通select),MVCC方式解决幻读:
- 针对当前读(除了普通select都是这个读),next-key lock(记录锁+间隔锁)方式解决幻读
对于读提交和可重复读隔离级别的事务来说,通过Read View实现,读提交与可重复读的快照时间不同,可重复读是在每个事务之前生成一个快照,读提交是在每个语句前进行快照操作
ReadView在MVCC中的工作方式
聚簇记录中还有两个隐藏列
其中
- trx_id:改动该数据的事务ID
- roll_pointer:改变记录后,旧版本的记录将会被写入到undo日志中,通过该指针能找到旧版本的记录
创建Read View后,记录中的trx_id可以划分成三种情况
一个事务访问记录时,自己更新的记录总是可见,其余情况下有 - 如果记录的trx_id小于ReadView中的min_trx_id,那么表示该事务在创建ReadView之前生成,那么对当前事务可见
- 如果记录的trx_id大于等于max_trx_id,那么这个版本的记录在创建ReadView后才生成,那么对该版本记录不可见,通过undolog去查找
- 如果trx_id在ReadView的min_trx和max_trx之间,需要判断trx_id是否在m_ids列表中,若在,那么该版本的事务依然活跃,那么不可见,若不在,则已被提交,该版本的记录对当前事务可见
两种隔离级别通过MVCC实现
- 可重复读是事务开始时就快照,整个事务期间都使用这个快照
- 读已提交则是每次查询数据前生成新的快照
幻读被完全解决了吗
并没有,一种场景如下,事务A先查询一个数据,然后事务B插入一个数据,之后A再对该数据更新(虽然这样的操作很违和),这样之后A就能读B插入的数据,正因为这种特殊情况的存在,所以并不能认为MVCC解决了幻读