当前位置: 首页 > article >正文

MYSQL MVCC多版本并发控制机制与原理、可重复读/读已提交原理

1,前置知识回顾

        1.1,当前读和快照读

        根据前面章节可知,mysql在【可重复读】(RR)的事务隔离机制下,同一个事务中的多次读取结果是一样的。而【读已提交】(RC)事务隔离机制,每次读取都会读取到已提交事务的最新数据。【读未提交】(RU)事务隔离机制,可以直接读取到未提交事务的数据。

        工作中常用的两种隔离机制【RR】(mysql默认)是如何保证同一事务中每次读取结果都一样呢?【RC】又是如何做到读取已提交事务的数据的呢?一切都源于MVCC(Multi-Version Concurrent Control多版本并发控制)的数据可见性原则实现。本章将通过实际案例讲解MVCC数据的可见性机制,分析RR和RC隔离机制是如何追溯数据的。

        先回顾两个名词,【快照读】和【当前读】

        快照读:RR机制下,第一次查询会生成一个当前时间节点的数据快照,后续该事务中的所有数据查询,都读取当前快照的数据,保证了数据的一致性。

        当前读:RR机制下,第一次查询后,当前事务T未结束前,如果有其他事务对某个数据A做了修改并提交了事务,事务T需要对数据A做update,事务T需要获取数据A的最新结果,以防止脏读和保证数据的一致性。

        即然如此,RR机制下mysql是不是对每个查询事务都生成一个快照呢?原则上是可以通过这种方式实现,但这种方式快照过多,会对数据库资源造成极大的浪费,且事务结束后需删除无用快照,I/O过多也会占用大量资源。由此引出实现快照读的另外几个名词:【undo log】(回滚日志)、【undo log版本链】、【read-view】(一致性视图)。下面根据案例解释这几个名词,并在案例中讲解快照读的实现原理。

        1.2,如何查询事务id

        案例开始前,还需再次回顾上一章内容,如何查询mysql事务id:

SELECT * FROM information_schema.INNODB_TRX;

        1.3,mysql表隐藏字段

        本章节案例还是使用前面章节使用的表t_account,表数据如下

        表中可见只有三个字段:id, name, balance. 实则mysql添加了两个隐藏字段:trx_id(事务id),roll_pointer(回滚指针)。案例中将讲解这两个字段的作用。

2,案例讲解MVCC原理

        案例的事务操作顺序很重要,顺序错误将导致不同的结果,思维如果对顺序的概念不强也很难理解,所以每步操作和顺序都将命名且排序。

        操作一:事务A开启事务,查询当前ID = 4 的数据,即原数据。并查询当前事物ID,即已提交的事务ID = 421991945340720。

        操作二:事务B开启事务,修改id=4的数据,更改balance = 5,不提交事务。查询当前事物B的事务ID = 103386,如下图:

        操作三:事务C开启事务,查询id=4的数据,不提交事务。查询事务C的事务id = 421991945341624,如下图:

        操作先暂停。根据前面的知识,此时事务C查询id=4的数据,balance = 6000.

        当前操作流程可用下图表示:

        【undo log】:用来回滚 增/删/改 操作的回滚日志。如:事务insert一条数据,则该事务的【undo log】就是delete语句;

        【trx_id】:当前操作的事务ID;

        【roll_pointer】:当前事物指向【undo log】的指针,表示当前事务如果回滚,将执行哪个【undo log】语句。如上图,事务A修改数据,则事务A的【roll_pointer】指针指向原数据,如果事务A回滚,id = 4的数据将回滚到原数据。事务B时查询事务,不用回滚,所以没有【undo log】.

        【undo log版本链】:是指一行数据被多个事务依次修改过后,在每个事务修改完后,Mysql会生成并保留undo log,并且用两个隐藏字段trx_id和roll_pointer把这些undo日志串联起来形成一个历史记录版本链,如上图的黑色箭头。

        MVCC的数据可见性是如何实现可重复读的呢?

        事务B在读取数据时,获取了当前时间节点的【read-view】,【read-view】的查找逻辑如下:

1,在当前查询的时间节点,查询数据库所有未提交的事务ID,放在数组中。当前案例数据:

        uncommitted_trx_ids[] = {103378};

2,获取当前已提交的最大事务ID。当前案例数据:max_trx_id = 421991945340720。

        事务C的可重复读,是通过当前事务中数据的可见性实现的。数据可见性实现原理:

1,数据操作执行顺序是按照上图中绿色箭头,依次往下执行,事务ID递增;        
2,事务里的任何sql查询结果,需要从当前对应版本链里的最新数据(即当前事务ID),开始逐条向上(按上图红色箭头顺序)跟【read-view】做比对,从而得到最终的快照结果。

        当前案例比对过程:

1,查询时获取当前事务C的【read-view】: uncommitted_trx_ids[] = {103378},max_trx_id = 421991945340720

2,按照上图中红色箭头依次向上获取事务ID,与当前事务的【read-view】数据作比对。向上比对一条,即比对事务B的trx_id = 103378。事务ID在当前所有未提交事务数组中,该数据不可见;

3,向上比对一条,即比对事务A的trx_id = 421991945340720。事务ID是当前最大已提交事务,数据可见,获取该事务ID对应的数据,即 balance = 6000.        

        总结: 

【可重复读】:查询时获取一次【read-view】,所有未提交事务的数据不可见,最大已提交事务数据可见。由此保证了可重复读。

【读已提交】:每次查询时都获取最新的【read-view】,所有未提交事务的数据不可见,最大已提交事务数据可见,保证了每次提交的最新数据都可见。


http://www.kler.cn/news/343497.html

相关文章:

  • vue3中自定义校验函数密码不生效问题
  • Carrier Aggregation 笔记
  • odoo16 视图(View)和界面布局(UI Layout)
  • STM32F407寄存器操作(DMA+SPI)
  • this,this指向
  • SpringBootWeb AOP
  • 胤娲科技:破茧成蝶——具身智能工业机器人引领工业新纪元
  • 尚硅谷rabbitmq 2024 第18-21节 消息可靠性答疑一
  • 压力测试指南-云环境中的压力测试实践
  • 刷题 排序算法
  • Linux与科学计算
  • 第J3周:DenseNet算法实战与解析
  • godot帧同步-关于“显示与逻辑分离”
  • 证明算法(参数估计)满足大样本性质
  • Spring Boot集成encache快速入门Demo
  • 提示词格式化
  • 卡码网C++基础课 |20. 排队取奶茶
  • xmltodict 处理 XML 数据案例解析
  • 无人机在矿业领域的应用!
  • 探秘纯前端Excel表格:构建现金流量表的完整指南