Mysql 日志 binlog redolog

binlog

binlog 属于 server 层,所有引擎共有。主要用于主从同步。

它记录逻辑日志,即原始 sql 语句。

redolog

redolog 属于 InnoDB 引擎。

理论上,InnoDB 可以将每一行更新立即写入磁盘数据页,以提高安全性。但这样IO效率太低。

实际上,InnoDB 只在内存数据页更新数据,将更新记录按时间顺序写入 redolog 日志,等待合适时间(系统不忙)再将更新存入数据页。这就是WAL(Write ahead Logging)。写入数据页是随机写。写入 redolog 是顺序写,效率高很多。

为了提高写入效率,redolog 存放的不是逻辑日志,而是物理日志(即某页某位置某修改),因此无法直接提供信息给客户端,需要配合数据页。为了避免 redolog 太大导致太多数据页过时,Mysql 将其设置为环形日志,write_pos 是最新记录的位置,check_point 是已写入数据页(即不再有意义)的位置。如果 write_pos 追上 check_point, Mysql 会停止业务,将 redolog 写入数据页,增加 check_point。

正常情况下,Mysql 会将内存数据页刷新到磁盘,这种情况用不着 redolog。但是假设系统崩溃重启,磁盘数据页可以借助 redolog 实现恢复数据。这就是崩溃恢复。

二阶段提交

InnoDB 提出 redolog 时,binlog 早已出现而且应用广泛。InnoDB 提出二阶段提交来保证 redolog 和 binlog 一致。

对于一个事务,redolog 分二阶段。第一阶段 redolog 写入磁盘,标记 prepare。之后 binlog 写入磁盘。redolog 第二阶段就是确认 binlog 写入磁盘后将 redolog 标记为 commit。至此,事务结束。

无论是 redolog 落盘还是 binlog 落盘期间,系统崩溃重启,redolog 都处于 prepare 阶段,系统会回滚事务。

假如 redolog 第二阶段系统崩溃重启,系统确认 binlog 已经写入磁盘,那么会继续将 redolog 标记为 commit,实现提交事务。

刷脏页性能问题

InnoDB 用 buffer poll 管理内存数据页。如果内存页更新了,但是磁盘页还未更新,此时内存页称为脏页。buffer poll 空间有限,如果需要加载新数据页,那么需要从 buffer poll 移除最久未用的数据页。如果移除脏页,InnoDB 会将脏页写入磁盘(这是正常情况下用不着 redolog 的原因),写入磁盘需要IO资源。如果某个查询需要淘汰大量脏页,会导致事务性能降低。

如果 redolog 空间太小,write_pos 总是追上 check_point。系统会停下来将脏页写入磁盘,使得 check_point 位置的 redolog 失效,推动 check_point 向前。这也会导致性能降低。

为了解决刷脏页性能问题,可以从以下方面入手。

  1. 合理配置IO参数 innodb_io_capacity,让 InnoDB 知道系统IO能力。
  2. 合理配置 redolog 大小,避免系统总是暂停写入脏页。
  3. 关注脏页比例,维持在 75% 以下。

Mysql 对于刷脏页的一个优化是:如果脏页相邻数据页也是脏页,会一起写入磁盘。这可能导致更多脏页被写入磁盘,降低查询效率。在机械硬盘时代这个优化很有效,因此机械硬盘 IOPS 很低,相邻脏页一起写入磁盘可以降低磁盘IO。但是 IOPS 相对高的 SSD 就不需要这项优化。因此平时可以关闭 innodb_flush_neighbors 参数。