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

MYSQL三大日志、隔离级别(MVCC+锁机制实现)

MySQL三大日志

​Undo Log(回滚日志)

作用

事务回滚时恢复数据到修改前的状态。
支持 ​​MVCC​​,为读操作提供历史版本数据。

存储

存放在 undo tablespace 中,通过回滚段管理。

格式

undo log 格式都有一个 roll_pointer 指针和一个 trx_id 事务id:
通过 trx_id 可以知道该记录是被哪个事务修改的;
通过 roll_pointer 指针可以将这些 undo log 串成一个链表,这个链表就被称为版本链;
在这里插入图片描述

​​Redo Log(重做日志)

作用

保证事务的​​持久性​​,记录物理页的修改(物理日志,比如对 XXX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新,每当执行一个事务就会产生这样的一条或者多条物理日志),用于崩溃恢复。

写入时机​​

事务执行中按顺序循环写入,通过 innodb_flush_log_at_trx_commit 控制刷盘策略(0/1/2)。

组成

内存中的 redo log buffer 和磁盘上的 redo log file。

undo log 和 redo log 区别

undo log 记录了此次事务「开始前」的数据状态,记录的是更新之前的值;
redo log 记录了此次事务「完成后」的数据状态,记录的是更新之后的值;
事务提交之前发生了崩溃,重启后会通过 undo log 回滚事务,事务提交之后发生了崩溃,重启后会通过 redo log 恢复事务
在这里插入图片描述

​​​​Binlog(二进制日志)

MySQL 在完成一条更新操作后,Server 层还会生成一条 binlog,等之后事务提交的时候,会将该事物执行过程中产生的所有 binlog 统一写 入 binlog 文件。

作用

记录所有对数据库的​​修改操作​​(逻辑日志),用于主从复制、数据恢复(如通过 mysqlbinlog 工具恢复数据)。

写入时机​​

事务提交后顺序写入,通过参数 sync_binlog 控制刷盘策略。

格式

STATEMENT(记录SQL语句)、ROW(记录行数据变化)、MIXED(混合模式)。

redo log 和 binlog 区别

1、适用对象不同:
binlog 是 MySQL 的 Server 层实现的日志,所有存储引擎都可以使用;
redo log 是 Innodb 存储引擎实现的日志;
2、文件格式不同:
binlog 记录每一条修改数据的 SQL (相当于记录了逻辑操作,所以针对这种格式, binlog 可以称为逻辑日志),
redo log 是物理日志,记录的是在某个数据页做了什么修改,比如对 XXX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新;
3、写入方式不同:
binlog 是追加写,写满一个文件,就创建一个新的文件继续写,不会覆盖以前的日志,保存的是全量的日志。
redo log 是循环写,日志空间大小是固定,全部写满就从头开始,保存未被刷入磁盘的脏页日志。
4、用途不同:
binlog 用于备份恢复、主从复制;
redo log 用于掉电等故障恢复。

MySQL Buffer Pool

核心概念​

MySQL 的数据都是存在磁盘中的,那么我们要更新一条记录的时候,得先要从磁盘读取该记录,然后在内存中修改这条记录。修改完这条记录不是直接写回到磁盘,而是缓存起来。
为此,Innodb 存储引擎设计了一个缓冲池(Buffer Pool),来提高数据库的读写性能。
Buffer Pool​​ 是 InnoDB 引擎的关键内存区域,用于缓存​​数据页和索引页​​,减少磁盘 I/O,提升数据库性能。
所有读写操作首先作用于 Buffer Pool,修改后的页称为​​脏页(Dirty Page)​​,最终由后台线程刷回磁盘。
在这里插入图片描述
当读取数据时,如果数据存在于 Buffer Pool 中,客户端就会直接读取 Buffer Pool 中的数据,否则再去磁盘中读取。
当修改数据时,如果数据存在于 Buffer Pool 中,那直接修改 Buffer Pool 中数据所在的页,然后将其页设置为脏页(该页的内存数据和磁盘上的数据已经不一致),为了减少磁盘I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入到磁盘。

Buffer Pool 配置​

大小设置​​:

通过参数 innodb_buffer_pool_size 配置,通常设为系统内存的 ​​50%-70%​​。

多实例​​:

在高并发场景下,可通过 innodb_buffer_pool_instances 分割为多个实例,减少锁竞争。

内存管理与 LRU 算法​

LRU 链表优化​​:

InnoDB 将 LRU 链表分为 ​​Young 区(热点数据)​​和 ​​Old 区(新加载数据)​​,避免全表扫描冲刷热点数据。
​​Old SubList​​:新数据页首先插入 Old 区头部。
​​Young SubList​​:若 Old 区的页在 innodb_old_blocks_time(默认 1 秒)后被再次访问,则移到 Young 区。

预读机制​​:

​​线性预读(Linear Read-ahead)​​:基于顺序访问模式,预读相邻页。
​​随机预读(Random Read-ahead)​​:根据页的访问模式预测加载其他页(默认关闭)。

脏页刷新与持久化​

刷新机制​​:

​​Checkpoint​​:定期将脏页刷盘,崩溃恢复时从 Checkpoint 位置开始重放 redo log。
​​自适应刷新(Adaptive Flushing)​​:根据负载动态调整刷盘速率,避免 I/O 尖峰。

​​刷盘触发条件​​:

Buffer Pool 空间不足时。
Redo Log 空间不足(通过日志推进检查点)。
后台线程定期刷新。

与日志系统的协作​

Redo Log​​:事务提交时先写 redo log,确保即使脏页未刷盘,崩溃后仍可通过 redo log 恢复。
​​Undo Log​​:用于事务回滚和 MVCC,其历史版本数据可能存储在 Buffer Pool 的 undo 页中。

事务隔离级别​

MySQL 支持四种隔离级别,通过 ​​MVCC​​ 和​​锁机制​​实现:

READ UNCOMMITTED(读未提交)​​

事务可以读到其他未提交事务的修改。
​​问题​​:脏读、不可重复读、幻读。

READ COMMITTED(读已提交,RC)​​

事务只能读到其他已提交事务的修改。
​​实现​​:每次 SELECT 生成新的 ReadView。
​​问题​​:不可重复读、幻读。

REPEATABLE READ(可重复读,RR)​​

​​默认隔离级别​​,事务内多次读取同一数据的结果一致。
​​实现​​:事务第一次 SELECT 时生成 ReadView,后续复用。
​​解决​​:脏读、不可重复读;通过​​间隙锁(Gap Lock)​​ 避免幻读(当前读时生效)。

SERIALIZABLE(串行化)​​

所有操作加锁,强制事务串行执行。
​​解决​​:所有并发问题,但性能最低。

MVCC

MVCC(多版本并发控制 Multi-Version Concurrency Control)是数据库实现高并发访问的核心机制,​​通过维护数据的多个版本,允许读操作不阻塞写操作,写操作也不阻塞读操作​​。InnoDB 引擎使用 MVCC 实现了 ​​非阻塞读​​,显著提升了数据库的并发性能,尤其在高读低写的场景下效果显著。

MVCC 的核心原理​

在这里插入图片描述

​​隐藏字段​​

InnoDB 的每行数据包含两个隐藏字段:
​​DB_TRX_ID​​:最近修改该行数据的事务 ID(事务提交时写入)。
​​DB_ROLL_PTR​​:指向 Undo Log 的回滚指针,用于构建数据的历史版本链。

​​Undo Log​​

存储数据的历史版本,通过回滚指针(DB_ROLL_PTR)串联成一个链表(版本链)。
​​作用​​:
事务回滚时恢复数据到旧版本。
提供 MVCC 所需的历史版本数据。

ReadView​​(读视图)

事务在发起读操作时生成的一个​​快照​​,用于判断数据版本的可见性。
​​关键信息​​:
m_ids:生成 ReadView 时活跃(未提交)的事务 ID 集合。
min_trx_id:活跃事务中的最小事务 ID。
max_trx_id:生成 ReadView 时系统将分配给下一个事务的 ID。
creator_trx_id:创建该 ReadView 的事务 ID(仅当该事务自身有修改时存在)。
在这里插入图片描述

数据可见性规则​

MVCC 通过以下规则判断数据版本是否对当前事务可见:
如果数据版本的 ​​DB_TRX_ID < min_trx_id​​,说明该版本在 ReadView 生成前已提交,​​可见​​。
如果数据版本的 ​​DB_TRX_ID >= max_trx_id​​,说明该版本由未来事务修改,​​不可见​​。
如果 ​​DB_TRX_ID 在 m_ids 中​​,说明该版本由未提交的事务修改,​​不可见​​。
如果 ​​DB_TRX_ID 不在 m_ids 中且 DB_TRX_ID < max_trx_id​​,说明该版本已提交,​​可见​​。
特殊处理​​:
如果该版本由当前事务自身修改(DB_TRX_ID == creator_trx_id),则可见。

MVCC 与隔离级别​

不同隔离级别下,MVCC 的行为差异主要体现在 ​​ReadView 的生成时机​​:

READ COMMITTED(读已提交,RC)​​

​​每次 SELECT 都生成新的 ReadView​​。
​​效果​​:能读到其他事务已提交的最新数据,但可能导致不可重复读。

REPEATABLE READ(可重复读,RR)​​

​​事务第一次 SELECT 时生成 ReadView,后续复用​​。(每开启一个事物才会生成一个 readView,一个事务的所有SQL语句共享一个 readView。)
​​效果​​:整个事务中看到的数据版本一致,解决不可重复读。

MVCC 如何解决并发问题​

脏读​​

不可见未提交事务的版本(通过 ReadView 过滤活跃事务的修改)。
例:
如下所示是 student 学生表中的一条数据
在这里插入图片描述
(1) 假设现在有事务A和事务B两个事务并发操作该行数据。事务A执行了两次查询操作,事务B执行了一次更新操作;
(2)事务A执行第一次查询操作,先生成 readView ,我们姑且称之为 readView_1,还未开始查询操作,事务B率先执行了更新操作,将数据进行了修改并提交,事务B结束,此时事务A第一次查询开始,但由于事务A已经生成了 readView_1 ,所以它不会读取到事务B修改过后的数据,读取到的是 readView_1 中事务B修改之前的数据,解决了脏读的问题;
(3)然后,事务A进行第二次查询操作。注意!!!这里它又生成了一个 readView,我们称之为 readView_2,此时的 readView_2 中的数据是已经被事务B修改后的数据了,事务A再次进行查询,发现查询到的数据和刚操第一次查询到的不一样了,就产生了不可重复读的问题。
(4)所以说,MVCC 在读已提交隔离级别下只解决了脏读的问题,没有解决不可重复读的问题。

​​不可重复读​​

RR 隔离级别下,复用同一个 ReadView 保证多次读取结果一致。
例:
仍以上面的学生表举例,如下所示
在这里插入图片描述
(1)假设现在事务A与事务B并发操作来查询 student 表。事务A 执行SELECT查询操作,执行查询操作之前会生成一个 readView,我们姑且称之为 readView_1 ,事务A从始至终使用的都是 readView_1;
(2)此时事务B来修改 student 数据,可重复读隔离级别中一个事务生成一个 readView ,所以事务B也生成了一个 readView ,我们称之为 readView_2,然后事务B率先修改完毕并提交;
(3)事务A在事务B提交之后才进行的查询,按道理来说因为事务B修改了数据,我们会产生不可重复读,但是因为事务A从始至终都是用的 readView_1 ,所以 事务A在进行查询操作的时候,查询到的其实还是事务B修改之前的数据,由此就解决了不可重复读;而且即便事务A后续进行了多次 SELECT 查询操作,仍然使用最开始生成的 readView,解决了 不可重复读的问题。
(4)总结:其实归根结底,读已提交,可重复读两种隔离级别最关键的因素就是 readView 生成的时机不同,造就了它们不同的隔离级别。

幻读​​

幻读(Phantom Read)是数据库事务隔离性问题的一种,表现为:​​同一事务中多次执行相同范围的查询,后续查询返回了其他事务插入的新行​​。MySQL 在 ​​可重复读(RR)​​ 隔离级别下,通过 ​​MVCC​​ 和 ​​间隙锁(Gap Lock)​​ 结合的方式解决幻读问题。以下是详细分析:

幻读的本质​

​​场景示例​​:
事务 A 查询 age > 20 的用户,返回 10 条数据。
事务 B 插入一条 age = 25 的新用户并提交。
事务 A 再次查询 age > 20,发现返回了 11 条数据(多出事务 B 插入的行)。
​​核心问题​​:​​范围查询中新增的数据破坏了事务的一致性视图​​。

MySQL 解决幻读的机制​
MVCC(快照读)​​

​​原理​​:
在 RR 隔离级别下,事务首次读取数据时生成 ​​ReadView​​,后续查询复用该快照,确保多次读取同一范围的数据时,结果一致(基于历史版本)。
​​效果​​:
​​快照读(普通 SELECT)​​:通过 MVCC 读取历史版本数据,​​不会看到其他事务插入的新行​​,避免了幻读。
​​局限性​​:若事务中执行写操作(如 UPDATE/DELETE),可能触发当前读,导致幻读风险。

间隙锁(Gap Lock)​

​​​​原理​​:
对索引记录之间的“间隙”加锁,阻止其他事务在范围内插入新数据。
​​间隙范围​​:例如,表中现有 id=5 和 id=10 的记录,间隙锁会锁定 (5, 10) 区间。
​​加锁场景​​:
执行 SELECT … FOR UPDATE(当前读)。
执行 UPDATE 或 DELETE 影响范围内的数据。
​​效果​​:
其他事务无法在加锁的间隙内插入新数据,​​彻底避免幻读​​。
仅在 RR 隔离级别下生效。

不同隔离级别下的幻读行为​

在这里插入图片描述

RR 隔离级别下的幻读解决方案​
快照读 + MVCC(隐式解决)​​

​​场景​​:事务中仅执行普通 SELECT 查询(快照读)。
​​示例​​:

-- 事务 A(RR 隔离级别)
START TRANSACTION;
SELECT * FROM users WHERE age > 20; -- 生成 ReadView,返回 10 条数据-- 事务 B 插入新数据并提交
INSERT INTO users (age) VALUES (25);
COMMIT;-- 事务 A 再次查询
SELECT * FROM users WHERE age > 20; -- 仍返回 10 条数据(ReadView 未更新)

结果​​:事务 A 的两次查询结果一致,MVCC 屏蔽了事务 B 的插入操作。

当前读 + 间隙锁(显式解决)​

​​场景​​:事务中执行 SELECT … FOR UPDATE、UPDATE 或 DELETE(当前读)。
​​示例​​:

-- 事务 A(RR 隔离级别)
START TRANSACTION;
SELECT * FROM users WHERE age > 20 FOR UPDATE; -- 当前读,对 age > 20 的间隙加锁-- 事务 B 尝试插入新数据(age=25)
INSERT INTO users (age) VALUES (25); -- 被阻塞,直到事务 A 提交或回滚-- 事务 A 提交后,事务 B 才能继续执行
COMMIT;

结果​​:事务 B 的插入操作被间隙锁阻塞,事务 A 的两次查询结果一致。

MVCC 的实现细节​

​​版本链遍历​​

从当前数据行的 DB_ROLL_PTR 开始,沿着 Undo Log 链表查找符合可见性规则的版本。

Undo Log 清理​​

当没有事务需要访问某个历史版本时,对应的 Undo Log 会被 Purge 线程清理。
​​风险​​:长事务可能导致 Undo Log 堆积,引发存储膨胀。

事务 ID 分配​​

事务 ID(DB_TRX_ID)是全局自增的,每个事务在修改数据时被分配唯一 ID。

快照读 vs 当前读​

快照读(Snapshot Read)​​

​​操作​​:普通 SELECT 语句(不加锁)。
​​实现​​:基于 MVCC 读取历史版本数据。
​​特点​​:不阻塞写操作,但可能读到旧数据。

当前读(Current Read)​​

​​操作​​:SELECT … FOR UPDATE、UPDATE、DELETE、INSERT。
​​实现​​:读取最新数据并加锁(如行锁、间隙锁),确保操作的一致性。
​​特点​​:阻塞其他事务的并发修改,保证数据最新。

相关文章:

  • 【深度学习与大模型基础】第14章-分类任务与经典分类算法
  • Vue2 相关知识点整理
  • 数据结构---单链表的增删查改
  • 第十六届蓝桥杯 2025 C/C++组 25之和
  • 【Bash】可以请您解释性地说明一下“2>1”这个语法吗?
  • 基于tabula对pdf中多个excel进行识别并转换成word中的优化(四)
  • 关于插值和拟合(数学建模实验课)
  • 【记】Laya2.x数字末尾导致换行异常问题
  • C# 14 field keyword:属性简化新利器
  • 基于PyTorch的Fashion-MNIST图像分类数据集处理与可视化
  • Java后端图形验证码的使用
  • [Linux网络_68] 转发 | 路由(Hop by Hop) | IP的分片和组装
  • 当OA闯入元宇宙:打卡、报销和会议的未来狂想
  • 【C++11】包装器:function与bind
  • 【BotSharp框架示例 ——实现聊天机器人,并通过 DeepSeek V3实现 function calling】
  • 【MuJoCo仿真】开源SO100机械臂导入到仿真环境
  • 在 Ubuntu 上离线安装 ClickHouse
  • ShaderToy学习笔记 05.3D旋转
  • 人工智能数学基础(三):微积分初步
  • 深入解析常见排序算法及其 C# 实现
  • 西班牙葡萄牙电力基本恢复
  • 连演三场,歌剧《义勇军进行曲》在上海西岸大剧院上演
  • 香港警务处高级助理处长叶云龙升任警务处副处长(行动)
  • 国家发改委:是否进口美国饲料粮、油料不会影响我国粮食供应
  • 2025厦门体育产业采风活动圆满举行
  • 清华成立人工智能医院,将构建“AI+医疗+教育+科研”闭环