- 本文是笔记类型文章,只有结论没有证明,可用于复习巩固知识,不能用于新知识的学习。如有错误,恳请指正,不胜感谢。
- 转载请于文首标明出处:【MySQL】日志简述 - 掘金 (juejin.cn)
- 文章仍未完工,内容会逐步完善
系列文章:
- 【MySQL】逻辑架构简述 - 掘金 (juejin.cn)
- 【MySQL】索引简述 - 掘金 (juejin.cn)
- 【MySQL】并发,事务与锁简述 - 掘金 (juejin.cn)
- 【MySQL】日志简述 - 掘金 (juejin.cn)
binlog
binlog 称为二进制日志(Binary Log),是 MySQL 服务层的日志,用于记录执行的写操作语句。只有事务提交后才会写 binlog,当事务提交但是数据没有落盘时如果发生宕机,则在重启之后可以通过 mysqlbinlog 和 binlog 重做事务。由于是 MySQL 服务层的日志,因此可以对所有存储引擎提供支持。
由于 MySQL 服务层不负责存储,因此落盘操作由存储引擎控制,因此服务层必须通过 binlog 来保证 DML 执行的持久性。
配置
-
启用与关闭:
可以通过在配置文件中配置
log-bin=[log_file_name]
或者执行SET SQL_LOG_BIN=1
来开启 binlog。删除log-bin
配置项或执行SET SQL_LOG_BIN=0
可以关闭 binlog。 -
写入时机:binlog 的写入时机由
sync_binlog
控制。sync_binlog=0
,MySQL 不控制 binlog 的刷新,由操作系统自行在空闲时刷入磁盘。sync_binlog=N
,N 为正整数,表示每 N 次事务,MySQL 手动将 binlog 刷入磁盘。
因此 sync_binlog=1
可以带来最高的一致性,通常我们也是选择配置为 1。
创建
以下情况会重新创建一个 binlog 文件:
- MySQL 重启。
- 使用
flush log
命令。 - 文件大小超过
max_binlog_size
,max_binlog_size
默认为最大值 1G,最小值为 4k。但是当单次 DML 大小超过 1G 时,由于不会将单次操作拆分为多个文件存储,因此会出现 binlog 大小大于max_binlog_size
的情况。
写入事件
binlog 的写入事件包括:
- STATEMENT:基于 SQL 语句保存。
- ROW:基于行保存。
- MIX:混合模式,一般的语句使用 STATEMENT 格式保存。对于无法完成主从复制操作的语句,如有
date()
函数的语句,则采用 ROW 格式保存。
redo log
redo log 又称重做日志,是 InnoDB 存储引擎的日志,由于 InnoDB 支持事务,redo log 就是用于保证事务执行一致性的。redo log 仅记录事务对哪些数据页做了哪些修改,采用顺序写的方式,因此写入速度非常快,不会对性能造成太大影响。当事务执行提交之后,如果数据还未落盘数据库就宕机了,重启后 InnoDB 会自动根据 redo log 对丢失的事务进行重做。
为什么事务提交时不直接将数据落盘呢?是由于对于数据的修改很多时候都是随机的,而 InnoDB 默认页大小是 16K,因此如果一个修改涉及到多个页,此时需要进行多次随机 IO。修改完之后又需要刷入磁盘,又需要进行多次随机 IO,速度非常慢。因此对于事务的写操作通常都会将修改缓存到内存中,等待空闲时写入。
InnoDB 的 WAL(Write Ahead Log)技术的产物就是 redo log,对于写操作,永远都是日志先行,先写入 redo log 确保一致性之后,再对修改数据进行落盘。
log buffer
redo log 除了在磁盘上的文件实体之外,还在内存中有一块日志缓冲区 log buffer。如果每次进行修改都直接写磁盘的话,效率并不高,因此使用一个缓冲区先在内存中记录写操作,等到多次写操作发生,再一次写入磁盘。通过将随机写优化为顺序写,可以极大提高 redo log 的性能。redo log buffer 有三种落盘策略,由 innodb_flush_log_at_trx_commit
控制:
- 0:按秒写,按秒刷。每秒调用
write()
写入 OS Buffer 并调用flush()
刷入磁盘。 - 1:实时写,实时刷。每次事务提交都调用
write()
写入 OS Buffer 并调用flush()
刷入磁盘。 - 2:实时写,延迟刷。每次事务提交都调用
write()
写入 OS Buffer,但每秒调用flush()
刷入磁盘。
因此采用延迟写策略时系统运行速度最快,但是发生意外会有 1s 的数据丢失。根据使用场景选择即可。
write
和flush
的概念是由于存在 OS Buffer,OS Buffer 是操作系统为了协调内存与磁盘的速度而提供的写缓冲区。因此我们单纯的write()
并无法保证数据即刻落盘,因此 OS 提供flush()
系统调用来触发立刻刷新缓冲。
循环写入
redo log 采用循环写入的模式,当前写入位置有一个 write_pos
指针,当前已落盘的数据日志结尾有一个 check_point
指针,也就是位于安全点之前日志可以被删除,位于安全点之后的数据不可被删除。当 write_pos 与 check_point 相遇时,redo log 空间不够,此时需要立刻将部分数据落盘,后移 check_point 以保证 redo log 的使用。