目录
1.1 定义
MVCC(Multiversion concurrency control):多版本并发控制,并发访问(读或写)数据库时,对正在事务内处理的数据做多版本的管理。以达到用来避免写操作的堵塞,从而引发读操作的并发问题。
MVCC 在 MySQL InnoDB 中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读
1.2 当前读和快照读
1.2.1 当前读
select lock in share mode (共享锁)
select for update;
update; insert; delete (排他锁)
这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
1.2.2 快照读
普通的select ... 操作都是快照读,读取的数据可能是历史快照数据,不是数据库最新的数据,快照读是通过 MVCC 和 Undo Log 实现的。(注意:快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读)
说白了 MVCC 就是为了实现读-写冲突不加锁,而这个读指的就是快照读, 而非当前读,当前读实际上是一种加锁的操作,是悲观锁的实现
1.3 MVCC能解决什么问题
- 读写之间阻塞的问题,通过 MVCC 可以让读写互相不阻塞,读不相互阻塞,写不阻塞读,这样可以提升数据并发处理能力。
- 降低了死锁的概率,这个是因为 MVCC 采用了乐观锁的方式,读取数据时,不需要加锁,写操作,只需要锁定必要的行。
- 解决了一致性读的问题,当我们朝向某个数据库在时间点的快照是,只能看到这个时间点之前事务提交更新的结果,不能看到时间点之后事务提交的更新结果。
1.4 MVCC实现原理
1.4.1 隐式字段
1.例子:
1.4.2 undo log日志
1.例子:
2.基于UNDO_LOG版本连
3.为什么要有undo日志?
数据库事务未提交时,会将事务修改数据的镜像(即修改前的旧版本)存放到 undo 日志里,当事务回滚时,或者数据库奔溃时,可以利用 undo 日志,即旧版本数据,撤销未提交事务对数据库产生的影响。
- 对于 insert 操作,undo 日志记录新数据的 PK(ROW_ID),回滚时直接删除;
- 对于 delete/update 操作,undo 日志记录旧数据 row,回滚时直接恢复;
- 他们分别存放在不同的 buffer 里。
一句话,undo 日志用于保障,未提交事务不会对数据库的 ACID 特性产生影响。
4.什么是回滚段?
存储 undo 日志的地方,是回滚段。
1.4.3 Read View
1.读已提交(PC):在每一次执行快照读时生成ReadView
数据提取过程:
(PS:m_ids:记录哪些事务还没有被提交)
2.可重复读(RR):仅在第一次执行快照读时生成ReadView,后续快照读复用
3.RR级别下使用MVCC能避免幻读吗?
答:能,但不完全能!
解释:连续多次快照读,ReadView会产生复用,没有幻读问题
特例:当两次快照读之间存在当前读,ReadView会重新生成,导致产生幻读
例:
1.5 如何查询一条记录
- 获取事务自己的版本号,即 事务ID
- 获取 Read View
- 查询得到的数据,然后 Read View 中的事务版本号进行比较。
- 如果不符合 ReadView 规则, 那么就需要 UndoLog 中历史快照;
- 最后返回符合规则的数据
InnoDB 实现多版本控制 (MVCC)是通过 ReadView+ UndoLog 实现的,UndoLog 保存了历史快照,ReadView 规则帮助判断当前版本的数据是否可见。
1.6 redo log日志
为什么要有redo日志?
数据库事务提交后,必须将更新后的数据刷到磁盘上,以保证 ACID 特性。磁盘随机写性能较低,如果每次都刷盘,会极大影响数据库的吞吐量。
优化方式是,将修改行为先写到 redo 日志里(此时变成了顺序写),再定期将数据刷到磁盘上,这样能极大提高性能。
这里的架构设计方法是,随机写优化为顺序写,思路更重要。
假如某一时刻,数据库崩溃,还没来得及刷盘的数据,在数据库重启后,会重做 redo 日志里的内容,以保证已提交事务对数据产生的影响都刷到磁盘上。
一句话,redo 日志用于保障,已提交事务的 ACID 特性。
1.7 总结
- 常见并发控制保证数据一致性的方法有锁,数据多版本;
- 普通锁串行,读写锁读读并行,数据多版本读写并行;
- redo 日志保证已提交事务的 ACID 特性,设计思路是,通过顺序写替代随机写,提高并发;
- undo 日志用来回滚未提交的事务,它存储在回滚段里;
- InnoDB 是基于 MVCC 的存储引擎,它利用了存储在回滚段里的 undo 日志,即数据的旧版本,提高并发;
- InnoDB 之所以并发高,快照读不加锁;
- InnoDB 所有普通 select 都是快照读;