ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

MySQL高级系列--MVCC

2021-01-21 10:33:36  阅读:366  来源: 互联网

标签:事务 log 记录 -- MVCC undo MySQL ID


其他网址

【MySQL】当前读、快照读、MVCC - wwcom123 - 博客园

正确的理解MySQL的MVCC及实现原理-12172612-51CTO博客
数据库MVCC 隔离级别_数据库_Jaylon Wang的专栏-CSDN博客

简介

        多版本并发控制(Multi-Version Concurrency Control, MVCC),顾名思义,在并发访问的时候,数据存在版本的概念,可以有效地提升数据库并发能力,常见的数据库如MySQL、MS SQL Server、IBM DB2、Hbase、MongoDB等等都在使用。简单讲,如果没有MVCC,当想要读取的数据被其他事务用排它锁锁住时,只能互斥等待;而这时MVCC可以通过提供历史版本从而实现读取被锁的数据(的历史版本),避免了互斥等待。

在 MySQL中,MVCC是 InnoDB 存储引擎实现隔离级别的一种具体方式。

  • 未提交读:无需使用 MVCC(总是读取最新的数据行)
  • 提交读可重复读:使用MVCC来实现。
  • 可串行化:需要对所有读取的行都加锁,单纯使用 MVCC 无法实现。

MVCC一般有两种实现方式,本文所讲的InnoDB采用的是后者:

  • 实时保留数据的一个或多个历史版本
  • 在需要时通过undo日志构造出历史版本

快照读与当前读

简介

 

原理

        事务ID是在mysql开启事务时为其分配的递增序列号,由于是递增的,所以可以基于此判断事务先后关系。

        MVCC的多版本指的是针对数据库中的一行数据,都可能通过undolog中的数据算出多条行数据,每行数据版本不同(是为多版本),针对每次写操作,事务提交前,都会在undolog中记录相应的变动(是为回滚log),以及对应的事务ID,再结合数据表中的当前行数据,就可以回溯出一个行的的多个版本了。

        Innodb会为每行数据添加两个字段 up_txid、del_txid,分别是更新事务ID、删除事务ID,事务新增或者更新一个数据行后,会将该事务ID记录在该行数据的up_txid中,事务删除行数据后,会将该事务ID记录在del_txid中。

在read repeatable隔离级别下

        该隔离级别下的事务启动时,除了分配上面说的事务ID外,系统还会查出当前活跃的事务ID列表(也就是开启了但还未提交的事务),分配给该事务存储下来,有了这些信息,就可以实现快照读了,RR隔离级别下,其查询到的行数据需要满足:

  1. 行数据的up_txid<=当前事务ID,并且不在活跃事务ID列表中
  2. 行数据的del_txid为null,或者>当前事务ID,或者在活跃事务ID列表中

        简单理解下,只查询在当前事务开启之前就已经提交的数据,并且这行数据未被删除或者在当前事务开启后删除,相当于事务启动时,拍了个快照,事务执行期间,就通过这个快照读取数据,其他事务的变动不会再对当前事务产生影响,是为可重复读

        在读取时,会从最新的一条数据开始读起,如果满足条件就以其为准,如果不满足就找到更旧的一行数据继续判断。

read committed隔离级别下

        和RR隔离级别一样的是,RC隔离级别下的查询也是快照读,区别就是RC隔离级别下每次select时都会获取下当前活跃事务ID列表,然后从最新一行数据开始,判断是否满足如下条件,不满足则继续判断更旧的一行数据:

  1. 行数据的up_txid不在活跃事务ID列表中,表示已经提交
  2. 行数据的del_txid为null,或者在活跃事务ID列表中未提交

简单理解下,就是每次都读取当前已经提交的并且未被删除的最新数据,相当于每次查询都会拍个快照

当前读

        如果查询加了锁,就不在mvcc的控制范畴了,因为此时用的是当前读 。当前读的规则,就是要能读到所有已经提交的记录的最新值。当前读是由锁来保证的。Innodb中有行锁,上面举例的几条语句,都会锁住id=1的这行数据,这样其他事务如果要对id=1这行数据进行当前读,只能等行锁释放,等到啥时候?事务完成的时候会释放掉锁,既然事务都完成了,那其他事务自然能读取到已提交的最新值。

MVCC原理简述

在Mysql中MVCC是在Innodb存储引擎中得到支持的,InnoDb的最基本的行中包含一些额外的存储信息:DATA_TRX_ID,DATA_ROLL_PTR,DB_ROW_ID,DELETE BITInnodb为每行记录都实现了三个隐藏字段:

  • 6字节的事务ID(DB_TRX_ID )。
    (该行所的事务id,每处理一个事务,其值自动+1。可以基于此判断事务先后关系)
  • 7字节的回滚指针(DB_ROLL_PTR)。
    (指向当前记录项的rollback segment的undo log记录,找之前版本的数据就是通过这个指针)
  • 6字节的隐式主键(DB_ROW_ID)。
    Innodb自动产生聚集索引时,聚集索引包括这个DB_ROW_ID的值,否则聚集索引中不包括这个值,这个用于索引当中。
  • 删除标识位(DELETE BIT)。
    用于标识该记录是否被删除,这里的不是真正的删除数据,而是标志出来的删除。真正意义的删除是在commit的时候

MVCC并发控制的执行过程

以update为例:begin=> 用排他锁锁定该行=> 记录redo log=> 记录undo log=> 修改当前行的值,写事务编号

  • SELECT
    Innodb检查每行数据,确保他们符合两个标准:
    1、InnoDB只查找版本早于当前事务版本的数据行(也就是数据行的版本必须小于等于事务的版本),这确保当前事务读取的行都是事务之前已经存在的,或者是由当前事务创建或修改的行。
    2、行的删除操作的版本一定是未定义的或者大于当前事务的版本号,确定了当前事务开始之前,行没有被删除。
    符合了以上两点则返回查询结果。
  • INSERT
    InnoDB为每个新增行记录当前系统版本号作为创建ID。“创建时间”=DB_ROW_ID,这时,“删除时间 ”是未定义的;
  • DELETE
    InnoDB为每个删除行的记录当前系统版本号作为行的删除ID。
  • UPDATE
    InnoDB复制了一行。这个新行的版本号使用了系统版本号。它也把系统版本号作为了删除行的版本。

为了支持事务,Innbodb引入了下面几个概念:

  • redo log
    redo log就是保存执行的SQL语句到一个指定的Log文件,当Mysql执行recovery时重新执行redo log记录的SQL操作即可。当客户端执行每条SQL(更新语句)时,redo log会被首先写入log buffer;当客户端执行COMMIT命令时,log buffer中的内容会被视情况刷新到磁盘。redo log在磁盘上作为一个独立的文件存在,即Innodb的log文件。
  • undo log
    与redo log相反,undo log是为回滚而用。具体内容就是copy事务前的数据库内容(行)到undo buffer,在适合的时间把undo buffer中的内容刷新到磁盘。undo buffer与redo buffer一样,也是环形缓冲,但当缓冲满的时候,undo buffer中的内容会也会被刷新到磁盘;与redo log不同的是,磁盘上不存在单独的undo log文件,所有的undo log均存放在主ibd数据文件中(表空间),即使客户端设置了每表一个数据文件也是如此。
  • rollback segment
    回滚段这个概念来自Oracle的事物模型,在Innodb中,undo log被划分为多个段,具体某行的undo log就保存在某个段中,称为回滚段。可以认为undo log和回滚段是同一意思。
  • 锁(前边已有讲述)
  • 隔离级别(前边已有讲述)

MVCC原理详解

有事务插入persion表插入了一条新记录:name为Jerry, age为24岁。可认为:隐式ID是1,事务ID和回滚指针,我们假设为NULL

事务1对该记录的name做出修改,改为Tom

当事务1更改该行的值时,会进行如下操作:

  • 用排他锁锁定该行
  • 把该行数据拷贝到undo log中,作为旧记录(即在undo log中有当前行的拷贝副本)
  • 拷贝完毕后,有如下操作:
    修改该行name为Tom;
    修改隐藏字段的事务ID为当前事务1的ID(我们默认从1开始,之后递增);
    回滚指针指向拷贝到undo log的副本记录(即表示我的上一个版本就是它)。
  • 事务提交后,释放锁

事务2修改person表的同一个记录,将age修改为30岁

当事务2更改该行的值时,会进行如下操作:

  • 用排他锁锁定该行
  • 把该行数据拷贝到undo log中,作为旧记录。
    发现该行记录已经有undo log了,那么最新的旧数据作为链表的表头,插在该行记录的undo log最前面
  • 拷贝完毕后,有如下操作:
    修改该行age为30岁;
    修改隐藏字段的事务ID为当前事务2的ID, 那就是2
    回滚指针指向刚刚拷贝到undo log的副本记录
  • 事务提交后,释放锁

        从上面,我们就可以看出,不同事务或者相同事务的对同一记录的修改,会导致该记录的undo log成为一条记录版本线性表,即:事务链,undo log的链首就是最新的旧记录,链尾就是最早的旧记录。 

        因此,如果undo log一直不删除,则会通过当前记录的回滚指针回溯到该行创建时的初始内容,所幸的时在Innodb中存在purge线程,它会查询那些比现在最老的活动事务还早的undo log,并删除它们,从而保证undo log文件不至于无限增长。

标签:事务,log,记录,--,MVCC,undo,MySQL,ID
来源: https://blog.csdn.net/feiying0canglang/article/details/112917493

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有