1.前文
前面介绍了数据库锁的作用和mvcc实现的隔离级别,这里探讨一下mysql中mvcc的实现
前文:
https://blog.csdn.net/qq_24516549/article/details/89374174
https://blog.csdn.net/qq_24516549/article/details/89405983
2.高性能mysql里介绍的mvcc实现
3.innodb的mvcc实现
行记录有以下隐藏字段:
- db_row_id,行ID,用来生成默认聚簇索引(聚簇索引,保存的数据在物理磁盘中按顺序保存,这样相关数据保存在一起,提高查询速度)
- db_trx_id,事务ID,新开始一个事务时生成,实例内全局唯一
- db_roll_ptr,undo log指针,指向对应记录当前的undo log
- deleted_bit,删除标记位,删除时设置
3.1 undo log
undo log用于行记录回滚,同时用于实现MVCC
具体的mvcc操作:
update
行记录数据写入undo log,事务的回滚操作就需要undo log
更新行记录数据,当前事务ID写入db_trx_id,undo log指针写入db_roll_ptr
delete
和update一样,只增加deleted_bit设置
insert
生成undo log
插入行记录数据,当前事务ID写入db_trx_id, db_roll_ptr为空
这样设计使得读操作很简单,性能很好,并且也能保证只会读到符合标准的行,不足之处是每行记录都需要额外的储存空间,需要做更多的行检查工作,以及额外的维护工作
DB_ROLL_PTR用来指定undolog里的行号,同时undolog里的该行也会有上一次的DB_ROLL_PTR来指定上一次修改,这看起来像是这一行有多个版本,他们像链表一样被连接
3.2 read view
mvcc还需要判断当前事务对记录的可读性,通过read_view判断
在InnoDB中,创建一个新事务的时候,InnoDB会将当前系统中的活跃事务列表(trx_sys->trx_list)创建一个副本(read view),副本中保存的是系统当前不应该被本事务看到的其他事务id列表。当用户在这个事务中要读取该行记录的时候,InnoDB会将该行当前的版本号与该read view进行比较。
具体的算法如下:
设该行的当前事务id为trx_id,read view中最早的事务id为trx_id_min, 最迟的事务id为trx_id_max。
如果trx_id< trx_id_min的话,那么表明该行记录所在的事务已经在本次新事务创建之前就提交了,所以该行记录的当前值是可见的。
如果trx_id>trx_id_max的话,那么表明该行记录所在的事务在本次新事务创建之后才开启,所以该行记录的当前值不可见。
如果trx_id_min <= trx_id <= trx_id_max, 那么表明该行记录所在事务在本次新事务创建的时候处于活动状态,从trx_id_min到trx_id_max进行遍历,如果trx_id等于他们之中的某个事务id的话,那么不可见。
从该行记录的DB_ROLL_PTR指针所指向的回滚段中取出最新的undo-log的版本号的数据,将该可见行的值返回。
需要注意的是,新建事务(当前事务)与正在内存中commit 的事务不在活跃事务链表中。