各位观众老爷们,大家好!今天咱们聊点刺激的,聊聊MySQL的“起死回生术”——崩溃恢复。
想象一下,你正在玩一个紧张刺激的游戏,眼看就要通关了,突然电脑蓝屏了!心态崩了?别急,MySQL也有可能遇到类似的情况,比如服务器突然断电、系统崩溃等等。但MySQL可比咱们的游戏聪明多了,它有两大法宝:Redo Log
(重做日志)和 Undo Log
(撤销日志),能让它在崩溃后恢复到安全状态,最大程度地减少数据丢失。
今天咱们就来一场“Redo Log”与“Undo Log”的恢复之旅,看看MySQL是如何靠它们起死回生的。
一、什么是Redo Log?为什么要它?
简单来说,Redo Log
就是MySQL记录“我要做什么”的日记本。它记录的是物理级别的修改,也就是“把哪个页面的哪个字节改成什么”。
- 为什么需要Redo Log?
假设你执行一个更新语句:UPDATE users SET age = 30 WHERE id = 1;
这个操作可能涉及到多个步骤:
- 找到
id = 1
的那一行数据所在的页。 - 修改该页面的数据。
- 将修改后的页面刷盘(写入磁盘)。
问题来了:如果数据库在第2步完成,但第3步还没来得及做的时候崩溃了,那数据就丢失了,因为内存中的修改还没持久化到磁盘上。
这时候,Redo Log
就派上用场了。在修改数据之前,MySQL会先将本次修改的日志写入Redo Log
,然后再去修改内存中的数据。这样,即使数据库崩溃,重启后也能通过Redo Log
将未完成的修改重新执行一遍,保证数据的完整性。
-
Redo Log 的特点:
- 循环写入:
Redo Log
是一组固定大小的文件,采用循环写入的方式。当写满时,会覆盖最早的日志记录。 - 顺序IO:
Redo Log
采用顺序IO,写入速度非常快。 - 物理日志: 记录的是物理级别的修改,例如 "page X offset Y 改成 Z"。
- 循环写入:
-
Redo Log 长什么样?
虽然我们不能直接打开Redo Log文件看到里面的内容,但我们可以想象它大概是这样的:
Log Sequence Number (LSN) Table ID Page Number Offset Length Data 1001 5 10 128 4 30 // 修改了Table ID为5的第10页,偏移量为128的位置,长度为4个字节,修改为30 1002 5 10 132 4 ‘Bob’ // 修改了Table ID为5的第10页,偏移量为132的位置,长度为4个字节,修改为’Bob’ LSN
(Log Sequence Number)是一个递增的序列号,用于标识Redo Log
的顺序。 -
Redo Log 相关参数
以下是一些重要的Redo Log相关参数,可以通过
SHOW GLOBAL VARIABLES LIKE '%innodb_log%';
查看:参数名称 描述 innodb_log_file_size
每个Redo Log文件的大小。 innodb_log_files_in_group
Redo Log文件的数量。通常设置为2或3。 innodb_log_buffer_size
Redo Log 缓冲区的大小。InnoDB会将Redo Log先写入缓冲区,然后再批量刷盘。 innodb_flush_log_at_trx_commit
控制Redo Log的刷盘策略。 -
innodb_flush_log_at_trx_commit
的三种策略:- 0: 每秒将Redo Log缓冲区的内容刷盘一次。即使事务提交了,也可能没有立即刷盘。
- 1: 每次事务提交时,都将Redo Log缓冲区的内容刷盘。这是最安全的策略,但性能也最差。
- 2: 每次事务提交时,将Redo Log缓冲区的内容写入操作系统缓冲区,然后由操作系统决定何时刷盘。
通常建议设置为
1
,以保证数据的安全性。
二、什么是Undo Log?为什么要它?
Undo Log
记录的是修改前的状态,也就是“我要怎么撤销”。它记录的是逻辑级别的修改,也就是“把哪个页面的哪个记录改成什么”。
- 为什么需要Undo Log?
假设你执行一个更新语句:UPDATE users SET age = 30 WHERE id = 1;
如果更新过程中出现错误,例如唯一约束冲突,或者你执行了 ROLLBACK
语句,就需要将数据恢复到修改前的状态。这时候,Undo Log
就派上用场了。
MySQL会根据 Undo Log
中记录的信息,将数据恢复到原始状态,保证事务的原子性。
-
Undo Log 的特点:
- 保证事务的原子性: 用于回滚事务,将数据恢复到修改前的状态。
- 逻辑日志: 记录的是逻辑级别的修改,例如 "将 id = 1 的 age 改回 25"。
- 可能需要多次写入: 一个事务可能对应多个
Undo Log
记录。
-
Undo Log 长什么样?
同样,我们不能直接看到
Undo Log
文件的内容,但可以想象它大概是这样的:Transaction ID Table ID Page Number Row ID Original Data 123 5 10 1 {age: 25, name: ‘Alice’} // 事务ID为123的事务,修改了Table ID为5的第10页,Row ID为1的行的原始数据 -
Undo Log 存储位置
- 在MySQL 5.6之前,Undo Log 只能存储在共享表空间 (ibdata1) 中。
- 从MySQL 5.6开始,Undo Log 可以存储在独立的Undo表空间中,通过
innodb_undo_tablespaces
参数配置Undo表空间的数量。
-
Undo Log 相关参数
innodb_undo_tablespaces
: 用于配置Undo表空间的数量,默认值为0,表示Undo Log存储在共享表空间中。如果设置为大于0的值,则表示使用独立的Undo表空间。innodb_undo_directory
: 用于指定Undo表空间的存储目录。
三、Redo Log 和 Undo Log 的协同工作
Redo Log
和 Undo Log
就像一对黄金搭档,共同保证了MySQL的数据一致性。
当执行一个事务时,MySQL会:
- 将要修改的数据的原始状态写入
Undo Log
。 - 将要做的修改写入
Redo Log
。 - 修改内存中的数据。
- 提交事务。
- 根据
innodb_flush_log_at_trx_commit
的设置将Redo Log
刷盘。 - 在适当的时候将修改后的数据刷盘。
-
崩溃恢复过程:
- 分析 Redo Log: MySQL会扫描
Redo Log
,找到所有未完成的事务。 - 重做未完成的事务: 对于已经提交但尚未刷盘的事务,MySQL会根据
Redo Log
将其重做,保证数据的持久性。 - 回滚未提交的事务: 对于尚未提交的事务,MySQL会根据
Undo Log
将其回滚,保证事务的原子性。
- 分析 Redo Log: MySQL会扫描
四、一个更形象的比喻
咱们再来打个比方:
- Redo Log: 就像是饭店的“点菜记录”。厨师根据点菜记录,保证每一道菜都能做出来(即使客人突然断电跑路了,饭店也能根据点菜记录把菜做出来,然后找他要钱)。
- Undo Log: 就像是饭店的“退菜记录”。如果客人突然说“这道菜我不想要了”,厨师可以根据退菜记录,把菜恢复到原来的状态(比如把已经切好的葱花放回葱里)。
五、实战演练:模拟崩溃恢复
为了更好地理解 Redo Log
和 Undo Log
的作用,我们可以通过模拟崩溃来观察MySQL的恢复过程。
准备工作:
- 关闭自动提交:
SET autocommit = 0;
-
创建一个测试表:
CREATE TABLE test_recovery ( id INT PRIMARY KEY, name VARCHAR(255) );
-
插入一条数据:
INSERT INTO test_recovery (id, name) VALUES (1, 'Original Name');
模拟崩溃:
- 开启一个事务:
START TRANSACTION;
- 修改数据:
UPDATE test_recovery SET name = 'New Name' WHERE id = 1;
- 在事务提交之前,手动停止MySQL服务器。 (例如使用
sudo systemctl stop mysql
)。
恢复过程:
- 启动MySQL服务器。 (例如使用
sudo systemctl start mysql
) - 连接到数据库。
- 查询数据:
SELECT * FROM test_recovery WHERE id = 1;
观察结果:
- 如果
innodb_flush_log_at_trx_commit
设置为1
,那么你会发现数据已经被修改为 ‘New Name’,因为Redo Log已经刷盘,MySQL在启动时会重做该事务。 - 如果
innodb_flush_log_at_trx_commit
设置为0
或2
, 并且Redo Log还没来得及刷盘, 那么你会发现数据仍然是 ‘Original Name’,因为MySQL在启动时会根据Undo Log回滚该事务。
代码示例:
-- 准备工作
SET autocommit = 0;
CREATE TABLE test_recovery (
id INT PRIMARY KEY,
name VARCHAR(255)
);
INSERT INTO test_recovery (id, name) VALUES (1, 'Original Name');
-- 模拟崩溃
START TRANSACTION;
UPDATE test_recovery SET name = 'New Name' WHERE id = 1;
-- 此时手动停止MySQL服务器
-- 恢复过程
-- 启动MySQL服务器
-- 连接到数据库
-- 查询数据
SELECT * FROM test_recovery WHERE id = 1;
-- 观察结果
六、总结
Redo Log
和 Undo Log
是MySQL保证数据一致性的两大基石。Redo Log
用于重做已提交的事务,保证数据的持久性;Undo Log
用于回滚未提交的事务,保证事务的原子性。
理解 Redo Log
和 Undo Log
的工作原理,可以帮助我们更好地理解MySQL的事务机制,从而写出更高效、更可靠的SQL语句。
希望通过今天的讲解,大家对MySQL的崩溃恢复机制有了更深入的了解。下次遇到MySQL崩溃的情况,就可以淡定地告诉你的同事:“别慌,有Redo Log和Undo Log呢!”
好了,今天的讲座就到这里,感谢大家的观看! 咱们下期再见!