mysql:MVCC的实现原理
程序员文章站
2022-05-04 07:57:22
...
MVCC,中文叫多版本并发控制,它是通过读取历史版本的数据,来降低并发事务冲突,从而提高并发性能的一种机制。它的实现依赖于隐式字段、undo日志、快照读&当前读、Read View,因此,我们先来了解这几个知识点。
隐式字段
对于InnoDB存储引擎,每一行记录都有两个隐藏列DB_TRX_ID、DB_ROLL_PTR,如果表中没有主键和非NULL唯一键时,则还会有第三个隐藏的主键列DB_ROW_ID。
- DB_TRX_ID,记录每一行最近一次修改(修改/更新)它的事务ID,大小为6字节;
- DB_ROLL_PTR,这个隐藏列就相当于一个指针,指向回滚段的undo日志,大小为7字节;
- DB_ROW_ID,单调递增的行ID,大小为6字节;
undo日志
- 事务未提交的时候,修改数据的镜像(修改前的旧版本),存到undo日志里。以便事务回滚时,恢复旧版本数据,撤销未提交事务数据对数据库的影响。
- undo日志是逻辑日志。可以这样认为,当delete一条记录时,undo log中会记录一条对应的insert记录,当update一条记录时,它记录一条对应相反的update记录。
- 存储undo日志的地方,就是回滚段。
多个事务并行操作某一行数据时,不同事务对该行数据的修改会产生多个版本,然后通过回滚指针(DB_ROLL_PTR)连一条Undo日志链。
我们通过例子来看一下~
mysql> select * from account ;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | Jay | 100 |
+----+------+---------+
1 row in set (0.00 sec)
复制代码
- 假设表accout现在只有一条记录,插入该该记录的事务Id为100
- 如果事务B(事务Id为200),对id=1的该行记录进行更新,把balance值修改为90
事务B修改后,形成的Undo Log链如下:
快照读&当前读
快照读:
读取的是记录数据的可见版本(有旧的版本),不加锁,普通的select语句都是快照读,如:
select * from account where id>2;
复制代码
当前读:
读取的是记录数据的最新版本,显示加锁的都是当前读
select * from account where id>2 lock in share mode;
select * from account where id>2 for update;
复制代码
Read View
- Read View就是事务执行快照读时,产生的读视图。
- 事务执行快照读时,会生成数据库系统当前的一个快照,记录当前系统中还有哪些活跃的读写事务,把它们放到一个列表里。
- Read View主要是用来做可见性判断的,即判断当前事务可见哪个版本的数据~
为了下面方便讨论Read View可见性规则,先定义几个变量
- m_ids:当前系统中那些活跃的读写事务ID,它数据结构为一个List。
- min_limit_id:m_ids事务列表中,最小的事务ID
- max_limit_id:m_ids事务列表中,最大的事务ID
- 如果DB_TRX_ID < min_limit_id,表明生成该版本的事务在生成ReadView前已经提交(因为事务ID是递增的),所以该版本可以被当前事务访问。
- 如果DB_TRX_ID > m_ids列表中最大的事务id,表明生成该版本的事务在生成ReadView后才生成,所以该版本不可以被当前事务访问。
- 如果 min_limit_id =<DB_TRX_ID<= max_limit_id,需要判断m_ids.contains(DB_TRX_ID),如果在,则代表Read View生成时刻,这个事务还在活跃,还没有Commit,你修改的数据,当前事务也是看不见的;如果不在,则说明,你这个事务在Read View生成之前就已经Commit了,修改的结果,当前事务是能看见的。
RR跟RC隔离级别,最大的区别就是:RC每次读取数据前都生成一个ReadView,而RR只在第一次读取数据时生成一个ReadView。