MySQL崩溃恢复时,先通过Redo Log前滚使数据页达到最新状态,再扫描Undo Log中TRX_UNDO_ACTIVE或TRX_UNDO_PREPARED标记,识别无COMMIT记录的未提交事务并回滚;Undo Log存储结构化逆操作信息(如旧值、物理位置、行镜像),支持精准字段级还原,而非执行反向SQL。

崩溃恢复时MySQL怎么发现哪些事务该回滚
MySQL在崩溃后启动时,会先读取redo log重放已写入但未刷到数据页的变更(前滚),确保数据页处于“最新可能状态”;接着扫描undo log段中的事务状态——关键看每个事务对应的TRX_UNDO_ACTIVE或TRX_UNDO_PREPARED标记是否还存在。如果事务的trx_id出现在活跃事务列表里,且没有对应的COMMIT记录(即undo log中最后一条是INSERT/UPDATE而非TRX_UNDO_COMMIT_MARK),就判定为未提交,需回滚。
Undo Log里存的到底是什么,够用来反向操作吗
不是SQL语句文本,而是结构化的逆操作记录:
-
INSERT事务的 undo log 存的是对应DELETE的物理位置(space_id+page_no+heap_no) -
UPDATE事务存的是被覆盖前的旧值(完整字段值,含隐藏列如DB_TRX_ID、DB_ROLL_PTR) -
DELETE事务存的是整行原始镜像(用于后续可能的 purge,但崩溃恢复时也会用它做反向INSERT)
所以回滚不是“执行反向SQL”,而是按undo log记录直接还原字段值或删除记录——这要求undo log必须在事务修改数据页前就持久化(即 write-ahead logging 约束)。
为什么有时候崩溃后没回滚完就报错退出
常见于以下情况:
-
innodb_force_recovery > 0被设为非零值,MySQL会跳过 undo 回滚阶段直接启动(此时数据不一致风险极高) -
undo tablespace文件损坏或不可读(比如磁盘坏道导致ibdata1或独立 undo 文件缺失),MySQL无法解析事务链表,报错InnoDB: Cannot initialize trx_rseg - 回滚本身耗时过长(如大事务未提交,undo log 占用数百MB),触发
innodb_rollback_on_timeout或被 kill,但此时 rollback 已部分执行,状态不一致
这类问题不会抛出“回滚失败”的友好提示,而是表现为启动日志中卡在 Starting crash recovery... 后突然中断,或出现 Database was not shut down normally! 之后无下文。
回滚过程中能访问表数据吗
不能。整个崩溃恢复过程是单线程、独占式的:
- MySQL 启动时若检测到异常关闭,会先进入
SRV_FORCE_RECOVERY = 0的严格恢复模式(默认) - 期间所有用户连接被拒绝,
SHOW PROCESSLIST为空,SELECT报错ERROR 2002 (HY000): Can't connect to local MySQL server(实际是 socket 未监听) - 只有完成 redo + undo 两阶段后,才初始化网络线程、打开端口、接受新连接
也就是说,你看到 MySQL 进程起来了但连不上,大概率还在 undo 回滚中——尤其当错误日志里反复出现 Rolling back trx 行时,别急着 kill -9,否则下次启动要重做全部。


















