MVCC到底是啥

MVCC是一种提升数据库并发性能的技术,通过维护多个数据版本解决并发事务中的问题。文章详细解释了MVCC的工作原理、事务隔离级别、InnoDB中的实现细节,以及Undo-log和read-view在事务管理和并发控制中的作用。
摘要由CSDN通过智能技术生成

什么是MVCC

MVCC机制的全称为Multi-Version Concurrency Control--即多版本并发控制。

MVCC主要是为了提升数据库并发性能而设计的,用于解决并发事务访问数据库时可能出现的一些问题,如脏读、不可重复读,其中采用更好的方式处理了读-写并发冲突,做到即使有读写冲突时,可以实现并发执行,从而提升并发能力,确保了任何时刻的读操作都是非阻塞的。(仅InnoDB有)

在MVCC机制中,数据库中的每个数据行都可以存在多个版本,并且每个事务看到的数据版本可能不同。具体来说,MVCC机制通过以下方式实现并发控制

  1. 版本控制:每当对数据库中的数据行进行更新操作时,不是直接覆盖原始数据,而是创建一个新的数据版本,并将新版本的数据与事务的时间戳相关联
  2. 快照读取:在MVCC中读取操作不会阻塞写入操作,也不会阻塞其他读取操作,事务可以读取数据库中的数据快照,即某个时间点之前的数据版本,而不会受到其他事务的影响。
  3. 可见性判断:在执行读取操作时,事务只能看到在其开始之前已经提交的数据版本,而看不到其他食物正在修改的数据,这样可以避免脏读和不可重复读。
  4. 回滚操作:当事务回滚时,不会对数据库中的数据进行物理删除或修改,而是通过标记事务所涉及的数据版本为无效,使得其他事务无法看到该版本。

总得来说,MVCC机制通过维护多个数据版本,实现了事务的隔离性和并发性,保证了数据库的一致性和可靠性。

事务隔离级别

什么是事务个隔离级别?事务隔离级别主要定义了事务在并发执行时的行为,特别是它们如何与其他事务交互以及它们如何看到数据库中的更改。

ANSI/ISO SQL标准定义了4中事务隔离级别:未提交读(read uncommitted),提交读(read committed),重复读(repeatable read),串行读(serializable)

  1. READ UNCOMMITTED(读取未提交)

    • 允许读取尚未提交的数据变更。

    • 这是最低的隔离级别,它可能导致脏读、不可重复读和幻读。

    • 在这个级别,一个事务可以读取到另一个尚未提交事务的修改,这可能导致数据的不一致性。

  2. READ COMMITTED(读取已提交)

    • 只允许读取并发事务已经提交的数据。

    • 这个级别可以防止脏读,但仍可能导致不可重复读和幻读。

    • 在这个级别,每个事务只能看到它开始时的数据状态以及它提交时其他事务所做的提交。

  3. REPEATABLE READ(可重复读取)

    • 这是MySQL的默认隔离级别

    • 它确保在同一事务中多次读取同一数据时,看到的是相同的数据版本,即使其他事务在此期间修改了这些数据。

    • 尽管可以避免脏读和不可重复读,但在这个级别下仍可能出现幻读(即在一个事务中,两次相同的查询可能会返回不同的结果集,因为其他事务在此期间插入了新的记录)。

  4. SERIALIZABLE(可串行化)

    选择适当的事务隔离级别需要根据应用的需求和性能考虑进行权衡。在某些情况下,可能需要更高的隔离级别来确保数据的一致性,而在其他情况下,可能需要降低隔离级别以提高性能。同时,也需要注意不同隔离级别可能带来的并发问题,如脏读、不可重复读和幻读等。这是最高的隔离级别。它通过强制事务串行执行来避免脏读、不可重复读和幻读。在这个级别,每个事务在执行时都会完全锁定它所访问的数据,从而确保数据的一致性。但这也可能导致性能下降,因为并发事务必须等待其他事务完成才能执行。

只有RC/RR 适用MVCC

1:RU读未提交级别,不适用MVCC。

既然都允许存在脏读问题、允许一个事务读取另一个事务未提交的数据,直接进行当前读,那自然可以直接读最新版本的数据,因此无需MVCC介入。

2:Serializable串行化级别不存在事务并发,不适用MVCC。

如果是Serializable串行化级别,因为会将所有的并发事务串行化处理,

Serializable串行化级别,不论事务是读操作,亦或是写操作,都会被排好队一个个执行,这都不存在所谓的多线程并发问题了,自然也无需MVCC介入。

MVCC的实现

需要隐藏字段、undo log(回滚日志)、read view这三个配合

隐藏字段

在Innodb存储引擎中,每一行记录中都有隐藏字段

  • 在有聚簇索引的情况下每一行记录中都会隐藏3个字段,

  • 如果没有聚簇索引的情况下每一行记录中都会隐藏4个字段。

在有聚簇索引的情况下每一行记录中都会隐藏3个字段为DB_TRX_ID,DB_ROLL_PTR、deleted_bit,

  • DB_TRX_ID:记录创建这条数据上次修改它的事务 ID,

  • DB_ROLL_PTR:回滚指针,指向这条记录的上一个版本

  • deleted_bit字段,即记录被更新或删除,这里的删除并不代表真的删除,而是将这条记录的delete flag改为true

除了上面的3个隐藏字段,没有聚簇索引还会有DB_ROW_ID这个字段。

undo log(回滚日志)

在事务的ACID特性中,undo log(回滚日志)主要用于实现事务的原子性、隔离性、一致性的关键组件之一。它的主要作用包括:

  1. 事务的回滚操作

    当一个事务执行过程中发生错误或者被用户显式回滚时,数据库系统需要能够撤销该事务已经执行的操作,将数据库恢复到事务开始之前的状态。这就是回滚操作。

    undo log记录了事务执行过程中所做的所有修改操作的逆操作,通过undo log可以快速回滚事务所做的修改,从而保证事务的原子性。

  2. 恢复和崩溃恢复

    当数据库系统发生崩溃或者异常关闭时,可能会导致部分事务未提交的修改操作丢失或者部分已提交的修改操作未持久化到磁盘。

    通过undo log,数据库系统可以在恢复过程中, 将未提交的修改操作回滚,并将已提交但未持久化的修改操作重新应用到数据库中,从而保证数据库的一致性和完整性。

总的来说,undolog在数据库系统中扮演着非常重要的角色,它不仅用于实现事务的回滚操作和并发控制,还用于数据库系统的恢复和崩溃恢复。通过记录事务的修改操作和逆操作,undo log确保了数据库的原子性、隔离性和一致性,是数据库系统的关键组件之一。

 read-view(数据快照)

undo log保存的是一个版本链,也就是使用DB_ROLL_PTR这个字段来连接的。多个事务的 undo-log 日志副本 (数据快照),组成了一个副本链。

12345事务提交时间不一致,相对于4来说1是可见的、2,3是不可见的

意思是这样的,但是MVCC不是通过时间来比较的。

MVCC 使用 一个新的组件,read-view + 一组对比规则,来计算可见版本。

read-view 有一些列的对比规则,这些规则用于确定一个事务在读取数据时,如何与数据库中的其他事务的版本号(这里其实就是事务ID)进行比较,以确定它所能看到的数据版本。

当 执行一个select语句时MVCC 会产生一致性视图read view。那么这个read view 没有记录事务的开始时间,和截止时间 , 而是换成另一种方式去记录开始时间和截止时间,换成什么方式呢:

  • read view 记录当前活跃事务 id,组成活跃事务id数组 ,这个属性的作用,哪些事务是当前事务,也是不可见的

  • read view 记录当前最小活跃事务 id,这个属性的作用,用于判断哪些事务是已经提交了的

  • read view 记录当前的下一个事务 id,这个属性的作用,用于判断哪些事务是未来事务,也是不可见的

row_id

隐藏主键,单调递增的行ID,不是必需的,占用6个字节。

deleted_bit

删除标识,占用1个字节。

trx_id

最近的更新事务Id,记录操作该行数据事务的事务ID,占用6个字节。

roll_pointer

回滚指针,指向当前记录行的Undo-log日志中的旧版本数据,占用7个字节。

比较过程:

  1. 首先比较DB_TRX_ID<up_limit_id,如果小于,则当前事务能看到DB_TRX_ID所在记录,大于的话进入下面判断
  2. 判断DB_TRX_ID>=low_limit_id,如果>=,则代表DB_TRX_ID所在记录的read view是生成后才出现的--不可见,如果小于进入下面判断
  3. 判断DB_TRX_ID是否在活跃事务中,如果在,则代表read view生成时刻这个事务还在活跃状态,还没commit修改的数据,当前事务也是看不到的,如果不在则说明这个事务在read view生成前就commit了,修改的数据是可见的
InnoDB引擎的Undo-log日志

Undo-log可以理解成回滚日志,它存储的是老版本数据。

在表记录修改之前,会先把原始数据拷贝到Undo-log里,如果事务回滚,即可以通过Undo-log来还原数据。

或者如果当前记录行不可见,可以顺着Undo-log链找到满足其可见性条件的记录行版本。

在insert/update/delete(本质也是做更新,只是更新一个特殊的删除位字段)操作时,都会产生Undo-log。

在InnoDB里,Undo-log分为如下两类:

  1. insert Undo-log : 事务对insert新记录时产生的Undo-log, 只在事务回滚时需要, 并且在事务提交后就可以立即丢弃。

  2. update Undo-log : 事务对记录进行delete和update操作时产生的Undo-log,不仅在事务回滚时需要,快照读也需要,只有当数据库所使用的快照中不涉及该日志记录,对应的回滚日志才会被删除。

Undo-log有什么用途呢?

1.事务回滚时,保证原子性和一致性。 2.如果当前记录行不可见,可以顺着undo log链找到满足其可见性条件的记录行版本(用于MVCC快照读)。

 ReadView的生成规则

在RC隔离级别时,每一次快照读都会生成一个新的readview

在RR隔离级别时,只有在第一次进行快照读时才会生成readview,之后读的都是之前的readview,所以别的事务修改东西了当前事务看不到,实现可重复读。

两个隔离级别区别点就是readview生成时机不同

  • 16
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值