【搞定面试之mysql】第二篇:事务和MVCC

前言:

mysql在我们开发当中可谓是非常非常重要呀,特别是在面试当中这可是一个重点,今天我们一起来学习mysql的事务和MVCC。

事务

MySQL 的事务(Transaction)是数据库管理系统中非常重要的一个概念。简单来说,事务是一组原子性的 SQL 查询,要么全部执行成功,要么全部不执行。在 MySQL 中,主要使用 InnoDB 存储引擎来支持事务

事务的四大特性

  • 原子性

    事务被视为不可分割的最小单元。要么全部执行成功,要么全部失败回滚。如果执行到一半系统崩溃,数据库会通过 undo log(回滚日志)将数据恢复到事务开始前的状态。

  • 一致性

    事务执行前后,数据库的完整性约束没有被破坏。例如:转账业务,A 扣钱 100 元,B 必须增加 100 元,总金额不变。如果原子性、隔离性被破坏,一致性就无法保证。

  • 隔离性

    多个事务并发执行时,一个事务的执行不应被其他事务干扰。数据库通过锁机制和 MVCC(多版本并发控制)来实现隔离。

  • 持久性

    一旦事务被提交,它对数据库的修改就是永久性的。即使系统随后发生崩溃,通过 redo log(重做日志)也能恢复已提交的数据。

例如,A向B转账500元,这个操作要么都成功,要么都失败,体现了原子性。转账过程中数据要保持一致,A扣除了500元,B必须增加500元。隔离性体现在A向B转账时,不受其他事务干扰。持久性体现在事务提交后,数据要被持久化存储。

并发事务可能引发的问题

  • 脏读:读到其他事务未提交的数据。

  • 不可重复读:同一事务内两次读取同一条记录,数据内容不一致(因为被其他事务修改并提交了)。

  • 幻读:同一事务内两次查询数据,结果第一次查不到,第二次居然查到了(因为其他事务插入了新数据)。

隔离级别

由于并发产生了问题,所以为了解决问题,mysql就有了事务隔离来解决问题。SQL 标准定义了四种隔离级别,MySQL InnoDB 都支持。隔离级别越高,并发性能越低,数据一致性越高。

  1. 未提交读(READ UNCOMMITTED):解决不了所有问题。

  2. 读已提交(READ COMMITTED):能解决脏读,但不能解决不可重复读和幻读。

  3. 可重复读(REPEATABLE READ):能解决脏读和不可重复读,但不能解决幻读,这也是MySQL的默认隔离级别。

  4. 串行化(SERIALIZABLE):可以解决所有问题,但性能较低。

在考虑了性能和实际问题最终mysql的默认隔离级别采用了可重复读(REPEATABLE READ)

事务的实现原理

Redo log

Undo log

接下来就是我们的重点MVCC

MVCC

MVCC(多版本并发控制)是 MySQL InnoDB 存储引擎实现高并发、非阻塞读的关键技术。它通过为每行数据保存多个历史版本,让读操作可以读取"快照"数据,而写操作继续更新最新数据,从而避免了传统锁机制下读写互斥的问题,大幅提升并发性能。保证了事务的隔离性。

InnoDB 中 MVCC 的核心实现

在mysql中它的主要实现是

隐藏列

每行记录除了用户定义的列,InnoDB 还会自动添加三个隐藏字段:

  • DB_TRX_ID:最近修改该行的事务 ID。每次对行进行 INSERT、UPDATE、DELETE 时,都会将该事务的 ID 记录在此。

  • DB_ROLL_PTR:回滚指针,指向该行在 Undo Log 中的旧版本记录。通过它可以找到该行的历史版本链。

  • DB_ROW_ID:隐藏主键。如果表没有定义主键,InnoDB 会用这个隐藏列来构造聚簇索引。

Undo Log

Undo Log 中保存了数据被修改前的旧值。当一行数据被修改时,InnoDB 会先将旧值写入 Undo Log,然后更新当前行,并将 DB_ROLL_PTR 指向 Undo Log 中的旧版本记录。

这样,多个事务修改同一行时,就会形成一条版本链

当前最新版本→ 旧版本1(通过 DB_ROLL_PTR 指向 Undo 记录)→ 旧版本2 → ......

版本链的头节点就是当前记录,每个节点都包含对应的 DB_TRX_ID(创建该版本的事务 ID)和回滚指针。

Read View

Read View 是 MVCC 中判断哪个版本对当前事务可见的核心数据结构。当事务执行快照读(普通 SELECT)时,会生成一个 Read View,记录当前系统中活跃的事务 ID 列表。

Read View 主要包含以下信息:

  • m_ids:生成 Read View 时,系统中所有活跃(未提交)的事务 ID 列表。

  • min_trx_id:m_ids 中的最小值。

  • max_trx_id:系统下一个要分配的事务 ID(即生成 Read View 时已经分配的最大事务 ID + 1)。

  • creator_trx_id:创建该 Read View 的事务 ID。

说白了快照读就是有啥读啥

最后根据下面这个规则,就可以判断到底访问的是哪个版本的事务

RC隔离级别

在这个隔离级别下,事务中每读一次就会生成一个新的,比如事务五有两个读操作所以就有两个读试图。然后根据比对规则,比如,从当前DB_TRX_ID开始一一比对,4不满足,下一个就是3,谁满足任意一条数据,说明读到了这个版本的事务。

RR隔离级别

只会在第一次读的时候生成然后一直用这个图

总结

这篇文章介绍了mysql的事务和实现原理,以及mvcc及其实现原理,如果这篇文章对你有帮助请给个点赞或者关注支持一下

相关推荐
zhglhy20 小时前
Java分库分表技术对比分析
java·分库分表
wuyikeer21 小时前
Spring Framework 中文官方文档
java·后端·spring
豆豆的java之旅21 小时前
软考中级软件设计师 数据结构详细知识点(含真题+练习题,可直接复习)
java·开发语言·数据结构
Victor35621 小时前
MongoDB(61)如何避免大文档带来的性能问题?
后端
Victor35621 小时前
MongoDB(62)如何避免锁定问题?
后端
无心水21 小时前
Java时间处理封神篇:java.time全解析
java·开发语言·python·架构·localdate·java.time·java时间处理
wuyikeer21 小时前
Spring BOOT 启动参数
java·spring boot·后端
多看书少吃饭21 小时前
Vue + Java + Python 打造企业级 AI 知识库与任务分发系统(RAG架构全解析)
java·vue.js·笔记
studyForMokey1 天前
【Android面试】Activity生命周期专题
android·面试·职场和发展
博傅1 天前
Kubernetes (K8s) 入门到实战教程
java