问题

binlog记录更改数据的SQL、redolog重做日志、undolog回滚日志

答案

本文全面解析MySQL三种核心日志的特性、作用及协同工作机制。

1. 三种日志的定位

MySQL日志体系架构:

┌──────────────────────────────────────────────────────────┐
│                      Server层                             │
│  ┌────────────────────────────────────────────────────┐  │
│  │ binlog(二进制日志)                                │  │
│  │ - 记录所有DDL和DML语句                              │  │
│  │ - 用于主从复制和数据恢复                            │  │
│  │ - 所有存储引擎共享                                  │  │
│  └────────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────┘
                           │
┌──────────────────────────────────────────────────────────┐
│                   InnoDB引擎层                            │
│  ┌─────────────────────┐  ┌──────────────────────────┐  │
│  │ redolog(重做日志)  │  │ undolog(回滚日志)       │  │
│  │ - 记录物理页修改     │  │ - 记录修改前的旧值        │  │
│  │ - 保证持久性(D)    │  │ - 保证原子性(A)         │  │
│  │ - 用于崩溃恢复       │  │ - 用于事务回滚和MVCC      │  │
│  └─────────────────────┘  └──────────────────────────┘  │
└──────────────────────────────────────────────────────────┘

2. binlog(二进制日志)

核心特性

名称:binlog(Binary Log)
层次:Server层(MySQL服务器层)
类型:逻辑日志
作用:
  1. 主从复制:主库将binlog发送给从库回放
  2. 数据恢复:基于时间点恢复(PITR)
  3. 数据审计:追踪数据变更历史

记录内容

-- STATEMENT格式:记录SQL语句
BEGIN;
UPDATE account SET balance = balance - 100 WHERE id = 1;
COMMIT;

-- ROW格式:记录行变更
BEGIN;
### UPDATE `db`.`account`
### WHERE @1=1 @2=1000  -- 旧值
### SET   @1=1 @2=900   -- 新值
COMMIT;

-- MIXED格式:自动选择STATEMENT或ROW

写入时机

事务执行流程中的binlog写入:

T1: 事务开始
    └─> 分配binlog cache

T2: 执行SQL语句
    └─> 变更写入binlog cache(内存)

T3: 事务提交(两阶段提交)
    ├─> redolog进入prepare状态
    ├─> binlog cache写入binlog文件(OS缓存)
    ├─> fsync刷盘(根据sync_binlog参数)
    └─> redolog改为commit状态

关键点:binlog在事务提交时才写入文件

文件管理

-- 查看binlog文件列表
SHOW BINARY LOGS;
/*
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.000001 | 177       |
| mysql-bin.000002 | 156428    |
| mysql-bin.000003 | 1073741824|
+------------------+-----------+
*/

-- 查看binlog事件
SHOW BINLOG EVENTS IN 'mysql-bin.000003' LIMIT 10;

-- 删除旧的binlog(保留最近7天)
PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);

-- 配置binlog过期时间(MySQL 8.0)
SET GLOBAL binlog_expire_logs_seconds = 604800;  -- 7天

3. redolog(重做日志)

核心特性

名称:redolog(Redo Log)
层次:InnoDB引擎层
类型:物理日志
作用:
  1. 崩溃恢复:数据库崩溃后重做已提交事务
  2. WAL机制:先写日志,后写数据(提升性能)
  3. 持久性保证:保证事务的ACID特性中的D

记录内容

物理日志记录格式:

"在表空间5的数据页300的偏移量50位置,
 将4字节的值从0x00000064改为0x000000C8"

特点:
- 记录数据页的物理修改
- 记录最小修改单元(页级别)
- 可以快速重做(不需要重新执行SQL逻辑)

写入机制:循环写

redolog循环写结构(ib_logfile):

文件组:4个文件 × 1GB = 4GB
┌─────────────────────────────────────────┐
│ ib_logfile0 (1GB)                       │ ←─┐
├─────────────────────────────────────────┤   │
│ ib_logfile1 (1GB)                       │   │
├─────────────────────────────────────────┤   │ 循环写
│ ib_logfile2 (1GB)                       │   │
├─────────────────────────────────────────┤   │
│ ib_logfile3 (1GB)                       │ ──┘
└─────────────────────────────────────────┘
     ↑                            ↑
 checkpoint                   write pos
     │                            │
 可以擦除的位置              当前写入位置

可用空间 = write pos 到 checkpoint 之间的空间

刷盘策略

# innodb_flush_log_at_trx_commit参数控制
# 0: 每秒写入并刷盘(可能丢失1秒数据)
# 1: 每次事务提交都刷盘(最安全,默认)
# 2: 每次提交写OS缓存,每秒刷盘
innodb_flush_log_at_trx_commit = 1

性能对比:
- 0: 1000+ TPS(高性能,低安全)
- 1: 500 TPS(低性能,高安全)
- 2: 800 TPS(中等性能,中等安全)

4. undolog(回滚日志)

核心特性

名称:undolog(Undo Log)
层次:InnoDB引擎层
类型:逻辑日志
作用:
  1. 事务回滚:记录修改前的旧值,支持ROLLBACK
  2. MVCC:为其他事务提供一致性视图(快照读)
  3. 原子性保证:保证事务的ACID特性中的A

记录内容

-- 场景:UPDATE操作
UPDATE user SET age = 30, name = 'Bob' WHERE id = 1;

-- undolog记录(逻辑日志):
/*
表:user
主键:id = 1
旧值:age = 20, name = 'Alice'
操作类型:UPDATE
事务ID:trx_id = 12345
*/

-- 如果需要回滚:
-- 执行逆操作:UPDATE user SET age = 20, name = 'Alice' WHERE id = 1;

MVCC版本链

数据行的版本链(通过undolog形成):

当前版本(数据页中):
┌─────────────────────────────────────────┐
│ id=1, age=30, trx_id=103, roll_ptr=0x123│
└─────────────────────────────────────────┘
              │
              │ roll_ptr指向undolog
              ↓
历史版本1(undolog):
┌─────────────────────────────────────────┐
│ id=1, age=25, trx_id=102, roll_ptr=0x124│
└─────────────────────────────────────────┘
              │
              ↓
历史版本2(undolog):
┌─────────────────────────────────────────┐
│ id=1, age=20, trx_id=101, roll_ptr=NULL │
└─────────────────────────────────────────┘

不同事务根据Read View读取不同版本

Purge清理机制

-- undolog清理条件:
-- 1. 事务已提交或回滚
-- 2. 没有其他事务的Read View需要该版本

-- 查看undolog堆积
SHOW ENGINE INNODB STATUS\G
-- History list length: 1523  -- undolog数量

-- Purge线程定期清理
-- MySQL 5.6+支持多线程purge
innodb_purge_threads = 4  -- 4个purge线程

-- 清理速度监控
SHOW STATUS LIKE 'Innodb_purge%';

5. 三种日志的协同工作

完整的更新流程

UPDATE语句的完整执行流程:

┌────────────────────────────────────────────────────┐
│ 1. 开始事务,分配事务ID(trx_id)                  │
└────────────────────────────────────────────────────┘
                        ↓
┌────────────────────────────────────────────────────┐
│ 2. 写undolog                                        │
│    - 记录旧值(age=20)                             │
│    - 用于回滚和MVCC                                 │
└────────────────────────────────────────────────────┘
                        ↓
┌────────────────────────────────────────────────────┐
│ 3. 更新Buffer Pool                                  │
│    - 修改内存中的数据页(age=30)                   │
│    - 标记为脏页                                     │
└────────────────────────────────────────────────────┘
                        ↓
┌────────────────────────────────────────────────────┐
│ 4. 写redolog(prepare状态)                         │
│    - 记录物理修改                                   │
│    - 刷盘(根据innodb_flush_log_at_trx_commit)    │
└────────────────────────────────────────────────────┘
                        ↓
┌────────────────────────────────────────────────────┐
│ 5. 写binlog                                         │
│    - 记录逻辑变更(SQL或行)                        │
│    - 刷盘(根据sync_binlog)                        │
└────────────────────────────────────────────────────┘
                        ↓
┌────────────────────────────────────────────────────┐
│ 6. redolog状态改为commit                            │
│    - 事务提交完成                                   │
│    - 释放锁资源                                     │
└────────────────────────────────────────────────────┘
                        ↓
┌────────────────────────────────────────────────────┐
│ 7. 异步刷脏页                                       │
│    - 后台线程将脏页写入磁盘                         │
│    - 推进checkpoint                                 │
└────────────────────────────────────────────────────┘

两阶段提交保证一致性

为什么需要两阶段提交?

场景分析:
┌────────────────────────────────────────┐
│ 如果redolog和binlog分别写入:           │
├────────────────────────────────────────┤
│ Case 1: redolog成功,binlog失败        │
│ 结果:主库有数据,从库没有(不一致)   │
├────────────────────────────────────────┤
│ Case 2: binlog成功,redolog失败        │
│ 结果:主库崩溃恢复后数据丢失,          │
│       但从库有数据(不一致)            │
└────────────────────────────────────────┘

两阶段提交解决方案:
┌────────────────────────────────────────┐
│ Phase 1: Prepare                       │
│ - redolog写入并标记为prepare           │
├────────────────────────────────────────┤
│ Phase 2: Commit                        │
│ - 写binlog                              │
│ - redolog状态改为commit                │
└────────────────────────────────────────┘

崩溃恢复判断:
- redolog=prepare + binlog存在 → 提交事务
- redolog=prepare + binlog不存在 → 回滚事务

6. 在ACID中的作用

ACID特性实现机制:

Atomicity(原子性)
└─> undolog
    - 记录旧值,支持回滚
    - 保证事务要么全部成功,要么全部失败

Consistency(一致性)
└─> binlog + redolog(两阶段提交)
    - 保证主从数据一致
    - 保证binlog和数据文件一致

Isolation(隔离性)
└─> undolog(MVCC)+ 锁
    - undolog提供历史版本,实现快照读
    - 锁机制实现当前读的隔离

Durability(持久性)
└─> redolog
    - 事务提交后,redolog持久化
    - 崩溃后通过redolog恢复已提交事务

7. 性能优化配置

生产环境推荐配置(安全优先)

# binlog配置
log_bin = mysql-bin
binlog_format = ROW  # ROW格式保证数据一致性
sync_binlog = 1  # 每次事务提交都刷盘

# redolog配置
innodb_log_file_size = 2G  # 单个文件2GB
innodb_log_files_in_group = 4  # 4个文件
innodb_flush_log_at_trx_commit = 1  # 每次提交都刷盘

# undolog配置
innodb_undo_tablespaces = 2  # 独立undo表空间
innodb_undo_log_truncate = ON  # 自动收缩

# purge优化
innodb_purge_threads = 4  # 4个purge线程
innodb_max_purge_lag = 0  # 不限制purge滞后

高性能配置(可容忍少量数据丢失)

# 适用于非核心业务、可重建的数据

# binlog配置
sync_binlog = 100  # 每100个事务刷盘一次

# redolog配置
innodb_flush_log_at_trx_commit = 2  # 写OS缓存,每秒刷盘

# 性能提升:约2-3倍TPS
# 风险:最多丢失1秒的事务

8. 监控指标

-- 1. binlog监控
SHOW STATUS LIKE 'Binlog%';
-- Binlog_cache_use: binlog cache使用次数
-- Binlog_cache_disk_use: 溢出到磁盘次数(应该为0)

-- 2. redolog监控
SHOW ENGINE INNODB STATUS\G
-- Log sequence number: 当前LSN
-- Log flushed up to: 已刷盘的LSN
-- Pages flushed up to: 已刷数据页的LSN
-- Last checkpoint at: 最后checkpoint的LSN

-- 3. undolog监控
SHOW ENGINE INNODB STATUS\G
-- History list length: undolog堆积数量(<10000正常)

-- 4. 事务监控
SELECT COUNT(*) FROM information_schema.INNODB_TRX;
-- 当前活跃事务数

9. 常见问题排查

问题1:undolog膨胀

-- 现象
SHOW ENGINE INNODB STATUS\G
-- History list length: 500000  -- 堆积严重

-- 原因排查
SELECT trx_id, trx_started, trx_query
FROM information_schema.INNODB_TRX
ORDER BY trx_started LIMIT 1;
-- 找到最早的事务(长事务)

-- 解决
KILL {thread_id};  -- kill掉长事务

问题2:redolog空间不足

-- 现象:所有写操作阻塞

-- 原因排查
SHOW ENGINE INNODB STATUS\G
-- 查看write pos和checkpoint的位置
-- 如果write pos接近checkpoint,说明空间不足

-- 解决
-- 1. 等待checkpoint推进(刷脏页)
-- 2. 增大redolog文件大小

10. 答题总结

面试时可这样回答:

MySQL有三种核心日志,分工明确,协同工作:

binlog是Server层的逻辑日志,记录所有变更,主要用于主从复制和数据恢复。采用追加写,支持STATEMENT、ROW、MIXED三种格式。

redolog是InnoDB的物理日志,记录数据页的修改,保证事务持久性。采用固定大小的循环写,配合WAL机制实现高性能和crash-safe能力。

undolog也是InnoDB的逻辑日志,记录修改前的旧值,用于事务回滚和MVCC。通过版本链实现快照读,由purge线程异步清理。

协同工作:三种日志通过两阶段提交协议保持一致性,共同实现ACID特性:undolog保证原子性和隔离性,redolog保证持久性,binlog配合redolog保证一致性。

生产配置:推荐双1配置(innodb_flush_log_at_trx_commit=1, sync_binlog=1)+ ROW格式binlog + 多线程purge,确保数据安全和性能平衡。

关键要点

  • 三种日志层次、类型、作用各不相同
  • 两阶段提交保证binlog和redolog一致性
  • undolog支持MVCC,需要及时清理
  • WAL机制是性能优化的核心