Mysql 日志模块:redo log(重做日志)和binlog(归档日志)
redo log(InnoDB)
- 如果每一次更新都写磁盘,然后磁盘也要找到对应记录,再更新,整个过程的IO成本和查找成本都很高。
所以InnoDB采取WAL(Write-Ahead Logging)技术,即先写日志,再写磁盘。
- 当有记录需要更新时,InnoDB引擎先记录redo log,再更新内存。然后找适当时机将该操作写到磁盘(比如系统空闲时)。
- redo log的大小是固定的(可配置),比如4个文件,每个文件大小1GB,从第一个文件开始写,写到第四个文件末尾再从头循环。
- 图片来自Mysql45讲02
- write pos是当前写到哪里了,check point是当前擦除到哪里了(擦除之前要把记录更新到数据文件),两者间绿色部分是可以写的空间。如果write pos追上了check point,则不能再执行新的更新操作,得先擦除一些记录。
- 有了redo log,InnoDB可以保证数据库异常重启,之前提交的记录也不会丢失,这个能力称为
crash-safe
。
binlog
- redo log是InnoDB引擎特有的,而binlog属于Server层。
- 最初Mysql自带引擎是MyISAM,但是该引擎没有crash-safe能力,binlog只能用于归档。
两者不同点
- 如上所述,redo log是InnoDB特有,binlog所有引擎都可使用(因为所有引擎共用Server层)。
- redo log是物理日志,记录的是
某数据页做了什么修改
;binlog是逻辑日志,记录的是语句的原始逻辑,比如给ID=2这一行的c字段加1
。 - redo log是循环写的,空间固定会用完(用完后需要擦除记录才能写新的);binlog是追加写,单个文件达到一定大小会切换到下一个,不会覆盖旧的。
更新语句流程
update T set c=c+1 where ID=2;
- 执行器先找到ID=2这一行,ID是主键,引擎用树搜索找到这一行。如果该行所在数据页本来就在内存中,就直接返回给执行器。否则,需要先从磁盘读入内存,再返回。
- 执行器拿到引擎给的行数据,把c字段的值加1,得到新的行数据,再调用引擎接口写入这行新数据。
- 引擎将这行新数据更新到内存中,并同时写入redo log,此时redo log处于prepare状态,然后告知执行器执行完成了,随时可以提交。
- 执行器生成这个操作的binlog,并把binlog写入磁盘。
- 执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改为commit状态,更新完成。
- 图片来自Mysql45讲02,深色代表Server层,浅色代表InnoDB。
两阶段提交
- 为什么必须有两阶段提交?是为了让两份日志之间逻辑一致。
- binlog会记录所有逻辑操作,恢复数据时可以采用A时间点全量数据+A时间点后binlog的方式。
- redo log 和 binlog 都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。
配置
-
innodb_flush_log_at_trx_commit
这个参数设置成 1 的时候,表示每次事务的 redo log 都直接持久化到磁盘,这样可以保证 MySQL 异常重启之后数据不丢失。 -
sync_binlog
这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘,这样可以保证 MySQL 异常重启之后 binlog 不丢失。