携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第22天,点击查看活动详情
一、什么是MVCC
MVCC (Multiversion Concurrency Control),多版本并发控制。顾名思义,MVCC 是通过数据行的多个版本管理来实现数据库的 并发控制 。这项技术使得在InnoDB的事务隔离级别下执行 一致性读 操作有了保证。换言之,就是为了查询一些正在被另一个事务更新的行,并且可以看到它们被更新之前的值,这样在做查询的时候就不用等待另一个事务释放锁。
二、快照读与当前读
MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理 读-写冲突 ,做到即使有读写冲突时,也能做到 不加锁 , 非阻塞并发读 ,而这个读指的就是 快照读 , 而非 当前读 。当前读实际上是一种加锁的操作,是悲观锁的实现。而MVCC本质是采用乐观锁思想的一种方式。
1、快照读
快照读又叫一致性读,读取的是快照数据。不加锁的简单的 SELECT 都属于快照读,即不加锁的非阻塞读;之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于MVCC,它在很多情况下,避免了加锁操作,降低了开销。既然是基于多版本,那么快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本。快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读。
2、当前读
当前读读取的是记录的最新版本(最新数据,而不是历史版本的数据),读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。加锁的 SELECT,或者对数据进行增删改都会进行当前读。
三、MVCC实现原理之ReadView
MVCC 的实现依赖于:隐藏字段、Undo Log、Read View。
使用 READ UNCOMMITTED 隔离级别的事务,由于可以读到未提交事务修改过的记录,所以直接读取记录的最新版本就好了。
使用 SERIALIZABLE 隔离级别的事务,InnoDB规定使用加锁的方式来访问记录。
使用 READ COMMITTED 和 REPEATABLE READ 隔离级别的事务,都必须保证读到 已经提交了的 事务修改过的记录。假如另一个事务已经修改了记录但是尚未提交,是不能直接读取最新版本的记录的,核心问题就是需要判断一下版本链中的哪个版本是当前事务可见的,这是ReadView要解决的主要问题。
这个ReadView中主要包含4个比较重要的内容,分别如下:
- creator_trx_id ,创建这个 Read View 的事务 ID。
- trx_ids ,表示在生成ReadView时当前系统中活跃的读写事务的 事务id列表 。
- up_limit_id ,活跃的事务中最小的事务 ID。
- low_limit_id ,表示生成ReadView时系统中应该分配给下一个事务的 id 值。low_limit_id 是系统最大的事务id值,这里要注意是系统中的事务id,需要区别于正在活跃的事务ID。
四、总结
MVCC 在 READ COMMITTD 、 REPEATABLE READ 这两种隔离级别的事务在执行快照读操作时访问记录的版本链的过程。这样使不同事务的 读-写 、 写-读 操作并发执行,从而提升系统性能。
核心点在于 ReadView 的原理, READ COMMITTD 、 REPEATABLE READ 这两个隔离级别的一个很大不同就是生成ReadView的时机不同:
- READ COMMITTD 在每一次进行普通SELECT操作前都会生成一个ReadView
- REPEATABLE READ 只在第一次进行普通SELECT操作前生成一个ReadView,之后的查询操作都重复使用这个ReadView就好了。