0%

双写机制

InnoDB 双写机制(Doublewrite):解决部分写失效的关键

InnoDB 的双写机制(Doublewrite)是保障数据页完整性的核心技术,专门用于解决数据库宕机时可能出现的部分写失效问题。理解其原理和作用,有助于深入掌握 InnoDB 的数据可靠性设计。

问题背景:部分写失效的风险

当 InnoDB 刷新缓冲池中的脏页(已修改但未写入磁盘的页)到磁盘时,若发生宕机(如断电),可能出现页只写入部分数据的情况(即 “部分写失效”)。例如:

  • 一个 16KB 的数据页,仅写入前 4KB 时宕机,导致该页损坏。

此时,即使有重做日志(Redo Log)也无法恢复:

  • 重做日志记录的是对页的物理修改操作(如 “在偏移量 100 处写入值 A”),但损坏的页无法作为重做的基础,重做操作会因页结构异常而失效。

双写机制的原理:双重保障数据页完整性

双写机制通过 “先写备份,再写原页” 的方式,确保即使原页写入失败,也能通过备份恢复完整页。

双写的组成部分

双写机制包含两个关键部分:

  • 内存中的 doublewrite buffer:大小为 2MB,临时存放待刷新的脏页数据。
  • 磁盘上的 doublewrite 区域:位于 InnoDB 共享表空间(如 ibdata1)中,连续占用 128 个页(128 × 16KB = 2MB),用于持久化存储 doublewrite buffer 中的数据。

双写的工作流程

当缓冲池中的脏页需要刷新到磁盘时,流程如下:

  1. 复制脏页到 doublewrite buffer:通过 memcpy 函数将脏页数据从缓冲池复制到内存中的 doublewrite buffer(无 IO 操作,纯内存复制)。
  2. 写入 doublewrite 磁盘区域:将 doublewrite buffer 中的数据分两次写入磁盘上的 doublewrite 区域(每次写入 1MB,共 2MB),并调用 fsync 强制同步到磁盘(确保数据持久化)。
  3. 写入原数据页:将 doublewrite buffer 中的数据写入实际的数据文件(表空间中的原页位置)。
  4. 完成刷新:原页写入成功后,释放 doublewrite buffer 和磁盘区域中的对应数据。

宕机恢复逻辑

若在步骤 3(写入原数据页)时宕机导致部分写失效,恢复过程如下:

  1. 数据库重启时,检查磁盘上的 doublewrite 区域。
  2. 对比 doublewrite 区域中的备份页与原数据页:
    • 若原页完整(校验和匹配),无需处理。
    • 若原页损坏(校验和不匹配),用 doublewrite 区域中的备份页覆盖原页,再通过重做日志恢复该页的最新修改。

双写机制的性能影响与监控

性能影响

双写机制本质上是 “写两次数据”(先写备份,再写原页),理论上会增加 IO 开销,但实际影响较小:

  • doublewrite 磁盘区域是连续的,两次 1MB 写入为顺序 IO,效率较高。
  • 现代磁盘和文件系统对顺序写的优化较好,且 fsync 调用次数被合并(一次 fsync 即可同步 2MB 数据)。

监控双写状态

通过 show global status 查看双写的运行指标:

1
show global status like 'innodb_db%';

输出示例:

1
2
3
4
5
6
+-------------------------+----------+
| Variable_name | Value |
+-------------------------+----------+
| Innodb_dblwr_pages_written | 98848391 | -- 写入的双写页数
| Innodb_dblwr_writes | 9607931 | -- 双写操作次数(每次可写入多个页)
+-------------------------+----------+
  • Innodb_dblwr_pages_written:累计写入 doublewrite 区域的总页数。
  • Innodb_dblwr_writes:累计双写操作次数(每次操作可写入多个页,通常每次写入 128 页)。

通过两者的比值可判断单次双写的平均页数(如 98848391 ÷ 9607931 ≈ 10.3 页 / 次),比值越高说明批量写入效率越好。

双写机制的禁用与适用场景

双写机制默认启用,不建议在生产环境禁用。但在以下特殊场景可考虑临时禁用(需承担数据风险):

  • 测试环境,追求极致性能且可接受数据丢失。
  • 使用具有原子写(Atomic Write)能力的存储设备(如某些 SSD),其硬件层面可保证页写入的完整性,无需双写保护。

禁用方式:在 my.cnf 中添加 innodb_doublewrite = 0,重启数据库生效

欢迎关注我的其它发布渠道