ICode9

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

MySql-MVCC

2021-09-09 18:00:00  阅读:174  来源: 互联网

标签:事务 20 Read MVCC trx MySql id View


MySql-MVCC

MVCC介绍

MySql在可重复度与读已提交事务隔离级别下实现了MVCC机制。

undo日志版本链和Read View

undo日志

undo日志就是回滚日志,当修改一行数据时,undo日志会记录该行数据原始数据,当业务失败时,就根据undo日志的数据进行回滚事务。

undo日志版本链

对每一行数据进行多次修改时,undo日志会记录每一次操作执行后的数据,并将它们串联起来,不管这些操作是在同一个事务还是跨事务。

MySql会为每次对数据记录的修改操作添加两个隐藏字段:

  • trx_id(事务ID):当前操作发生所在的事务
  • roll_pointer(回滚指针):回滚到上一个操作的连接点

MySql使用roll_pointer将对数据修改的每个操作连接起来保存在数据库中。

一致性视图Read-View

当事务开启时,执行任何查询InnoDB表的语句都会生成当前事务的一致性视图Read-View,Read-View由两部分组成:

  • 执行查询时所有未提交的事务id数组(最小事务id:min_trxId)
  • 已创建的最大事务id(max_trxId)

事务中的任何查询sql都需要从对应日志版本链中最新数据开始,和Read View逐条对比,得到最终的快照结果

在RR和RC不同隔离级别中Read View的生成有不同表现:

  • 在RR中,开启事务后,执行第一次select查询InnoDB的表时生成Read View视图,Read View视图在该事务结束之前都不会变化,除非在事务中对该数据进行了修改,之后的查询会重新生成Read View视图;
  • 在RC中,每次执行查询语句时都会重新生成Read View视图;

Read View在每个事务中保存一份。

事务id

MySql在start transaction时并不会直接生成事务id,而是在执行第一条修改InnoDB表的语句时事务才真正启动,去申请事务id,MySql严格按照这个真正启动顺序分配事务id(依次递增)。

事务划分

MySql根据Read View中所有未提交的事务id数组(最小事务id:min_trxId)和最大事务id(max_trxId)把所有事务划分成三部分:

  • 已提交事务:事务id < min_trxId

  • 已提交和未提交事务:min_trxId <= 事务id <= max_trxId

    也就是说这里有未提交的事务,也包含已提交的事务。

  • 未开始事务:事务id > max_trxId

版本链对比规则

  • row的trx_id < min_trxId,这个版本的数据是由已提交的事务生成,数据是可见的;
  • row的trx_id > max_trxId,这个版本的数据由将来启动的事务生成的,数据不可见;
  • row的min_trxId <= trx_id <= max_trxId,有两种情况:
    • trx_id在Read View视图数组中,这个版本的数据是由还未提交的事务生成的,不可见,如果是在当前自己的事务,是可见的;
    • trx_id不在Read View视图数组中,这个版本的数据是已经提交的事务生成的,数据是可见的;

删除情况
delete可以看作是update的特殊情况,delete时步骤操作如下:

  1. 将版本链上最新版本的数据复制一份,然后将trx_id修改为删除操作的trx_id;
  2. 该条delete操作记录的头信息(Record Header)中的delete_flag标识位改为true,表示当前记录已经被删除;
  3. 查询时按照上面的版本链对比规则查到的记录如果delete_flag为true,则意味着记录已经被删除,不返回数据;

MVCC机制实现

MVCC就是通过一致性视图Read View和undo日志版本链进行对比,使不同事务读取数据时,读取该数据在undo日志版本链上的不同版本数据,来实现的。

MVCC示例

请看下面这个例子:
user表有三个字段,id(主键)、name和age,表数据如下:

id	name	age
1	小明	19
2	小张	19
3	小王	19
12	小红	19
35	小爱	19

接下来在不同事务中执行update和select语句,Sql执行时序图如下:
Sql执行时序图

这些update操作都会被记录在undo日志中,形式如下:
undo日志版本链

select查询语句分析:

  1. 事务30中第7步的查询sql,已提交事务id=10,未提交事务id=20,最大的事务id=20,这时生成的Read View为:未提交的事务数组[20], 最大事务id=20,min_trxId = 20,max_trxId = 20:

    根据版本链对比规则当前row的trx_id = 10,而trx_id = 10 < 20,trx_id < min_trxId,事务trx_id=10的事务已经提交,数据可见,所以步骤7的查询语句的结果age=30;

  2. 事务30步骤9的查询,因为步骤7已经生成了Read View,所以步骤9的Read View也是:未提交的事务数组[20], 最大事务id=20:

    根据规则对比当前row的trx_id=20,20在Read View数组中,这个版本的数据是未提交的,数据是不可见的;再往下找,下一个row的trx_id = 10,而10 < min_trxId = 20,所以trx_id = 10的事务已经提交,数据可见,所以步骤9的查询结果age=30;

  3. 临时事务中步骤12的查询语句,已提交的事务有trx_id = 10和trx_id = 20,当前没有未提交的事务,最大事务id=20,这时的Read View为:未提交的事务数组[],最大事务id=20,max_trxId = 20:

    当前row的trx_id=20,根据版本链对比规则,虽然trx_id=20 <= max_trxId,但trx_id=20不在视图数组中,当前row的trx_id=20的事务已经提交了,数据是可见的,所以步骤12的查询结果age=40;

  4. 再回来查看事务30中步骤14的查询语句,因为事务30中执行了步骤13,对同一条数据进行了修改(id都是1),在修改的时候会在当时重新生成Read View,这时候已提交的事务有trx_id = 10和trx_id = 20,未提交的事务trx_id=30,最大事务id=30,所以Read View为:未提交的事务数组[30],最大事务id=30,min_trxId = 30, max_trxId = 30:

    当前row的trx_id = 30,根据版本链对比规则,trx_id <= max_trxId,并且trx_id=30在未提交的事务数组中,所以当前row的trx_id=30的事务未提交,数据不可见;再往下找,下一个row的trx_id=20,而trx_id=20 < min_trxId=30,所以当前row的trx_id=20的事务已经提交,数据是可见的,所以步骤14的查询结果age=40;

标签:事务,20,Read,MVCC,trx,MySql,id,View
来源: https://blog.csdn.net/cui1315570800/article/details/120195480

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

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

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

ICode9版权所有