关于MySQL的InnoDB的MVCC原理,很多朋友都能说个大概:

每行记录都含有两个隐藏列,分别是记录的创建时间与删除时间

每次开启事务都会产生一个全局自增ID

在RR隔离级别下

INSERT ->  记录的创建时间 = 当前事务ID,删除时间 = NULL

DELETE -> 记录的创建时间不动,删除时间 = 当前事务ID

UPDATE -> 将记录复制一次

        老记录的创建时间不动,删除时间 = 当前事务ID

        新记录的创建时间 = 当前事务ID,删除时间 = NULL

SELECT -> 返回的记录需要满足两个条件:

        创建时间 <= 当前事务ID (记录是在当前事务之前或者由当前事务创建的)

        删除时间 == NULL || 删除时间 > 当前事务ID (记录是在当前事务之后被删除的)

但实际上,这个描述是很不严格的,问题有以下几点:

1. 每条记录含有的隐藏列不是两个而是三个

它们分别是:

DB_TRX_ID, 6byte, 创建这条记录/最后一次更新这条记录的事务ID

DB_ROLL_PTR, 7byte,回滚指针,指向这条记录的上一个版本(存储于rollback segment里)

DB_ROW_ID, 6byte,隐含的自增ID,如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引

另外,每条记录的头信息(record header)里都有一个专门的bit(deleted_flag)来表示当前记录是否已经被删除

2. 记录的历史版本是放在专门的rollback segment里(undo log)

  UPDATE非主键语句的效果是

    老记录被复制到rollback segment中形成undo log,DB_TRX_ID和DB_ROLL_PTR不动

    新记录的DB_TRX_ID = 当前事务ID,DB_ROLL_PTR指向老记录形成的undo log

    这样就能通过DB_ROLL_PTR找到这条记录的历史版本。如果对同一行记录执行连续的update操作,新记录与undo log会组成一个链表,遍历这个链表可以看到这条记录的变迁)

3. MySQL的一致性读,是通过一个叫做read view的结构来实现的

read_view中维护了系统中活跃事务集合的快照,这些活跃事务ID的最小值为up_limit_id,最大值为low_limit_id(不要搞反了!!!)

附上源码注释以便于理解

trx_id_t low_limit_id; // The read should not see any transaction with trx id >= this value. In other words, this is the "high water mark".
trx_id_t up_limit_id; // The read should see all trx ids which are strictly smaller (<) than this value. In other words, this is the "low water mark".

SELECT操作返回结果的可见性是由以下规则决定的:

DB_TRX_ID < up_limit_id  -> 此记录的最后一次修改在read_view创建之前,可见

DB_TRX_ID > low_limit_id   -> 此记录的最后一次修改在read_view创建之后,不可见  ->  需要用DB_ROLL_PTR查找undo log(此记录的上一次修改),然后根据undo log的DB_TRX_ID再计算一次可见性

up_limit_id <= DB_TRX_ID <= low_limit_id -> 需要进一步检查read_view中是否含有DB_TRX_ID

    DB_TRX_ID ∉ read_view  -> 此记录的最后一次修改在read_view创建之前,可见

    DB_TRX_ID ∈ read_view -> 此记录的最后一次修改在read_view创建时尚未保存,不可见  ->  需要用DB_ROLL_PTR查找undo log(此记录的上一次修改),然后根据undo log的DB_TRX_ID再从头计算一次可见性

经过上述规则的决议,我们得到了这条记录相对read_view来说,可见的结果。

此时,如果这条记录的delete_flag为true,说明这条记录已被删除,不返回。

   如果delete_flag为false,说明此记录可以安全返回给客户端

4. 用MVCC这一种手段可以同时实现RR与RC隔离级别

它们的不同之处在于:

RR:read view是在first touch read时创建的,也就是执行事务中的第一条SELECT语句的瞬间,后续所有的SELECT都是复用这个read view,所以能保证每次读取的一致性(可重复读的语义)

RC:每次读取,都会创建一个新的read view。这样就能读取到其他事务已经COMMIT的内容。

所以对于InnoDB来说,RR虽然比RC隔离级别高,但是开销反而相对少。

补充:RU的实现就简单多了,不使用read view,也不需要管什么DB_TRX_ID和DB_ROLL_PTR,直接读取最新的record即可。

5. 二级索引与MVCC

MySQL的索引分为聚簇索引(clustered index)与二级索引(secondary index)两种。

刚才讲的内容是基于聚簇索引的,只有聚簇索引中含有DB_TRX_ID与DB_ROLL_PTR隐藏列,可以比较容易的实现MVCC

但是二级索引中并不含有这几个隐藏列,只含有1个bit的deleted flag,咋办?

  好办,如果UPDATE语句涉及到二级索引的键值,将老的二级索引的deleted flag标记为true,然后创建一条新的二级索引记录即可。

但是如果想根据二级索引来做查询,这可就麻烦了。因为二级索引不维护版本信息,无法判断二级索引中记录的可见性。

所以还是需要回到聚簇索引中来:

根据二级索引维护的主键值去聚簇索引中查找记录(使用MVCC规则)

如果查出来的结果跟二级索引里维护的结果相同 -> 返回,如果不同 -> 丢弃

如果对于一条查询语句,二级索引中有很多条满足条件的结果(连续多次更新,导致二级索引中有很多条记录),那上面这个流程就比较低效了。所以InnoDB的作者搞了个机智的小优化:

在二级索引中,用一个额外的名为MAX_TRX_ID的变量来记录最后一次更新二级索引的事务的ID

那么,如果当前语句关联的read_view的 up_limit_id > MAX_TRX_ID,说明在创建read_view时最后一次更新二级索引的事务已经结束,也就是说二级索引里的所有记录对于当前查询都是可见的,此时可以直接根据二级索引的deleted flag来确定记录是否应该被返回。

小结一下:二级索引的MVCC可见性判断在MAX_TRX_ID失效的情况下需要依赖聚簇索引才能完成。

6. purge

从前面的分析可以看出,为了实现InnoDB的MVCC机制,更新或者删除操作都只是设置一下老记录的deleted_bit,并不真正将过时的记录删除。

为了节省磁盘空间,InnoDB有专门的purge线程来清理deleted_bit为true的记录。

为了不影响MVCC的正常工作,purge线程自己也维护了一个read view(这个read view相当于系统中最老活跃事务的read view)

如果某个记录的deleted_bit为true,并且DB_TRX_ID相对于purge线程的read view可见,那么这条记录一定是可以被安全清除的。

参考文献

InnoDB多版本(MVCC)实现简要分析(水平很高,分析深入,必须要看,但可能不太好理解)

深入浅出INNODB MVCC机制与原理

MVCC原理探究及MySQL源码实现分析

关于InnoDB中mvcc和覆盖索引查询的困惑?

InnoDB Multi-Versioning

MySQL 一致性读 深入研究

MySQL InnoDB MVCC深度分析的更多相关文章

  1. MySQL InnoDB MVCC

    MySQL 原理篇 MySQL 索引机制 MySQL 体系结构及存储引擎 MySQL 语句执行过程详解 MySQL 执行计划详解 MySQL InnoDB 缓冲池 MySQL InnoDB 事务 My ...

  2. 何登成大神对Innodb加锁的分析

    背景 MySQL/InnoDB的加锁分析,一直是一个比较困难的话题.我在工作过程中,经常会有同事咨询这方面的问题.同时,微博上也经常会收到MySQL锁相关的私信,让我帮助解决一些死锁的问题.本文,准备 ...

  3. MySQL InnoDB 实现高并发原理

    MySQL 原理篇 MySQL 索引机制 MySQL 体系结构及存储引擎 MySQL 语句执行过程详解 MySQL 执行计划详解 MySQL InnoDB 缓冲池 MySQL InnoDB 事务 My ...

  4. (转)mysql、innodb和加锁分析

    mysql.innodb和加锁分析 原文:https://liuzhengyang.github.io/2016/09/25/mysqlinnodb/ 介绍 本文主要介绍MySQL和InnoDB存储引 ...

  5. 定点分析: MySQL InnoDB是如何保证系统异常断电情况下的数据可靠性?

    MySQL支持事务,所以保证数据可靠的前提是对数据的修改事务已经成功提交 这个问题可以解释为'MySQL InnoDB是如何保证事务C(一致性)D(持久性)性的?' 可能出现的两种情况: (一致性)数 ...

  6. mysql innodb锁简析(2)

    继续昨天的innodb锁的分析: 注:此博文参考一下地址,那里讲的也很详细.http://xm-king.iteye.com/blog/770721 mysql事务的隔离级别分为四种,隔离级别越高,数 ...

  7. MySQL InnoDB存储引擎中的锁机制

    1.隔离级别 Read Uncommited(RU):这种隔离级别下,事务间完全不隔离,会产生脏读,可以读取未提交的记录,实际情况下不会使用. Read Committed (RC):仅能读取到已提交 ...

  8. 腾讯云数据库团队:浅谈如何对MySQL内核进行深度优化

    作者介绍:简怀兵,腾讯云数据库团队高级工程师,负责腾讯云CDB内核及基础设施建设:先后供职于Thomson Reuters和YY等公司,PTimeDB作者,曾获一项发明专利:从事MySQL内核开发工作 ...

  9. 搞懂MySQL InnoDB事务ACID实现原理

    前言 说到数据库事务,想到的就是要么都做修改,要么都不做.或者是ACID的概念.其实事务的本质就是锁和并发和重做日志的结合体.那么,这一篇主要讲一下InnoDB中的事务到底是如何实现ACID的. 原子 ...

随机推荐

  1. Session接口常用方法

    org.hibernate.Session接口 beginTransaction 开启事务 clear 清缓存 close 关闭session connection - 过时 获取Connection ...

  2. 【bzoj2815】[ZJOI2012]灾难 拓扑排序+倍增LCA

    题目描述(转自洛谷) 阿米巴是小强的好朋友. 阿米巴和小强在草原上捉蚂蚱.小强突然想,果蚂蚱被他们捉灭绝了,那么吃蚂蚱的小鸟就会饿死,而捕食小鸟的猛禽也会跟着灭绝,从而引发一系列的生态灾难. 学过生物 ...

  3. [USACO07DEC]美食的食草动物Gourmet Grazers

    ---题面--- 题解: 首先观察题面,直觉上对于一头奶牛,肯定要给它配pi和qi符合条件的草中两者尽量低的草,以节省下好草给高要求的牛. 实际上这是对的,但观察到两者尽量低这个条件并不明确,无法用于 ...

  4. BZOJ_day4&&DSFZ_day1

    昨天坐火车才水了三道题... 25题 100810221041105110591087108811791191119212571303143218541876195119682140224224382 ...

  5. HDU 2844 二进制优化的多重背包

    Coins Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  6. fastjson对json操作

    fastjson对json字符串JSONObject和JSONArray互相转换操作示例  fastjson的方法: Fastjson API入口类是com.alibaba.fastjson.JSON ...

  7. 使用HTML实现对汉字拼音的支持

    <!DOCTYPE HTML><html> <head> <meta charset="utf-8"> <title>无 ...

  8. Java之戳中痛点 - (5)switch语句break不能忘以及default不同位置的用法

    先看一段代码: public class Test{ public static void main(String[] args){ System.)); } } public static Stri ...

  9. Struts2 内建的验证规则

    Struts2 内建的验证规则 conversion validator:转换验证器 date validator:日期验证器 double validator:浮点验证器 email validat ...

  10. 51Nod 2006 飞行员配对(二分图最大匹配)-匈牙利算法

    2006 飞行员配对(二分图最大匹配) 题目来源: 网络流24题 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题  收藏  关注 第二次世界大战时期,英国皇家空军从沦陷国 ...