[小技巧55]深入解析数据库日志机制:逻辑日志、物理日志与物理逻辑日志在 MySQL InnoDB 中的实现

一、引言:日志是数据库可靠性的基石

在现代数据库系统中,日志(Logging) 是实现 原子性(Atomicity)持久性(Durability) 的核心机制。根据记录内容的抽象层次,数据库日志通常分为三类:

  • 逻辑日志(Logical Logging)
  • 物理日志(Physical Logging)
  • 物理逻辑日志(Physiological Logging)

这三种日志在粒度、语义、恢复方式和适用场景上存在显著差异。

二、三种日志格式详解

1. 逻辑日志(Logical Logging)

  • 定义:记录事务的高层操作语义,如 SQL 语句或行级变更。
  • 特点
    • 与存储结构无关;
    • 可跨版本、跨引擎重放;
    • 适用于复制、逻辑备份。
  • MySQL 实现Binary Log(binlog)
    • STATEMENT 格式:记录原始 SQL;
    • ROW 格式:记录主键 + 列值变更(仍属逻辑层,因不涉及页/偏移)。
    • mixed格式:混合模式

示例(ROW 格式):

text 复制代码
Table_map: test.T
Write_rows: (c1=1, c2='abc')

2. 物理日志(Physical Logging)

  • 定义 :记录底层存储的字节级变化,如"将页 X 偏移 Y 处的 Z 字节从 A 改为 B"。
  • 特点
    • 高度依赖页格式;
    • 恢复时直接 memcpy 覆盖内存;
    • 无法支持页结构演进。
  • MySQL 实现InnoDB 未使用纯物理日志
  • 典型系统:LevelDB WAL、早期 Berkeley DB。

InnoDB 不采用此模式,因其无法兼容压缩页、加密页等高级特性。

3. 物理逻辑日志(Physiological Logging)

  • 定义 :以 物理页为作用域 ,记录 页内的逻辑操作,在页 X 上插入记录 Y。
  • 特点
    • 日志 = <page_id, operation, args>
    • 恢复时调用存储引擎函数重做操作(非字节覆写);
    • 兼顾效率与灵活性。
  • MySQL 实现InnoDB Redo Log

✅ 示例:

text 复制代码
<MLOG_REC_INSERT, space=1, page_no=100, record=(1,'abc'), index_id=PRIMARY>
<MLOG_REC_INSERT, space=1, page_no=200, record=(1,ptr), index_id=key_c1>

三、以一条 INSERT 为例:三种日志的生成对比

假设执行:

sql 复制代码
CREATE TABLE T (c1 INT, c2 VARCHAR(10), KEY(c1)) ENGINE=InnoDB;
INSERT INTO T VALUES (1, 'abc');

插入一行 (1, 'abc'),涉及:

  • 聚簇索引(主键索引,默认基于 rowid 或隐式主键)
  • 二级索引 key_c1

通常至少修改 两个 B+ 树页(聚簇索引页 + 二级索引页)。

1. 逻辑日志(Logical Log)记录情况

  • 记录内容:事务的高层语义

  • 示例

    text 复制代码
    <INSERT, table=T, values=(1, 'abc')>
  • MySQL 对应binlog(ROW 格式)

    text 复制代码
    Table_map: T
    Write_rows: (1, 'abc')
  • 数量1 条(整个 INSERT 操作)

2. 物理逻辑日志(Physiological Log)记录情况

  • 记录内容页 ID + 页内逻辑操作

  • 示例

    text 复制代码
    <MLOG_REC_INSERT, space_id=1, page_no=100, record=(1, 'abc', DB_ROW_ID=...), index_id=PRIMARY>
    <MLOG_REC_INSERT, space_id=1, page_no=200, record=(1, ptr_to_clustered), index_id=key_c1>
  • 特点

    • 每条日志绑定一个 page_no
    • 记录的是 "在页 X 上插入记录 Y",而非字节;
    • 恢复时需调用 InnoDB 的 page_cur_insert_rec_low() 等函数重做。
  • MySQL 对应InnoDB Redo Log

  • 数量≥2 条(每个被修改的页至少一条)

特殊情况

只有聚簇索引(无二级索引):插入数据

InnoDB 内部操作:

  1. 聚簇索引(基于 DB_ROW_ID)中插入一条记录;
  2. 该记录包含:DB_ROW_ID(自动生成)、a=1b='abc'
  3. 此操作只涉及 一个 B+ 树(即聚簇索引树);
  4. 如果插入导致页分裂(page split),还会额外产生:
    • 创建新页的日志(MLOG_PAGE_CREATE);
    • 更新父页指针的日志(如果是非叶子页)。
场景 redo log 条数
插入到已有页(无分裂) 1 条(MLOG_REC_INSERT
插入导致页分裂 ≥2 条(MLOG_PAGE_CREATE + MLOG_REC_INSERT + 可能的父页更新)

即使只有 1 条用户记录插入,也可能因 B+ 树结构维护产生多条 redo log。

3. 物理日志(Physical Log)记录情况

  • 记录内容字节级变更(offset + old/new value)

  • 示例 (针对一个页的多次修改):

    text 复制代码
    <space=1, page=100, offset=24, new_value=0x0005>        // 页头:记录数从 4→5
    <space=1, page=100, offset=16380, new_value=0x0064>    // Slot 数组更新
    <space=1, page=100, offset=200, new_value=...>         // 新记录内容
    <space=1, page=100, offset=prev_rec+2, new_value=...>  // 前一条记录的 next 指针
  • 数量 :每个页可能产生 N 条(N ≥ 3~5 很常见)

  • MySQL 是否使用? 。InnoDB 不使用纯物理日志

对比表

日志类型 记录粒度 生成内容 数量 是否由 InnoDB 生成
逻辑日志 表/行级操作 <INSERT, T, (1,'abc')> 1 条 通过 binlog
物理逻辑日志 页 + 页内逻辑操作 两条页级插入操作,分别对应聚簇索引和二级索引页的插入操作 ≥2 条 InnoDB redo log
物理日志 字节偏移 多条字节修改(页头、slot、指针等) ≥2×N 条 InnoDB 不生成

关键洞察:

InnoDB 跳过纯物理日志 ,直接从逻辑操作生成 physiological redo log,避免了字节日志的冗余与脆弱性。

四、InnoDB 为何选择 Physiological Logging?

优势 说明
支持页格式演进 即使 InnoDB 修改页结构(如引入新字段),只要操作语义不变,旧日志仍可重放
兼容压缩/加密页 日志记录"插入记录",而非"写入偏移",可在解压后页上安全重做
日志体积小 插入一条记录仅需 ~50 字节日志,而非 16KB 页
恢复安全性高 通过调用 B+ 树函数重做,确保页结构一致性(如链表、slot 更新正确)

五、日志协同:Redo Log + Binlog + Doublewrite Buffer

InnoDB 的可靠性依赖三大组件协同:

  • Redo Log(Physiological):保证页级操作可重做;
  • Doublewrite Buffer:保证页本身完整(防 partial page write);
  • Binlog(Logical):保证事务可复制、可 PITR(时间点恢复)。

注意:只有当 innodb_flush_log_at_trx_commit=1sync_binlog=1 时,才能实现真正的 crash-safe replication

六、三种日志核心特性

特性 逻辑日志 物理日志 物理逻辑日志
记录粒度 表/行级 字节级 页 + 页内操作
是否依赖存储结构 部分(需页 ID)
恢复方式 重执行 SQL memcpy 覆写 调用存储引擎函数
日志体积 极大 中等
MySQL 组件 binlog InnoDB redo log
适用场景 复制、备份 简单 KV 存储 崩溃恢复

MySQL 通过 分层日志设计 实现了高可靠与高性能的统一:

  • 逻辑日志(binlog) 面向应用与复制;
  • 物理逻辑日志(redo log) 面向存储引擎崩溃恢复;
  • Doublewrite Buffer 作为最后一道防线,保障页完整性。

七、面试题

  1. Q:MySQL 中哪些日志属于逻辑日志?哪些属于物理逻辑日志?
    A:binlog 是逻辑日志;InnoDB redo log 是物理逻辑日志(physiological logging)。

  2. Q:为什么 InnoDB 不使用纯物理日志?
    A:纯物理日志无法支持页压缩、加密、格式演进,且恢复时缺乏语义校验,易导致页损坏。

  3. Q:一条 INSERT 会产生几条 redo log?为什么?
    A:至少两条------聚簇索引页和每个二级索引页各一条,因为每个 B+ 树页的修改需独立记录。

  4. Q:Physiological logging 如何保证恢复正确性?
    A:恢复时调用 InnoDB 的页管理函数(如 insert/delete)重做操作,确保页内结构(链表、slot 等)一致。

  5. Q:binlog 和 redo log 能否互相替代?
    A:不能。binlog 用于实例级复制,redo log 用于引擎级崩溃恢复,二者目标不同,必须共存。

相关推荐
OceanBase数据库官方博客2 小时前
主流关系型数据库系统缺陷实证研究——OceanBase 校企联合研究
数据库·oceanbase·分布式数据库
打工的小王2 小时前
redis(三)redis持久化和集群(redis版本:5.0.4)
数据库·redis·缓存
Access开发易登软件3 小时前
Access 窗体中实现数字滚动动画:Timer + Easing 的技术实现
运维·数据库·nginx·microsoft·access
心之伊始3 小时前
Redis 持久化机制深度解析(RDB / AOF / 混合持久化)
数据库·redis·bootstrap
马猴烧酒.3 小时前
【JAVA数据传输】Java 数据传输与转换详解笔记
java·数据库·笔记·tomcat·mybatis
Hgfdsaqwr4 小时前
使用Flask快速搭建轻量级Web应用
jvm·数据库·python
ruxshui4 小时前
Python多线程环境下连接对象的线程安全管理规范
开发语言·数据库·python·sql
OceanBase数据库官方博客4 小时前
客户案例|美的以OceanBase为基构建云中立数字化基座破局多云孤岛
数据库·oceanbase·分布式数据库
Mr_Xuhhh4 小时前
MySQL数据表操作全解析:从创建到管理
数据库·sql·oracle