mysql mvcc
在 MySQL 的 InnoDB 引擎中,MVCC (Multi-Version Concurrency Control,多版本并发控制) 是实现高性能并发读写的核心机制。
简单来说,它的作用是让“读不加锁,读写不冲突”。在处理事务时,它能让你在不影响别人写数据的情况下,读取到某个时间点的历史快照。
1. MVCC 的核心组件
MVCC 的实现并不是靠单一技术,而是由隐藏列、Undo Log 和 Read View 三者协作完成的。
① 记录中的隐藏列
InnoDB 会在每行数据的后面偷偷增加几个隐藏字段:
- DB_TRX_ID (6字节):最后一次插入或更新该行的事务 ID。
- DB_ROLL_PTR (7字节):回滚指针,指向该行上一个版本的 Undo Log 记录。
- DB_ROW_ID:如果表没有主键,InnoDB 会自动生成这个隐藏的聚簇索引 ID。
② Undo Log (回滚日志)
当你修改一条记录时,InnoDB 会先把原始数据拷贝一份到 Undo Log 里。通过 DB_ROLL_PTR 指针,这些不同版本的记录会被串联成一条版本链。
③ Read View (一致性视图)
这是 MVCC 的“大脑”。当一个事务执行快照读(Simple Select)时,会产生一个 Read View,里面记录了:
- m_ids:当前系统中活跃(还没提交)的事务 ID 列表。
- min_trx_id:活跃事务中最小的 ID。
- max_trx_id:系统即将分配给下一个事务的 ID(当前最大 ID + 1)。
- creator_trx_id:创建这个 Read View 的事务 ID。
2. 数据可见性算法(重点)
当事务想读某行数据时,会拿该行的 DB_TRX_ID 与 Read View 进行比对,判断规则如下:
- TRX_ID == creator_trx_id:说明这行是你自己改的,可见。
- TRX_ID < min_trx_id:说明改这行数据的事务在快照创建前就提交了,可见。
- TRX_ID >= max_trx_id:说明改这行的事务在快照创建后才开启,不可见。
- min_trx_id <= TRX_ID < max_trx_id:
- 如果 TRX_ID 在
m_ids列表里:说明事务还没提交,不可见。 - 如果不在:说明事务已提交,可见。
- 如果 TRX_ID 在
如果不可见怎么办? 顺着
DB_ROLL_PTR去 Undo Log 找上一个版本,重复上述判断,直到找到可见的版本。
3. 不同隔离级别下的表现
MVCC 主要服务于 RC (已提交读) 和 RR (可重复读):
- RC (Read Committed):
- 每次 Select 都会重新生成一个新的 Read View。
- 结果:能看到别人刚提交的修改,所以会产生“不可重复读”。
- RR (Repeatable Read):
- 只有第一次 Select 时生成 Read View,后续查询复用同一个。
- 结果:整个事务期间看到的数据一致,从原理上解决了“不可重复读”,并在很大程度上避免了“幻读”。
4. 总结:为什么需要 MVCC?
如果没有 MVCC,数据库为了保证隔离性必须频繁加锁。
- 传统方案:读写互相阻塞,并发性能差。
- MVCC 方案:
- 快照读 (Snapshot Read):普通的
SELECT,不加锁,读历史版本。 - 当前读 (Current Read):
SELECT ... FOR UPDATE或UPDATE/DELETE,读取最新版本并加锁。
- 快照读 (Snapshot Read):普通的
一句话总结:MVCC 通过保存数据的历史版本,让数据库在处理大量并发读写时,依然能保持像“单机快照”一样的丝滑体验。
mysql 日志
在 MySQL 的体系结构中,redo log 保证持久性,undo log 保证原子性,binlog 用于数据同步和恢复。
1. 三种日志对比概览
| 特性 | Redo Log (重做日志) | Undo Log (回滚日志) | Binlog (归档日志) |
|---|---|---|---|
| 所属层级 | InnoDB 存储引擎层 | InnoDB 存储引擎层 | MySQL Server 层 |
| 记录内容 | 物理日志(在哪个页做了什么修改) | 逻辑日志(相反的操作,如插入对应删除) | 逻辑日志(原始 SQL 或数据行变动) |
| 文件格式 | 循环写入,空间固定 | 存储在回滚段(Rollback Segment) | 追加写入,不覆盖旧文件 |
| 主要作用 | Crash-safe(宕机恢复) | 事务回滚、MVCC | 主从复制、数据恢复 |
2. 深入解析
① Redo Log:保证“不丢数据”
当有一条记录需要更新时,InnoDB 会先更新内存(Buffer Pool),并把这次修改记录到 Redo Log 中。此时更新就算完成了。
- WAL 技术 (Write-Ahead Logging):先写日志,再写磁盘。
- Checkpoint:Redo Log 大小固定(如一组 4 个文件),当日志写满时,系统会停下来将 Buffer Pool 中的脏页刷入磁盘,然后擦除旧日志。
- 意义:即使数据库异常重启,InnoDB 也可以根据 Redo Log 将未落盘的数据恢复。
② Undo Log:数据的“后悔药”
Undo Log 记录了事务执行过程中的中间状态。
- 回滚操作:如果你执行了
INSERT,Undo Log 记录对应的DELETE;如果你执行了UPDATE,它记录修改前的值。 - MVCC 实现:当一个事务读取某行记录,但该记录正被其他事务修改时,它可以通过 Undo Log 链找到该行之前的版本(快照读)。
③ Binlog:数据库的“流水账”
这是 MySQL Server 层产生的日志,记录了所有的 DDL 和 DML 操作(不包含查询)。
- 主从复制:从库读取主库的 Binlog 并重放,实现数据同步。
- 数据恢复:结合全量备份,重放 Binlog 可以将数据库恢复到任意时间点。
3. 三日志如何协作?(二阶段提交)
为了保证 Redo Log 和 Binlog 的一致性(防止 Redo Log 成功但 Binlog 没记上的情况),MySQL 使用了 二阶段提交 (Two-Phase Commit):
- Prepare 阶段:InnoDB 将更新记录写入 Redo Log,并将其状态设置为
prepare。 - 写 Binlog:MySQL Server 将操作写入 Binlog 并刷盘。
- Commit 阶段:MySQL Server 调用引擎提交接口,将 Redo Log 状态改为
commit。
为什么这么麻烦?
如果没有二阶段提交,可能会出现:Redo Log 记录了修改(物理数据恢复了),但 Binlog 没记录(从库没同步),导致主从数据不一致。
总结
- Redo Log 是为了 Crash-safe,保证数据库挂了重启能恢复。
- Undo Log 是为了 事务回滚 和 MVCC。
- Binlog 是为了 主从同步 和 数据恢复。
理解底层实现,本质上是理解 数据如何存放(磁盘)、数据如何流转(内存) 以及 一致性如何保证(日志)。
1. 磁盘结构:数据的“物理家园”
MySQL 的数据并不是散乱存放的,而是按照严格的层级组织在 .ibd 文件中:
- 表空间 (Tablespace):逻辑上的最高层。
- 段 (Segment):分为数据段(叶子节点)、索引段(非叶子节点)等。
- 区 (Extent):固定大小为 1MB(连续的 64 个页)。
- 页 (Page):最小的磁盘管理单位,默认 16KB。
- 底层原理:B+ 树的每个节点就是一个“页”。为了减少磁盘 I/O,MySQL 尽量让相关数据在物理上靠近。
2. 内存结构:性能的“加速器”
为了弥补磁盘和 CPU 之间的巨大速度鸿沟,InnoDB 设计了复杂的内存管理机制(In-Memory Structures):
Buffer Pool (缓冲池)
这是 MySQL 内存中最大的一块,缓存了数据页和索引页。
- LRU 算法优化:为了防止全表扫描把热数据刷掉,InnoDB 将 LRU 链表分为 New Sublist 和 Old Sublist。新读取的页先放到 Old 区域,只有多次访问且满足时间间隔后才升入 New 区域。
- 刷脏 (Flush):后台线程会异步将 Buffer Pool 中的脏页写入磁盘。
Change Buffer (写缓冲区)
- 原理:如果你修改的数据页不在内存里,InnoDB 不会立刻去磁盘读,而是先把修改操作记在 Change Buffer 中。等下次读取该页时再进行 Merge。
- 限制:仅适用于非唯一二级索引(因为唯一索引需要去磁盘校验唯一性)。
3. 核心机制:并发与可靠性的博弈
B+ 树索引底层
为什么是 B+ 树而不是 B 树或红黑树?
- 更矮更胖:阶数更多,磁盘 I/O 次数极少(通常 3-4 层即可支撑千万级数据)。
- 叶子节点链表:所有数据都在叶子节点,且通过双向链表连接,极大地优化了范围查询。
ARIES 恢复算法与二阶段提交
MySQL 结合了我们之前聊过的日志系统,实现了分布式事务中的 2PC (Two-Phase Commit)。
- 原子性 (A):靠 Undo Log 实现回滚。
- 一致性 (C):由约束(如主键、外键)和代码逻辑保证。
- 隔离性 (I):由 锁 (Locks) 和 MVCC 共同实现。
- 持久性 (D):靠 Redo Log 确保掉电不丢失。
4. 线程模型
MySQL 是单进程多线程模型:
- Master Thread:负责异步刷脏、合并 Change Buffer 等。
- IO Thread:负责处理 AIO 请求的回调。
- Purge Thread:负责回收已经不再需要的 Undo Log 记录,防止日志文件无限膨胀。
研究建议
深入研究底层实现时,我建议你按这个顺序“突破”:
- 看 Page 结构:理解行溢出、行格式(Compact/Dynamic)。
- 看 2PC 流程:理解为什么 Redo Log 和 Binlog 要协同工作。
- 看 锁机制:理解 Next-Key Lock 如何在 RR 级别下缓解幻读。
Buffer Pool (缓冲池)和双写缓冲区
在 MySQL 的底层架构中,Buffer Pool (缓冲池) 是为了提升性能(减少磁盘 I/O),而 Doublewrite Buffer (双写缓冲区) 则是为了保证数据的安全性(防止页断裂)。
它们一个负责“快”,一个负责“稳”。
1. Buffer Pool:内存的核心
Buffer Pool 是 InnoDB 在内存中开辟的一块连续空间,用来缓存磁盘上的数据页。
核心管理机制
由于内存空间有限,InnoDB 必须高效地管理这些页:
- Free List (空闲链表):记录哪些内存页是干净的,可以直接用来存放磁盘读入的数据。
- LRU List (最近最少使用链表):
- 优化:InnoDB 将其分为 New Sublist (5/8) 和 Old Sublist (3/8)。
- 目的:防止全表扫描(Table Scan)一次性将所有热数据挤出内存。新读入的页先放在 Old Header,只有满足一定时间间隔后再次被访问,才会升入 New 区域。
- Flush List (脏页链表):记录哪些页在内存中被修改了(变成了“脏页”),等待后台线程异步刷入磁盘。
2. Doublewrite Buffer:防止“页断裂”
这是很多初学者容易忽略但极其重要的机制。
为什么需要它?(Partial Page Write 问题)
- 背景:MySQL 的数据页大小通常是 16KB,而操作系统的文件系统(如 Linux 的 ext4)最小写入单位通常是 4KB。
- 危机:当 InnoDB 正在向磁盘写入一个 16KB 的页时,如果系统突然断电,可能只写了前 4KB 或 8KB。
- 结果:这个页在磁盘上损坏了(Partial Page Write)。
- Redo Log 能救吗? 不能。 Redo Log 记录的是物理偏移量的修改(比如:在偏移量 100 处改成了 'A')。如果基础页(Page)本身由于“半写”损坏了,Redo Log 无法在损坏的页上进行重做。
双写缓冲的工作流程
为了解决这个问题,InnoDB 在将脏页刷入 .ibd 数据文件之前,先走一步“中转站”:
- 第一步:将脏页复制到内存中的
doublewrite buffer。 - 第二步:将
doublewrite buffer分两次、每次 1MB 顺次写入磁盘上的共享表空间(物理上的连续存储块)。 - 第三步:在确认双写缓冲区写入成功后,再将脏页写入各个表对应的
.ibd文件(离散写入)。
恢复逻辑
- 如果写入
.ibd时崩溃,但双写缓冲区里有完整的备份,MySQL 重启时会用双写缓冲里的页覆盖损坏的页,再应用 Redo Log。 - 如果写入双写缓冲区时崩溃,由于原有的
.ibd文件还是完整的旧版本,直接应用 Redo Log 恢复即可。
3. 两者的关系与对比
| 特性 | Buffer Pool | Doublewrite Buffer |
|---|---|---|
| 位置 | 纯内存 | 内存 + 磁盘 (系统表空间) |
| 目标 | 提高读写性能,减少磁盘访问 | 保证写入的原子性,防止页损坏 |
| 副作用 | 占用大量内存 | 带来一定的双倍磁盘写开销(约 5-10%) |
| 是否可关闭 | 必须开启 | 可以关闭(但在不支持原子写的系统上极度危险) |
进阶思考
随着硬件的发展,现在一些高级文件系统(如 ZFS)或者某些高性能企业级 SSD 已经支持了 Atomic Write (原子写)。在这些环境下,由于硬件能保证 16KB 写入的完整性,我们可以通过设置 innodb_doublewrite = 0 来关闭双写缓冲区,从而压榨出更多的性能。
双写缓冲区如何保证不丢失数据
双写缓冲区(Doublewrite Buffer)的核心逻辑在于:它为“脏页落盘”这个高风险动作提供了一个物理备份。
即便发生极端情况(如断电),导致正在写入的数据页“断裂”损坏,MySQL 依然能从双写缓冲区中找回一份完整的拷贝。
1. 它是如何保证数据不丢失的?
要理解它的原理,必须先看 “页断裂(Partial Page Write)” 这一致命伤。
场景还原:如果没有双写缓冲区
- InnoDB 准备将一个 16KB 的脏页写入磁盘。
- 磁盘 IO 写入了前 4KB,此时突然断电。
- 结果:磁盘上的页处于中间状态(半新半旧),由于校验和(Checksum)对不上,这个页被判定为物理损坏。
- Redo Log 为什么救不了? Redo Log 记录的是物理偏移量的增量修改(例如:在第 10 字节处将 A 改为 B)。它要求基础页必须是完整的。如果页本身物理结构坏了,Redo Log 就像在废纸上画画,根本无法恢复。
解决方案:先备份,再落盘
有了双写缓冲区,过程变为:
- 内存备份:脏页先在内存中拷贝到双写缓冲区。
- 顺序写备份(安全区):双写缓冲区将数据分两次、批量地写入磁盘上的共享表空间(连续存储块)。由于是顺序 IO,性能开销较小。
- 离散写原表(目标区):确认备份写入磁盘成功后,再将脏页写入各自对应的
.ibd文件。
故障恢复逻辑:
- 如果在第 2 步崩溃:由于
.ibd原始文件还没动,数据还是旧但完整的。重启后,直接通过 Redo Log 恢复即可。 - 如果在第 3 步崩溃:此时
.ibd文件页损坏了。但没关系,MySQL 重启时发现页损坏,会去双写缓冲区磁盘空间找对应的完整副本,覆盖掉损坏的页,然后再应用 Redo Log。
2. 双写缓冲区有多大?
双写缓冲区的结构分为内存层和磁盘层。
磁盘上的物理空间
- 默认大小:通常为 2MB。
- 结构:它由 2 个区(Extent) 组成,每个区 1MB。
- 位置:在 MySQL 8.0 之前,它固定在系统表空间(ibdata1)中;在 MySQL 8.0 之后,它有了专门的
.dblwr文件,甚至可以配置多个文件来减少竞争。
内存中的缓冲区
- 大小:同样是 2MB。
- 分块:它由 128 个页(Page) 组成(每个页 16KB,\(128 \times 16KB = 2048KB = 2MB\))。
3. 性能与代价
既然每次写数据都要写两次磁盘,性能会下降吗?
- 性能损耗:大约在 5% - 10% 左右。
- 为什么损耗不大? 因为双写缓冲区的磁盘写入是顺序 IO(Sequential IO),速度极快;而写入
.ibd的过程是随机 IO(Random IO)。两者的开销不在一个量级上。
什么时候可以关掉它?
如果你满足以下条件之一,可以设置 innodb_doublewrite = 0 来压榨性能:
- 文件系统支持原子写:例如某些高端 NVMe 推进器或特定的文件系统(如 ZFS),能保证 16KB 写入的原子性。
- 对数据一致性要求不高:比如作为临时缓存的数据。
- 从库节点:如果主库有双写,从库为了追求同步速度,在某些架构下会选择关闭。
总结一下:
双写缓冲区就像是写正式文件前的底稿。万一正式文件写一半笔没墨了(断电),我们还能照着底稿(双写缓冲)再抄一份。
1. 为什么“第1步挂了”不可怕?
在第1步(内存拷贝和准备写入磁盘备份)发生崩溃时,情况如下:
- 内存状态:内存中的脏页还在(虽然断电会消失),但关键在于磁盘上的原始数据页(.ibd文件)完全没动过。
- Redo Log 状态:在修改 Buffer Pool 里的数据之前,Redo Log 已经先落盘了(WAL原则)。
恢复逻辑:
当 MySQL 重启进行崩溃恢复(Crash Recovery)时:
- 检测磁盘上的数据页,发现它们的 LSN(日志序列号)落后于 Redo Log。
- 因为磁盘上的页是完整且干净的旧版本(没有页断裂),MySQL 直接读取这些旧页。
- 通过 Redo Log 里的物理记录,把缺失的修改“重做”一遍。
- 结论:此时根本不需要双写缓冲区参与,Redo Log 就能搞定一切。
2. 核心原理:双写缓冲区到底是救谁的?
双写缓冲区存在的唯一目的,是救治“正在写磁盘却没写完”导致的物理损坏。
我们可以把落盘过程看作三个风险阶段:
| 阶段 | 动作 | 如果此时崩溃 | 恢复方案 |
|---|---|---|---|
| 阶段 1 | 内存拷贝/准备写备份 | 磁盘原文件完好 | 直接用 Redo Log 恢复 |
| 阶段 2 | 正在写双写磁盘备份 | 磁盘原文件完好 | 直接用 Redo Log 恢复(损坏的备份直接丢弃) |
| 阶段 3 | 正在写 .ibd 目标文件 | 磁盘原文件页断裂(坏了) | 用双写备份覆盖坏页,再跑 Redo Log |
关键点:只有在“写目标文件”的时候挂了,导致原数据页物理上变“碎”了,Redo Log 才会失效。而第 1 步和第 2 步,原文件都是安全的,所以完全不虚。
3. 深度思考:那双写缓冲区自己写坏了怎么办?
如果是在第 2 步(向共享表空间的双写备份区写入)时断电了,导致双写缓冲区里的备份页是断裂的呢?
这也没关系!
MySQL 重启时会检查双写缓冲区里的页。如果发现备份页的 Checksum(校验和)不对,说明备份是坏的。但由于此时 .ibd 原始文件还没开始写,它是完好无损的旧版,MySQL 依然会选择跳过双写缓冲区,直接用 Redo Log 恢复。
总结
双写缓冲区是一种“后备方案”。
- 只有当“主力(磁盘原页)”伤得体无完肤(页断裂)时,它才上场救火。
- 如果只是“准备阶段”或“后备方案自己”伤了,只要“主力”还在,Redo Log 就能包治百病。
redo log 何时写入
WAL(Write-Ahead Logging,预写日志)机制**。
简单直接的回答是:Redo Log 的写入一定是在双写缓冲区(Doublewrite Buffer)落盘之前完成的。 如果把数据页落盘比作“搬家”,Redo Log 就是你的“搬家清单”。你一定是先写好清单,才开始搬家具。
1. 完整的写入时序图
为了保证数据绝对安全,InnoDB 遵循严格的操作顺序:
- 修改内存:事务修改 Buffer Pool 中的页,该页变为“脏页”。
- 生成 Redo Log:同时产生一条 Redo Log 记录,先存入内存中的 Redo Log Buffer。
- Redo Log 刷盘:根据策略(通常是事务提交时),将 Redo Log 写入磁盘。此时,事务已经可以返回成功了。
- 准备刷脏:后台线程决定将脏页写入磁盘。
- 双写缓冲(第一步):脏页先拷贝到内存的 Doublewrite Buffer。
- 双写缓冲(第二步):Doublewrite Buffer 批量、顺序写入磁盘。
- 最终落盘:最后,脏页才写入对应的
.ibd数据文件。
2. Redo Log 是如何写入的?
Redo Log 的写入过程并不是简单的“写文件”,它为了极致的性能设计了三层结构:
第一层:Redo Log Buffer(内存)
在修改数据时,日志先写到内存缓冲区。它是顺序写的,非常快。
第二层:OS Cache(操作系统缓存)
通过调用系统的 write() 函数,日志从 MySQL 进程转交到了操作系统的文件缓存中。此时如果 MySQL 挂了,数据还在内存里;如果操作系统挂了,数据就丢了。
第三层:Log File(磁盘)
通过调用 fsync(),强制操作系统将数据刷入物理磁盘。这是真正的“落地”。
控制写入时机的参数:innodb_flush_log_at_trx_commit
你可以通过这个参数控制 Redo Log 的写入强度:
- 0:每秒写入并刷盘一次(性能最高,但崩溃可能丢 1 秒数据)。
- 1(默认):每次事务提交都必须刷盘(最安全,保证 ACID)。
- 2:每次事务提交写到 OS Cache,每秒刷盘一次(折中方案)。
3. 为什么一定要先写 Redo Log?
这就是所谓的 WAL 原则(Write-Ahead Logging)。
如果先写双写缓冲区(即开始刷脏页),万一写到一半断电了,磁盘上的页坏了,而此时 Redo Log 还没写完,那这个数据就彻底没救了。
现在的逻辑是:
- 只要 Redo Log 落地了,即使后面的双写缓冲区、数据页落盘全部失败,MySQL 重启时也能根据 Redo Log 把内存里的修改重新“复现”出来。
- Redo Log 记录的是增量修改,双写缓冲区保护的是物理页完整。 它们相辅相成,但 Redo Log 永远走在最前面。
总结
Redo Log 和双写缓冲区的写入是异步的。
- Redo Log 随着事务提交频繁写入。
- 双写缓冲区 随着脏页刷新(Checkpoint 机制)批量写入。
当你观察磁盘 IO 时,你会发现 Redo Log 的写入通常是极小的、持续的 IO,而双写缓冲和数据落盘则是大块的、周期性的 IO。
生成redo log时,服务挂了会丢数据吗,redo log写入时undo log何时写入
这是一个非常精细的边界问题,触及了事务原子性(Atomicity)和持久性(Durability)的底线。
我们先说结论:只要事务没提交(Commit),Redo Log 没写完也不算“丢数据”;只要事务提交成功,数据就绝对不会丢。
1. 生成 Redo Log 时服务挂了,会丢数据吗?
这取决于你的事务进行到了哪一步。我们要分两种情况看:
情况 A:事务还在运行中,尚未执行 COMMIT
- 现状:你执行了一半的 SQL,生成了一部分 Redo Log 在内存(Redo Log Buffer)里,还没来得及写磁盘,服务挂了。
- 结果:不丢数据。因为在数据库看来,这个事务从未成功过。
- 恢复:重启后,磁盘上的数据还是旧的,内存里的残余日志丢了就丢了,数据库保持一致性。
情况 B:你发起了 COMMIT 命令,但 Redo Log 写入磁盘时挂了
- 现状:MySQL 正在把这个事务的日志从内存往磁盘刷。
- 结果:事务失败。客户端会收到一个报错或连接断开。
- 判定标准:只有当 Redo Log 完整写入磁盘(对于默认配置
innodb_flush_log_at_trx_commit = 1而言),MySQL 才会告诉客户端“提交成功”。 - 结论:只要你收到了“提交成功”的反馈,数据就一定在磁盘日志里,不会丢。 如果没收到成功反馈,这个事务就被视为失败,重启后会被回滚。
2. Redo Log 写入时,Undo Log 何时写入?
这是一个经典的误区:很多人以为先写完 Undo 再写 Redo,或者反过来。
真相是:Undo Log 的产生是伴随着数据修改同时发生的,而且 Undo Log 本身的写入也受 Redo Log 保护。
写入时序(微观视角):
当你执行一条 UPDATE 语句时,InnoDB 内部的顺序是:
- 准备 Undo:在内存中找一个 Undo 页面,写入撤销当前修改所需的信息。
- 记录 Undo 的 Redo:(关键点) 产生一条 Redo Log,记录“我刚才对 Undo 页面做了什么修改”。
- 修改数据页:修改 Buffer Pool 里的数据页。
- 记录数据的 Redo:产生一条 Redo Log,记录“我刚才对数据页面做了什么修改”。
结论:Undo Log 比数据更早产生,但它们的“改变记录”都会被写进 Redo Log 并在事务提交时一起刷盘。
3. 为什么要这样设计?(底层逻辑)
你可能会问:为什么 Undo Log 也要记 Redo Log?
- 原因:Undo Log 也是存在磁盘文件里的(物理页)。为了保证性能,修改 Undo 页时也是先改内存。
- 场景:如果修改了数据,也生成了 Undo,但没记 Redo 就断电了。重启后,数据页可能坏了,你连 Undo 页都找不回来,怎么回滚?
- 解决:把 Undo Log 的修改也当成一种“数据修改”,交给 Redo Log 去保证持久性。
总结:
- Redo Log 没写盘,事务就不算成功。只要事务没成功,就不存在“丢数据”的说法,因为状态没变。
- Undo Log 先于数据修改产生。
- Redo Log 包含了 Undo Log 的变动记录。在磁盘层面,Redo Log 是老大,它保护了数据页,也保护了 Undo 页。