Mysql-WAL
“In computer science, write-ahead logging (WAL) is a family of techniques for providing atomicity and durability (two of the ACID properties) in database systems.”——维基百科
Introduction
在计算机领域,WAL(Write-ahead logging,预写式日志)是数据库系统提供原子性和持久化的一系列技术。
binlog: 记录数据库执行的写入操作,用于主从同步和数据恢复。
redo log: 实现持久性,在执行实际操作前,先将操作写入 redo log。即便系统意外宕机,也能从 redo log 中恢复未执行完操作。
undo log:实现原子性,可以通过 undo log 撤销执行完的部分操作
binlog vs redolog
| redo log | binlog | |
|---|---|---|
| 文件组织 | 循环写,文件大小固定 | 追加写,文件大小可以设置 | 
| 实现方式 | innodb 引擎实现 | server 层实现,所有引擎都有 | 
| 使用场景 | 崩溃恢复 crash-safe | 主从复制和数据恢复 | 
binlog
binlog用于记录数据库执行的写入性操作, 是一种逻辑日志。
- 逻辑日志:可以简单理解为记录的SQL语句
 - 物理日志:因为 mysql 数据最终保存在磁盘页上,物理日志记录的是数据页的变更。
 
binlog 是追加写入文件,可以设置单个 binlog 文件的大小,当达到最大时,生成新的文件来保存日志。
binlog使用场景
- 主从复制:在Master端开启binlog,然后将binlog发送到slave端,Slave端重放 binlog 从而实现主从一致。
 - 数据恢复:通过使用mysqlbinlog工具来恢复数据。
 
binlog刷盘时间
对于InnoDB存储引擎而言,只有在事务提交时才会记录binlog,此时记录还在内存中。
Mysql通过sync_binlog参数控制binlog的刷盘时机:
- 0: 由系统自行判断何时写入磁盘
 - 1: 每次commit时将binlog写入磁盘
 - N: 每N个事务,才会将binlog 写入磁盘
 
总结:sync_binlog 参数为1时最安全,这也是 MySQL5.7.7之后版本的默认值。实际中可以适当调整,以降低fsync频率,牺牲部分一致性来换取更好的性能。
binlog日志格式
在 MySQL 5.7.7之前,默认的格式是STATEMENT,MySQL 5.7.7之后,默认值是ROW。日志格式通过binlog-format指定。
- **STATMENT:**基于SQL语句的复制(statement-based replication, SBR),每一条会修改数据的sql语句会记录到binlog中。
优点:不需要记录每一行的变化,减少了binlog日志量,节约了IO, 从而提高了性能;
缺点:在某些情况下会导致主从数据不一致,比如执行sysdate()、sleep()等。 - ROW: 基于行的复制(row-based replication, RBR),不记录每条sql语句的上下文信息,仅需记录哪条数据被修改了。
优点:不会出现某些特定情况下的存储过程、或function、或trigger的调用和触发无法被正确复制的问题;
缺点:会产生大量的日志,尤其是alter table的时候会让日志暴涨 - MIXED: 基于STATMENT和ROW两种模式的混合复制(mixed-based replication, MBR),一般的复制使用STATEMENT模式保存binlog,对于STATEMENT模式无法复制的操作使用ROW模式保存binlog
 
redo log
数据库需要实现持久性: 只要事务成功提交,那么对数据库的修改就会永久性的保存下来。
因此为了实现持久性,最直接的办法就是每次提交成功,就直接将数据页的变动更新到磁盘上去,但这样频繁的磁盘操作严重影响性能。因此提出了 redo log, 用于记录事务对数据页做了哪些修改。
redo log 包括了
- 内存中的 redo log buffer
 - 磁盘中的 redo log file
 
每当 Mysql 执行一条 DML(数据库操作语言)语句,就将计入写入 redo log buffer,后续批量将 buffer 通过 fsync()刷入磁盘中的 redo log file。
在计算机操作系统中,用户空间(user space)下的缓冲区数据一般情况下是无法直接写入磁盘的,中间必须经过操作系统内核空间(kernel space)缓冲区(OS Buffer)。因此,redo log buffer写入redo log file实际上是先写入OS Buffer,然后再通过系统调用fsync()将其刷到redo log file中,过程如下:
redo log 刷盘时机
| 参数值 | 含义 | 
|---|---|
| 0(延迟写) | 事务提交时不会将redo log buffer中日志写入到os buffer,而是每秒写入os buffer并调用fsync()写入到redo log file中。也就是说设置为0时是(大约)每秒刷新写入到磁盘中的,当系统崩溃,会丢失1秒钟的数据。 | 
| 1(实时写,实时刷) | 事务每次提交都会将redo log buffer中的日志写入os buffer并调用fsync()刷到redo log file中。这种方式即使系统崩溃也不会丢失任何数据,但是因为每次提交都写入磁盘,IO的性能较差。 | 
| 2(实时写,延迟刷) | 每次提交都仅写入到os buffer,然后是每秒调用fsync()将os buffer中的日志写入到redo log file。 | 
redo log 记录形式
redo log 采用了循环写的机制,不同 binlog 的AOF机制。当写到结尾时,会回到开头循环写日志。
- write pos: redo log 当前写入位置
 - check point: 已经被持久化的 redo log的位置
 
顺时针来看,check point-> write pos 之间的 redo log 即为尚未被执行的 redo log。
write pos-> check point 之间的则是已经被执行过,数据变动已经落盘的 redo log,可以理解为空的。
当 write pos追上 check point 时,会先推动 check point 向前移动,释放出位置用于记录新的日志。
redo log 与宕机恢复
当启动 innodb 时,总会进行恢复操作,从 redo log file 文件中恢复出尚未落盘的数据变动。