通过MVCC实现数据库的可重复读

整理知识点,有四种隔离级别,也要知道每种的实现原理。。emmm

一、 MVCC


Multi-Version Concurrency Control 多版本并发控制,MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问;在编程语言中实现事务内存。它使得大部分支持行锁的事务引擎,不再单纯的使用行锁来进行数据库的并发控制,取而代之的是把数据库的行锁与行的多个版本结合起来,只需要很小的开销,就可以实现非锁定读,从而大大提高数据库系统的并发性能

一句话讲,MVCC就是用 同一份数据临时保留多版本的方式 的方式,实现并发控制。

这里留意到 MVCC 关键的两个点:

  1. 在读写并发的过程中如何实现多版本;
  2. 在读写并发之后,如何实现旧版本的删除(毕竟很多时候只需要一份最新版的数据就够了);

二、概念

  1. 系统版本号:每开启一个事务,系统版本号就会自增
  2. 事务版本号:事务创建时的系统版本号
  3. 创建版本号:创建一个数据行快照时的系统版本号
  4. 删除版本号:如果快照的删除版本号大于事务版本号,说明该快照有效;否则表示该快照已经被删除。
  5. innodb存储的最基本row中包含一些额外的存储信息 DATA_TRX_ID,DATA_ROLL_PTR,DB_ROW_ID,DELETE BIT
  6. 6字节的DATA_TRX_ID 标记了最新更新这条行记录的transaction id,每处理一个事务,其值自动+1
  7. 7字节的DATA_ROLL_PTR 指向当前记录项的rollback segment的undo log记录,找之前版本的数据就是通过这个指针
  8. 6字节的DB_ROW_ID,当由innodb自动产生聚集索引时,聚集索引包括这个DB_ROW_ID的值,否则聚集索引中不包括这个值.,这个用于索引当中
  9. DELETE BIT位用于标识该记录是否被删除,这里的不是真正的删除数据,而是标志出来的删除。真正意义的删除是在commit的时候

三、Undo日志

MVCC使用的快照保存在日志中,该日志通过回滚指针把一个数据行的所有快照连接起来。

●  redo log

    MySQL在开启事务时,会将执行的SQL保存到指定的log文件,即redo log。当MySQL执行recovery时执行redo log里的SQL操作即可。redo log不会被立即写入磁盘,会先写入redo buffer;当客户端执行commit时,redo buffer的内容会视情况存入磁盘。

●  undo log

    与redo log相反,undo log是为了回滚事务而写的日志,具体内容就是copy事务开始前的数据(行)到undo buffer。

    与redo buffer一样,undo buffer也是环形缓冲,当缓冲满的时候buffer内容会被刷新到磁盘。

    与redo log不同的是,undo log没有独立的磁盘文件,所有的undo log均被存在主ibd数据文件中(表空间)。

四、快照读和当前读

快照读:读取的是快照版本,也就是历史版本,MVCC读取的是快照中的数据,可以减少加锁带来的开销。

当前读:读取的是最新版本,读的是最新数据,需要加锁。

普通的SELECT就是快照读,而UPDATE、DELETE、INSERT、SELECT ...  LOCK IN SHARE MODE、SELECT ... FOR UPDATE是当前读。

五、 实现过程

innodb MVCC主要是为Repeatable-Read事务隔离级别做的。在此隔离级别下,A、B客户端所示的数据相互隔离,互相更新不可见

了解innodb的行结构、Read-View的结构对于理解innodb mvcc的实现由重要意义具体的执行过程

begin->用排他锁锁定该行->记录redo log->记录undo log->修改当前行的值,写事务编号,回滚指针指向undo log中的修改前的行

上述过程确切地说是描述了UPDATE的事务过程,其实undo log分insert和update undo log,因为insert时,原始的数据并不存在,所以回滚时把insert undo log丢弃即可,而update undo log则必须遵守上述过程

下面分别以select、delete、 insert、 update语句来说明

关键:开启一个事务时,该事务版本号肯定大于当前所有数据行的创建版本号

SELECT

Innodb检查每行数据,确保他们符合两个标准:

1、InnoDB只查找版本早于当前事务版本的数据行(也就是数据行的版本必须小于等于事务的版本),这确保当前事务读取的行都是事务之前已经存在的,或者是由当前事务创建或修改的行

2、行的删除操作的版本一定是未定义的或者大于当前事务的版本号,确定了当前事务开始之前,行没有被删除

符合了以上两点则返回查询结果。

INSERT

InnoDB为每个新增行记录当前系统版本号作为创建ID。

DELETE

InnoDB为每个删除行的记录当前系统版本号作为行的删除ID。

UPDATE

新插入一行(复制了要删除的记录),并以当前事务的版本号作为新行的创建版本号,同时将原记录行的删除版本号设置为当前事务版本号

说明

insert操作时 “创建时间”=DB_ROW_ID,这时,“删除时间 ”是未定义的;

update时,复制新增行的“创建时间”=DB_ROW_ID,删除时间未定义,旧数据行“创建时间”不变,删除时间=该事务的DB_ROW_ID;

delete操作,相应数据行的“创建时间”不变,删除时间=该事务的DB_ROW_ID;

select操作对两者都不修改,只读相应的数据

六、对于MVCC的总结

上述更新前建立undo log,根据各种策略读取时非阻塞就是MVCC,undo log中的行就是MVCC中的多版本,这个可能与我们所理解的MVCC有较大的出入,一般我们认为MVCC有下面几个特点:

  • 每行数据都存在一个版本,每次数据更新时都更新该版本
  • 修改时Copy出当前版本随意修改,各个事务之间无干扰
  • 保存时比较版本号,如果成功(commit),则覆盖原记录;失败则放弃copy(rollback)
  • 事务以排他锁的形式修改原始数据
  • 把修改前的数据存放于undo log,通过回滚指针与主数据关联
  • 修改成功(commit)啥都不做,失败则恢复undo log中的数据(rollback)

你可能感兴趣的:(MySql)