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

MVCC 多版本并发控制

解决多个事务同时读写数据时的冲突问题,主要用于实现事务的两个隔离级别:

  • 读已提交 (Read Committed)

  • 可重复读 (Repeatable Read) (这是 MySQL InnoDB 默认的隔离级别)

【可以理解为主要是实现读已提交,只能读已经被事务提交后产生的版本,在实现读已提交的基础上,将read view生成时机从每一次select之后改成只是第一次select的时候生成read view,后面的select不再生成新的read view,就可以实现可重复读了】

实现原理:版本链(每一行数据有一个版本链,历史版本数据组成的链)+readview

1.版本链:

在每行数据中加了三个隐藏字段,这前两个隐藏字段可以帮助构造一个版本链

  1. DB_TRX_ID: 最近一次修改这行数据的事务ID。

  2. DB_ROLL_PTR: 回滚指针,指向这行数据上一个版本在 undo log 中的位置。

      3.DB_ROW_ID: 隐藏的行ID,当表没有主键时,InnoDB会用它来生成聚簇索引(这个不用关注)。

account表://假设99号事务创建了这条记录,undo log里面存储ROW: {id:1, name:'张三', balance:1000}    HIDDEN: {DB_TRX_ID: 99, DB_ROLL_PTR: null}  // 第一次修改,101号事务将余额改成800 ROW: {id:1, name:'张三', balance:800}  HIDDEN: {DB_TRX_ID: 101, DB_ROLL_PTR: --> 指向undo_log_v1} || (DB_ROLL_PTR)vundo_log_v1: {balance:1000, DB_TRX_ID: 99, DB_ROLL_PTR: null}   //第二次修改,102事务将余额改成500(即使事务还没提交,只要执行了update语句undo log的版本链就会有这个版本)ROW: {id:1, name:'张三', balance:500}     HIDDEN: {DB_TRX_ID: 102, DB_ROLL_PTR: --> 指向undo_log_v2}       || (DB_ROLL_PTR)v
undo_log_v2: {balance:800, DB_TRX_ID: 101, ROLL_PTR: -->undo_log_v1}   || (DB_ROLL_PTR)v                                    
undo_log_v1: {balance:1000, DB_TRX_ID: 99, DB__ROLL_PTR: null}  
2.Read view:

此时一个新的事务103查询余额,为了决定它能看到哪个版本,数据库会为它生成一个 Read View:

creator_trx_id: 103          -- 创建这个Read View的事务ID   m_ids: [102]    --创建时,数据库里所有未提交的事务ID列表  (假设事务101已提交,事务102正在运行)  min_trx_id: 102    -- m_ids列表中的最小值max_trx_id: 104     -- 创建时,数据库下一个将要分配的事务ID         

把所有事务分成了三类:

  • 已提交的“过去”: 事务ID < min_trx_id (小于102) 的,比如事务99和101。

  • 未知的“未来”: 事务ID >= max_trx_id (大于等于104) 的。

  • 正在发生的“现在”(但是还没有提交): 事务ID 在 min_trx_id 和 max_trx_id 之间,并且在 m_ids 列表里(即事务102)。

3.遍历版本链中每个版本,和read view做比对

核心就是:只能读已经提交的数据,不能读还没提交的数据,可以读的数据是已经提交的版本(<max_trx_id,而且不在 m_ids列表里面的事务id创建的数据

事务103拿着它的 Read View,去读取张三那行数据对应的版本链,然后和版本链上每个节点进行比较:

ROW: {id:1, name:'张三', balance:500}     HIDDEN: {DB_TRX_ID: 102, DB_ROLL_PTR: --> 指向undo_log_v2}       || (DB_ROLL_PTR)v
undo_log_v2: {balance:800, DB_TRX_ID: 101, ROLL_PTR: -->undo_log_v1}   || (DB_ROLL_PTR)v                                    
undo_log_v1: {balance:1000, DB_TRX_ID: 99, DB__ROLL_PTR: null}  链条上的第一个节点:这个版本是102事务创建的
(1)是我103事务创建的吗,不是
(2) 比min_trx_id小吗,不是
(3)比max_trx_id大吗,不是(4) m_ids: [102] 里面有102吗,有
结论:102在 m_ids 列表,说明这个版本是在我创建read view时还没提交,因此我不能看这个版本的数据链条上的第二个节点:这个版本是101事务创建的
“101比 min_trx_id (102) 小吗?” -> 是!
说明创建这个版本的事务已经提交了,因此可以看到这条数据
4.如何实现读已提交和可重复读

按照上面的实现,已经可以实现读已提交

将read view生成时机从每一次select之后改成只是第一次select的时候生成read view,后面的select不再生成新的read view,就可以实现可重复读了

5.既然可重复读这么容易实现,为什么还会存在读已提交这个隔离级别呢?
  • “可重复读”为了维持那个“不变的快照”,是有成本的。对于一个长事务,只要这个事务不结束,这个旧版read view就要一直保存,这样如果要读很多行的数据,这样就要保存很多read view文件(读已提交则可以随时删掉,下一次select的时候会重新生成最新的),造成undo log文件非常大,增加数据库后台清理线程 (purge thread) 的工作负担

  • “读已提交”并非“可重复读”的降级版,而是一种不同的设计选择。它放宽了对一致性的要求,以换取更高的数据新鲜度(可以看到更实时的数据)、更好的并发性能和更低的系统资源消耗。

http://www.dtcms.com/a/453285.html

相关文章:

  • 【AI智能体】Coze 打造AI数字人视频生成智能体实战详解:多模态情感计算与云边协同架构
  • 重庆网站建设培训机构学费重庆市官方网站
  • 关系建设的网站上海网站seo招聘
  • Vue router-view和router-link分开写在不同的组件中实现导航栏切换界面
  • Wan2.2-Animate V2版 - 一键替换视频角色,实现角色动作及表情同步迁移替换 支持50系显卡 ComfyUI工作流 一键整合包下载
  • Coordinate Attention for Efficient Mobile Network Design 学习笔记
  • 初识MYSQL —— 数据类型
  • 大型网站建设行情南通专业网站设计制作
  • 【AI智能体】Coze 打造AI数字人视频生成智能体实战详解:从0到1构建可交互虚拟主播
  • LabVIEW使用3D场景光照
  • 河北建设厅网站修改密码在哪wordpress 前台 很慢
  • 数字设计 综合工具 yosys 源码安装与应用简介
  • HikariCP 连接池完全指南
  • 绵竹网站建设大连装修公司
  • C++空值初始化利器:empty.h使用指南
  • 电子版康奈尔笔记写作方案对比
  • (3)SwiftUI 的状态之上:数据流与架构(MVVM in SwiftUI)
  • 郴州网站seo个人兴趣网站设计
  • wordpress企业站源码做qq群头像网站
  • Vue和React怎么选?全面比对
  • C++之再谈类与对象
  • 巫山做网站那家好企业网站的建立
  • 深度学习基础:从原理到实践——第一章感知机(中)
  • 企业网站策划怎么样揭阳模板网站建站
  • 计算机网络第四章(8)——网络层《ICMB网际控制协议》
  • 网络教育网站如何做营销推广做ppt必备网站
  • 移植到Linux,Avalonia初次尝试意外美好
  • asp网站管理系统源码免费申请163邮箱
  • 欧拉公式剖析
  • 冲刺校招 打卡 day02