Redolog(引擎层-InnoDB)
InnoDB引擎持有的,只能被InnoDB引擎使用,是物理日志,记录的是“在某个数据页上做了什么修改”,并且是循环写的,大小固定,用完之后会覆盖原来的内容(write pos、check point)。InnoDB会定时把redo log的内容更新到磁盘中。
修改Redo log的大小和数量
- 修改日志缓冲大小:在my.cnf中修改
innodb_log_buffer_size,默认为16M - 修改日志大小:在my.cnf中修改
[innodb_log_file_size](https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_log_file_size) - 修改日志数量:在my.cnf中修改
[innodb_log_files_in_group](https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_log_files_in_group)
binlog(Server-MySQL)
属于Server层,可以被所有引擎使用,是归档日志,binlog是逻辑日志记录的是这个语句的原始逻辑,并且是追加写的,写完一个再写一个(写到一定大小),不会覆盖以前的日志。
日志刷盘控制
redo log:innodb_flush_log_at_trx_commit,当设置为1时,表示每次事务的redo log都直接持久化到磁盘,这个参数建议设置为1,可以保证MySQL异常重启之后数据不丢失
binlog:sync_binlog,这个参数设置成1的时候,表示每次事务的binlog都持久化到磁盘,这个参数也建议设置成1,这样可以保证MySQL异常重启后binlog不丢失
两个日志的作用
最开始没有InnoDB引擎的时候,MySQL只有binlog,binlog又不能保证crash-safe,只能用于归档;而有了InnoDB引擎实现了能够保证crash-safe的redo log,所以两个日志各有用处,相互独立,缺一不可。
两阶段提交
假设我们现在执行一条update语句:update T set c=c+1 where ID=1,给ID=1这行记录的c字段+1的操作,MySQL内部的流程如下:

通过流程图我们了解到update过程中,redo log有两个状态,分别为事务未提交状态prepare和事务提交后的commit状态,将redo log的写入拆成这两个步骤就是“两阶段提交”。
为什么需要“两阶段提交”?
由于binlog和redo log分别属于Server层和引擎层,是两个独立的东西,如果不使用“两阶段提交”会出现数据不一致的情况:
update语句大致流程分为 1.prepare -> 2. 写binlog -> 3. commit。
- 假如在第2步之前崩溃,MySQL重启之后读取redo log发现有prepare的记录,但是没有写binlog的记录,因此会将此事务回滚,redo log和binlog数据一致,当使用binlog进行数据恢复没有问题
- 假如在第3步之前崩溃,MySQL重启之后读取redo log发现有prepare的记录,并且已经写了binlog了,因此会将此事务提交,将redo log记录状态修改为commit,当使用binlog进行数据恢复没有问题
“两阶段提交”类似分布式系统中,系统与系统之间保证数据一致性的方式。
问题
binlog能去掉吗?
不能:一个原因是,redolog只有InnoDB有,别的引擎没有,如果把binlog去掉的话,如果不使用InnoDB了,没办法保证crash-safe;
另一个原因是,redolog是循环写的,不持久保存,binlog的“归档”这个功能,redolog是不具备的。
redo log怎么和binlog关联起来的?
它们有一个共同的字段 XID,崩溃恢复的时候,会按顺序扫描redo log,如果碰到既有prepare,又有commit的redolog,就直接提交;如果碰到只有prepare,而没有commit的redo log,就拿着XID去binlog找对应的事务,如果事务的完整的,就提交,如果不是就不提交。
怎么判断binlog是否是完整的?
一个事务的binlog是有完整格式的:
- statement:最后会有COMMIT;
- row:最后会有一个XID event;
处于prepare阶段的redo log加上完整的binlog,重启就能恢复,MySQL为什么要这么设计?
如果有主从设计,binlog写完整后,会同步到从库,此时MySQL异常宕机,如果不通过redo log+binlog在主库进行崩溃恢复,那么就会出现主从数据不一致能不能写完redolog后再写binlog,因为崩溃恢复的时候也需要两个日志完整才可以,这样是否可行?
两阶段提交是经典的分布式系统问题,并不是MySQL独有的,对于InnoDB引擎来说,如果redo log写完了直接提交,然后binlog写入的时候失败了,InnoDB又回滚不了,数据和binlog日志就不一致了。
两阶段提交就是为了当所有人都ok的时候,再一起提交。
为什么不能只用binlog来支持崩溃恢复?
不能,因为binlog是逻辑日志,无法确定哪一个事务是已经提交的,可能会重复执行,也可能会漏执行。
能不能只用redo log?
不能,因为redo log是物理日志,并且redo log是循环写,会导致历史日志丢失,起不到binlog的作用,binlog也用来作为其他系统同步数据的一种手段存在,因此不能替代。
redo log一般设置多大?
redo log太小的话,会导致很快就被写满,然后不得不强行刷redo log,这样WAL机制的能力就发挥不出来了。
正常运行中的实例,数据写入后的最终落盘,是从redo log更新过来的还是从buffer pool更新过来的?
由于redo log不会记录数据页的完整数据,所以它没有能力自己去更新磁盘的数据页。
- 如果是正常运行的实例,数据页被修改以后,和磁盘的数据页不一致被称为脏页,最终数据落盘,就是把内存中的数据页写盘,这个过程甚至与redo log毫无关系
- 在崩溃恢复场景中,InnoDB如果判断到一个数据页可能在崩溃恢复的时候丢失了更新,就会将它读到内存,然后让redo log更新内存的内容。更新完成后,内存页变成脏页,就变成了第一种情况
