当前位置:Gxlcms > 数据库问题 > MySQL 的MVCC机制

MySQL 的MVCC机制

时间:2021-07-01 10:21:17 帮助过:17人阅读

:隐藏字段、Read View、Undo log。

1.1、隐藏字段

        InnoDB存储引擎在每行数据的后面添加了三个隐藏字段:         1. 6字节的事务ID(DB_TRX_ID):表示最近一次对本记录行作修改(insert | update)的事务ID。至于delete操作,InnoDB认为是一个update操作,不过会更新一个另外的删除位,将行表示为deleted。并非真正删除。         2. 7字节的回滚指针(DB_ROLL_PTR):回滚指针,指向当前记录行的undo log信息,也就是记录行的历史版本         3. 6字节的行号(DB_ROW_ID):随着新行插入而单调递增的行ID。这个DB_ROW_ID跟MVCC关系不大。当表没有主键或唯一非空索引时,innodb就会使用这个行ID自动产生聚簇索引。如果表有主键或唯一非空索引,聚簇索引就不会包含这个行ID了。

1.2、Read View 结构(重点)

       其实Read View(读视图),跟快照、snapshot是一个概念。        Read View 里面保存了“对本事务不可见的其他活跃事务”,主要是用来做可见性判断的,       Read View 比较重要的3个字段是low_limit_id, up_limit_id以及一个数组trx_ids,         ① trx_ids:Read View创建时其他未提交的活跃事务ID列表。意思就是创建Read View时,将当前未提交事务ID记录下来,后续即使它们修改了记录行的值,对于当前事务也是不可见的。
         low_limit_id:目前出现过的最大的事务ID+1,即下一个将被分配的事务ID。源码 350行:         ③ up_limit_id活跃事务列表trx_ids中最小的事务ID,如果trx_ids为空,则up_limit_id 等于low_limit_id。(也就是让下界等于上界)源码 358行
一旦一个Read View被创建,这三个参数将不再发生变化,理解这点很重要,其中low_limit_id 和 up_limit_id分别是 trx_Ids数组的上下界。

1.3 Undo log       

        Undo log中存储的是老版本数据,当一个事务需要读取记录行时,如果当前记录行不可见,可以顺着undo log链找到满足其可见性条件的记录行版本。         大多数对数据的变更操作包括 insert/update/delete,在InnoDB里,undo log分为如下两类:         ①insert undo log : 事务对insert新记录时产生的undo log, 因为不存在正在对这行数据进行读的事务,所以这个日志只在事务回滚时需要, 并且在事务提交后就可以立即丢弃。         ②update undo log : 事务对记录进行delete和update操作时产生的undo log,不仅在事务回滚时需要,快照读也需要,因为可能存在正在对这行数据进行读的事务,只有当数据库所使用的快照中不涉及该日志记录,对应的回滚日志才会被purge线程删除。

2、记录行修改的具体流程

         ① 首先当前事务对记录行加排他锁                    ② 然后把改行数据拷贝到undo log中,作为旧版本         ③ 拷贝完毕后,修改该行的数据,并且修改记录行最新的修改事务id ,也就是DB_TRX_ID为当前事务id         ④ 事务提交,提交前用 CAS 机制判断记录行当前最新修改的事务id 是否发生了变化,如果没变,则提交成功,如果变了,说明存在其他事务修改了这个记录行,那么就应该回滚这个事务。也就是当前事务没有生效。

3. 记录行查询时的可见性判断算法

        在innodb中,创建一个新事务后,执行第一个select语句的时候,innodb会创建一个快照(read view),快照中会保存系统当前不应该被本事务看到的其他活跃事务id列表(即trx_ids)。当用户在这个事务中要读取某个记录行的时候,innodb会将该记录行的DB_TRX_ID与该Read View中的一些变量进行比较,判断是否满足可见性条件。         假设当前事务要读取某一个记录行,该记录行的DB_TRX_ID(即最新修改该行的事务ID)为trx_id,Read View的活跃事务列表trx_ids的上下界分别为 low_limit_id 和 up_limit_id. 具体的比较算法如下:         1. 如果 trx_id < up_limit_id, 那么表明“最新修改该行的事务”在“当前事务”创建快照之前就提交了,所以该记录行的值对当前事务是可见的。直接标识为可见,返回true,          2. 如果 trx_id >= low_limit_id, 那么表明“最新修改该行的事务”在“当前事务”创建快照之后才被创建且修改该行的,所以该记录行的值对当前事务不可见。应该通过回滚指针找到上个记录行版本,判断是否可见。循环往复,直到可见         3. 如果 up_limit_id <= trx_id < low_limit_id, 那就得通过二分查找判断trx_id 是否在trx_ids列表出现过,             ① 如果出现过,说明是当前read view 中某个活跃的事务提交了,那当然是不可见的,应该通过回滚指针找到上个记录行版本,判断是否可见,循环往复,直到可见             ② 如果没有出现过,说明这个事务是已经提交了的,表示为可见,返回true up_limit_id <= trx_id < low_limit_id 且在trx_ids中没找到 trx_id的情况举例: 技术图片

 

 

4. RR和RC的Read View的实现过程区别

提交读和可重复读都是使用 MVCC 机制来实现的,但是实现过程略微有一些不同,         ①在innodb中的Repeatable Read级别, 只有事务在begin之后,执行第一条select(读操作)时, 才会创建一个快照(read view),将当前系统中活跃的其他事务记录起来;并且事务结束前都是使用的这个快照,不会重新创建,直到事务结束。属于快照读         ②在innodb中的Read Committed级别, 事务在begin之后,执行每条select(读操作)语句时,快照会被重置,即会重新创建一个快照(read view)。属于当前读 所以这个实现的差别可以达到不同的隔离级别
补充:
        快照读(snapshot read):普通的 select 语句(不包括 select ... lock in share mode, select ... for update)         当前读(current read) :select ... lock in share mode,select ... for update,insert,update,delete 语句(这些语句获取的是数据库中的最新数据,官方文档:14.7.2.4 Locking Reads )  

本文节选自:MySQL中MVCC的正确打开方式(源码佐证),强力推荐这篇博客

MySQL 的MVCC机制

标签:将不   最大   行数据   博客   读取   事务隔离   locking   har   行修改   

人气教程排行