MySQL-事务(下)-MySQL事务隔离级别与MVCC

目录

一、事务隔离级别

(一)不同隔离级别与问题的关系

(二)MySQL默认隔离级别

二、MVCC:多版本并发控制的奥秘

(一)MVCC的前提知识

[1. 3个记录隐藏字段](#1. 3个记录隐藏字段)

[2. undo日志](#2. undo日志)

[3. Read View](#3. Read View)

(二)MVCC的工作流程

[(三)Read View与可见性判断](#(三)Read View与可见性判断)

三、RR与RC的本质区别

四、总结


在数据库的世界里,事务是保证数据一致性的核心机制,而事务隔离级别和多版本并发控制(MVCC)则是事务处理中极为关键的技术点。今天,我们就来深入探究MySQL中的事务隔离级别以及MVCC的工作原理。

一、事务隔离级别

事务隔离级别定义了多个事务并发执行时,一个事务能看到其他事务操作的程度。MySQL有四种隔离级别,从低到高分别是读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和可串行化(Serializable)。

(一)不同隔离级别与问题的关系

隔离级别 脏读 不可重复读 幻读 加锁读

**- 脏读:一个事务读取到另一个事务未提交的修改数据。

  • 不可重复读:一个事务内多次读取同一数据,结果因其他事务修改而不同(重点是修改和删除操作导致)。
  • 幻读:一个事务内多次查询,结果集因其他事务新增记录而不同(重点是新增记录导致)** 。

(二)MySQL默认隔离级别

MySQL默认的隔离级别是可重复读(Repeatable Read)。这是一个比较均衡的选择,它能避免脏读、不可重复读和幻读问题,同时相比可串行化隔离级别,并发性能更好。一般情况下,不建议修改这个默认设置,因为它在大多数场景下能很好地平衡数据安全性和数据库并发性能。

二、MVCC:多版本并发控制的奥秘

多版本并发控制(MVCC)是MySQL在可重复读等隔离级别下,**实现高效并发读操作的关键技术。**它通过维护数据的多个版本,使得读操作不需要加锁(或减少锁的使用),从而提高数据库的并发性能。

(一)MVCC的前提知识

要理解MVCC,需要先了解以下三个关键概念:

1. 3个记录隐藏字段

  • DB_TRX_ID:6字节,记录最近修改(修改/插入)该记录的事务ID。

  • DB_ROLL_PTR:7字节,回滚指针,指向该记录的上一个版本(这些版本数据一般存储在undo log中)。

  • DB_ROW_ID:6字节,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引。

此外,还有一个删除flag隐藏字段,记录被更新或删除时,并非真正删除,而是删除flag发生变化。

2. undo日志

undo log可以简单理解为MySQL中的一段内存缓冲区,用来保存旧数据。当事务需要回滚或者MVCC需要获取历史版本数据时,就会用到undo log中的内容。

3. Read View

Read View是事务进行快照读操作时产生的读视图。在事务执行快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(每个事务开启时,都会被分配一个递增的ID)。Read View本质是用来进行可见性判断的,即判断当前事务能够看到哪个版本的数据(可能是当前最新数据,也可能是undo log里的某个历史版本数据)。

(二)MVCC的工作流程

以一个简单的例子来模拟MVCC的工作过程:

假设我们有一个 student 表,结构如下:

sql 复制代码
CREATE TABLE IF NOT EXISTS student(
    name VARCHAR(11) NOT NULL,
    age INT NOT NULL
);

插入一条记录:

sql 复制代码
INSERT INTO student (name, age) VALUES ('张三', 28);

此时,该记录的隐藏字段情况大致为:DB_TRX_ID 为 null (表示创建该记录的事务ID,这里假设为初始状态), DB_ROW_ID 为 1 , DB_ROLL_PTR 为 null (因为没有历史版本)。

步骤 事务 10 操作流程 事务 11 操作流程
1 开始执行,准备修改 student 表中 name 为 "张三" 的记录 -
2 给目标记录加锁 -
3 将该记录拷贝到 undo log 中(写时拷贝) -
4 修改原始记录的 name 为 "李四" -
5 修改原始记录隐藏字段 DB_TRX_ID 为事务 10 的 ID -
6 修改原始记录回滚指针 DB_ROLL_PTR,指向 undo log 中副本数据的地址 -
7 事务 10 提交,释放锁 -
8 - 开始执行,准备修改 student 表中 name 为 "李四" 的记录
9 - 给目标记录加锁
10 - 将该记录(当前为 "李四" 的记录)拷贝到 undo log
11 - 修改原始记录的 age 为 38
12 - 修改原始记录隐藏字段 DB_TRX_ID 为事务 11 的 ID
13 - 修改原始记录回滚指针 DB_ROLL_PTR,指向 undo log 中刚拷贝的副本数据地址
14 - 事务 11 提交,释放锁

现在,我们就有了一个基于链表记录的历史版本链,所谓的回滚,就是用历史数据覆盖当前数据。

(三)Read View与可见性判断

当事务进行快照读时,会生成Read View,然后根据Read View中的信息(如当前活跃事务ID集合、高水位、低水位等)来判断数据版本的可见性。

例如:

Read View中有 m_ids (Read View生成时刻系统正活跃的事务ID列表)、

up_limit_id ( m_ids 列表中事务ID最小的值)、

low_limit_id (Read View生成时刻系统尚未分配的下一个事务ID,即目前已出现过的事务ID的最大值+1)、

creator_trx_id (创建该Read View的事务ID)等信息。

在实际读取数据版本链时,会获取到每个版本对应的事务ID(即记录的 DB_TRX_ID ),然后根据Read View中的这些信息来判断该版本是否对当前事务可见。如果当前版本不可见,就通过回滚指针遍历下一个版本,直到找到可见的版本或者遍历完版本链。

三、RR与RC的本质区别

可重复读(RR)和读已提交(RC)隔离级别最本质的区别在于Read View生成时机的不同:

**- 在RC隔离级别下,事务中每次快照读都会生成并获取最新的Read View。这就导致在RC级别下,事务内的多次快照读可能会看到不同的结果,因此RC级别存在不可重复读问题。

  • 在RR隔离级别下,同一个事务中的第一次快照读会创建一个Read View,之后该事务后续的快照读都使用同一个Read View。所以,只要当前事务在其他事务提交更新之前使用过快照读,那么之后的快照读使用的都是同一个Read View,对之后的修改不可见,从而避免了不可重复读问题。**

四、总结

事务隔离级别和MVCC是MySQL事务处理中非常重要的内容。事务隔离级别决定了事务间的隔离程度,平衡了数据安全性和并发性能;而MVCC则是在特定隔离级别下,实现高效并发读操作的核心技术。理解它们的工作原理,对于我们设计和优化数据库应用,以及排查数据库并发问题都有着至关重要的作用。

相关推荐
IvorySQL2 天前
PostgreSQL 全表 count 优化实践:从 SeqScan 痛点分析到 heapam 改进与性能突破
数据库·postgresql·oracle·deepseek·ivorysql
Java水解3 天前
MySQL 新增字段但 Java 实体未更新:潜在问题与解决方案
后端·mysql
ypf52083 天前
Docker 中安装 PostgreSQL 16
数据库
handsome123453 天前
CentOS 8.5.2.111部署Zabbix6.0 手把手、保姆级
运维·mysql·centos·zabbix·监控·centos8·zabbix6·linux源
叁沐3 天前
MySQL 32 为什么还有kill不掉的语句?
mysql
字节跳动数据平台3 天前
火山引擎多模态数据湖:基于 Daft 与 Lance,构筑 AI 时代数据湖新范式
数据结构·数据库·数据挖掘
倔强的石头_4 天前
零门槛体验国产数据库硬核实力 —— 金仓 KingbaseES 在线体验平台全解析
数据库
我不是混子4 天前
什么是MySQL的回表?
后端·mysql
我不是混子4 天前
为什么不建议使用SELECT * ?
后端·mysql
AAA修煤气灶刘哥4 天前
数据库优化自救指南:从SQL祖传代码到分库分表的骚操作
数据库·后端·mysql