二种MVCC对比分析

前言

​    MVCC(多版本并发控制,Multi-Version Concurrency Control)是一种数据库管理系统(DBMS)中用于处理并发访问的机制。它的核心思想是通过保存数据的多个版本来实现高效的读写并发操作,避免事务之间的阻塞,从而提高系统性能, 这篇文章将从几个数据库来分析

MVCC

MVCC是为了在数据库中, 读写不阻塞的情况下, 提供一致性读的功能

​​    在并发操作中,当正在写时,如果有用户在读,这时写可能只写了一半,如一行的前半部分刚写入,后半部分还没有写入,这时读取到的数据行,可能是前半部分是新数据,后半部分是旧数据的不一致的行。解决这个问题的最简单的办法是 使用读写锁,写的时候,不允许读,正在读的时候也不允许写,但这种方法导致读和写不能并发。于是,有人就想到了一种能够让读写并发的方法,这种方法就 是MVCC, MVCC的方法是写数据时,旧的版本数据并不删除,并发的读还能读到旧的版本数据,这样就不会有问题了

​    从几个经典的数据库来看MVCC可以分为两种, 一种是基于版本链的MVCC, 比如MySQL的Innodb存储引擎, 还有Oracle数据库, 这种是将旧数据放在回滚段, 通过一条记录的回滚指针来找到上一个版本, 另一种是直接将旧版本的数据依然放在当前数据文件中, 比如PostgreSQL

Innodb的MVCC

​ ​    对于一条记录, 除开我们在创建表时规定的列, 还有几个隐藏字段, 在MVCC我们需要关注这俩个

在这里插入图片描述

  • trx_id : 创建这条记录的事务号
  • roll_pointer : 回滚指针, 指向上一个版本

版本链

通过回滚指针就可以形成版本链

在这里插入图片描述

  • 在Innodb中存在purge线程,它会查询那些比现在最老的活动事务还早的undo log,并删除它们,从而保证undo log文件不至于无限增长。

回滚与提交

  • 正常提交 : 一当事务正常提交时, innodb只需要更改事务状态为commit即可,不需做其他额外的工作
  • 回滚 : Rollback需要根据当前回滚指针从undo log中找出事务修改前的版本,并恢复。如果事务影响的行非常多,回滚则可能会很慢并且还会加行锁, 阻塞其他事务,回滚时,也会产生redo日志
  • Innodb的commit效率高,Rollback代价大

可见性判断

​ Innodb判断一条记录对于一个事务是否可见是通过读视图 ( Read View )来实现的

读视图可以看作一个数据结构, 里面有几个重点字段

  • Create_trx_id : 表示创建该读视图的事务id
  • m_ids : 表示创建该读视图时, MySQL中活跃的事务id集合
  • min_try_id : 表示创建该读视图时, MySQL中活跃的事务id最小值
  • max_try_id : 表示创建该读视图时, MySQL中全局最大事务id+1

那么对于一条记录

  • 如果修改这条记录的事务id比读视图中的min_id还小, 表示这条记录是在创建该读视图之前就修改的, 那么对于当前事务这条数据是可见
  • 如果这个id比读视图的Max_id还大, 说明是创建该读视图之后才修改的, 那么对于当前事务就是不可见的
  • 如果在两者之间, 再看读视图中的m_ids, 如果这个id存在读视图的m_ids里, 说明这条记录还没被提交, 那么对于当前事务不可见, 如果不在, 说明该记录的事务已经被提交, 那么对于当前事务来说就可见

​    在Innodb的事务隔离级别中, 读提交和可重复读的区别就是创建读视图的时机不同, 读提交是在该事务每次执行语句都会创建一个读视图, 那么在此过程中, 其他事务提交了, 那么就会影响新创建的读视图的min_try_id 和m_ids, 使得提交的数据对于当前事务可见, 而可重复读是在事务启动时创建一个读视图, 整个过程中都用这一个读视图, 在此期间, 其他事务提交了, 对于当前事务也是不可见的

Oracle的MVCC

​ Oracle也是通过回滚段来实现MVCC的, 但oracle的实现更复杂,更精细一些。

Oracle相对MySQL的Innodb有几个重点区别

  • Oracle中也有事务ID, 但不是递增的Undo Segment Number ( 回滚段 Number )+Transaction Table Slot Number ( 槽 Number ) +Wrap ( 槽重用次数), 所以不能通过事务id来判断事务的执行前后
  • 事务信息并不是记录在每个数据行上的,而是在块头中的ITL槽上,所以相对来说更省空间

在这里插入图片描述

  • ITL 槽存在数据块头部
  • 每条记录的头部会有一个字节的数据表示使用了哪一个ITL槽 ( ITL 槽最大255 个)
  • 因为Oracle数据库的事务ID不能判断事务的执行前后, 所以为了判断事务的执行前后引入了一个叫SCN的单调递增的序号

版本链

在这里插入图片描述

  • Oracle的回滚信息是存在ITL槽中的, 每条记录的头部会有一个字节的数据表示使用了哪一个ITL槽, 通过这个ITL槽里的回滚指针, 就可以找到旧版本数据
  • 在回滚段的数据会将旧的ITL槽和数据一起存, 这样就形成了版本链

PostgreSQL的MVCC

​​    PostgreSQL数据库的MVCC不同于基于版本链的MVCC, 是没有回滚段的, 旧数据是存放在原有数据文件中的

  • 为了清理旧数据有一个垃圾回收操作vacuum来做这个事。 同时还有有自动垃圾回收autovacuum
  • 更新操作中新行的物理位置发生了变化使用了HOT技术。如果原有的数据块之间有空间,旧行与新行之间会建一个链接,索引上仍然指向旧的数据行。

MVCC实现

  • 每行上有xmin和xmax两个系统字段, 当插入一行数据时,将这行上的xmin设置为当前的事务id,而xmax设置为0
  • 当更新一行时,实际上是插入新行,把旧行上的xmax设置为当前事务id,新插入行的xmin设置为当前事务id,新行的xmax设置为0
  • 当删除一行时,把当前行的xmax设置为当前事务id
  • 当读到一行时,到commitlog ( 记录事务是提交还是回滚 ) 中查询xmin和xmax对应的事务状态是否是己提交还是回滚了,就能判断出此行对当前行是否是可见。

在这里插入图片描述

可见性判断

​    类似MySQL的innodb, PostgreSQL在执行一条SQL也会生成一个快照, 这里是SnapshotData的数据结构, 里面有几个重点字段

  • xmin : 己完成的的最大事务
  • xmax : 未开始的事务
  • xip : 当前数据库中活动的事务id集合

那么对于一条数据

  • 如果数据中的xmin>SnapshotData.xmax 说明这条数据是在当前事务之后的事务插入的, 那么对当前事务不可见
  • 如果数据中的xmin在SnapshotData.xip中, 说明插入这条数据的事务还未提交, 那么对于当前事务不可见
  • 如果数据中的xmin<SnapshotData.xmin, 说明插入这条数据的事务在当前事务之前, 那么就去commit log中查看该事务的状态, 如果是回滚, 则不可见, 如果是提交, 那么接下来看xmax
    • 如果xmax==0 || xmax in SnapshotData.xip 说明这条数据是当前事务执行前的最新版, 对当前事务可见
    • 如果xmax<SnapshotData.xmin 说明可能存在更新的版本或该数据被删除, 那么就去commit log中查看该xmax的事务的状态, 如果是回滚, 那么对当前事务可见, 如果是提交, 说明有更新的版本或者被删除, 那么这条就对当前事务不可见

特点

  • 事务回滚可以立即完成,无论事务进行了多少操作, 数据可以进行很快更新
  • 旧版本数据需要清理,有autovacuum进程vacuum命令处理
<think>嗯,用户想了解MVCC的概念和工作原理。首先,我需要回顾一下自己对MVCC的理解,确保准确无误。MVCC全称是多版本并发控制,是数据库系统中用来处理并发访问的一种方法。它通过保存数据的历史版本来实现读不阻塞写、写不阻塞读,从而提高并发性能。 首先,用户可能需要知道为什么需要MVCC。传统锁机制在并发高时容易导致阻塞和死锁,而MVCC通过版本控制避免了这些情况。接着,要解释MVCC的基本原理,比如每个事务看到的数据版本可能不同,通过时间戳或事务ID来管理可见性。 然后,要考虑如何具体实现MVCC。比如,PostgreSQL使用事务ID和行版本,而MySQL的InnoDB存储引擎则是通过undo日志和Read View来实现。需要比较不同数据库的实现差异,但也要注意用户可能并不需要太深入的技术细节,而是更关注核心机制。 用户提供的引用中有提到日志和恢复机制,这可能与MVCC的可见性判断有关。例如,引用[3]提到事务日志用于恢复,这可能涉及到MVCC中如何确定哪些版本是已提交的。需要将MVCC的工作原理与事务日志、数据持久化联系起来,但要注意不要混淆概念。 另外,用户的问题可能隐含着对MVCC优缺点的兴趣。比如,高并发下的性能优势,以及可能带来的存储开销。需要平衡地介绍优缺点,让用户全面理解。 还有,用户提供的引用[2]提到高可用性和数据同步,这可能与MVCC在分布式系统中的应用有关。不过,MVCC主要解决的是单节点内的并发控制,但也可以扩展到分布式场景,这里可能需要简要提及。 在组织回答时,应该先定义MVCC,然后说明其解决的问题,再详细工作原理,接着实现机制,最后优缺点和应用场景。这样结构清晰,符合用户要求的逐步解决问题。同时,要注意引用提供的资料,比如事务日志的作用,可能需要在适当的地方引用[^3]。 需要检查是否有遗漏的关键点,例如快照隔离级别、版本链的管理、垃圾回收机制等。这些是MVCC的重要组成部分,必须涵盖。同时,要确保使用正确的中文术语,避免直译英文术语导致理解困难。 最后,生成的相关问题要围绕MVCC的扩展知识,比如与锁机制的比较、具体数据库的实现差异、优化方法等,这样用户可以有进一步学习的路径。</think>### MVCC(多版本并发控制)详解 #### 一、基本概念 MVCC(Multi-Version Concurrency Control)是数据库系统实现并发控制的核心技术,其核心思想是**为每个事务生成独立的数据版本快照**,使得读操作不会阻塞写操作,写操作也不会阻塞读操作[^3]。 #### 二、核心原理 1. **版本链存储** - 每个数据行维护多个历史版本,形成版本链 - 新增数据时生成新版本,旧版本保留 ```sql UPDATE table SET value=5 WHERE id=1; -- 生成新版本,旧版本保留 ``` 2. **可见性判断** - 通过事务ID(XID)和版本号控制可见性 - 关键参数: - `txid_current()`:当前事务ID - `xmin`:创建该版本的事务ID - `xmax`:删除/更新该版本的事务ID $$可见性条件:xmin < current\_txid < xmax$$ 3. **快照隔离** - 事务启动时获取数据库快照 - 通过`ReadView`(MySQL)或快照隔离级别(PostgreSQL)实现 #### 三、实现机制对比 | 数据库 | 实现方式 | |-----------|------------------------------------------------------------------------| | PostgreSQL | 使用事务ID和行版本号,通过Heap Only Tuple(HOT)优化更新操作 | | MySQL | 基于undo日志构建版本链,通过ReadView判断可见性 | | Oracle | 使用SCN(System Change Number)管理版本 | #### 四、工作流程示例 1. 事务A(TX100)读取id=1的数据 2. 事务B(TX101)更新id=1的数据 3. 事务A继续读取时仍看到旧版本 ```text Version Chain: TX100 → [value=3] TX101 → [value=5] ``` #### 五、优势与局限 **优势**: - 读写操作完全无锁 - 避免死锁问题 - 支持高并发场景 **局限**: - 需要维护版本链导致存储开销增加 - 需要定期清理过期版本(VACUUM) - 可能产生幻读问题(取决于隔离级别) #### 六、应用场景 1. 高并发OLTP系统 2. 长时间运行的查询与实时更新并存场景 3. 需要保证读一致性的大数据分析
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值