「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」
前言
继上篇写到《Mysql之Binary Log》,我们了解到Binary Log为mysql server层的逻辑操作日志,那么存储引擎有没有相关的日志呢?答案是有的,接下来我们来介绍一下我们平时最常用的InnoDB存储引擎的Redo Log物理操作日志。
为啥需要Redo Log?
其实很多小明会问:
- mysql server层不是已经有了同用的逻辑操作日志吗?为啥InnoDB还要搞个Redo Log日志呢?Binary Log和Redo Log日志有啥区别?
- Binary Log和RedoLog日志属于Mysql不同的层,一个事务中它们的执行顺序是怎样的?
- 其中一个Log提交了另外一个Log执行发生异常是怎么处理的? 等等吧,等介绍了Redo Log和Binary Log的执行流程后还有更多的小明会问,笔者这里会尽量全部介绍到。
首先我们还是回答第一个问题,在回答前我们可以先回顾下Binary Log做了啥?Mysql要保证高稳定还要做啥?
回顾Binary Log
- 对数据进行恢复
- 进行分布式复制,如主从
如果不考虑分布式,只考虑单机情况,Binary Log能对已落盘的数据丢失后进行恢复,那么在数据已经提交但是还没落盘准备落盘的时候crash重启了呢?其实Redo Log的功能之一就是crash-safe。
下面我们开始介绍它。
简介
- Redo Log是Mysql存储引擎层的日志,属于InnoDB存储引擎。
- Redo Log是物理日志,记录对磁盘某个数据页做了啥修改。
- Redo Log符合WAL(Write-Ahead Logging)设计,先写日志再落盘。有了日志之后就不怕数据丢失,所以不用每个事务有CUD操作都进行一次IO,有了日志之后就可以批量IO减少IO和查找成本提高效率。(虽然写日志也有IO但是日志文件相对固定并连续IO和查找成本小的多)
或许你已经发现了,Redo Log只是落盘前数据修改的缓存,等数据落盘后它可以不用存在。下面为Redo Log的逻辑结构,可以配置4个文件循环使用:
- write pos为当前记录的位置,往后循环在check point之间的都是可以写入日志的。如果write pos快要追上check point了就会提前落盘保证充足空间。
- check point为当前要擦除的位置,往后循环在write pos之间的都是可以擦除落盘的。
Binary Log和Redo Log日志有啥区别
- Redo Log是InnoDB存储引擎特有的;Binary Log是Mysql的Server层实现的,所有引擎都可以使用。
- Redo Log是物理日志,记录对磁盘某个数据页做了啥修改;Binary Log是逻辑日志,记录更新的语句,语句以描述修改的“事件”的形式存储。
- Redo Log相当于落盘前数据修改的缓存,空间循环利用。Binary Log是追加的形式。
两阶提交
下面我们使用一个update来描述下在一个事务中Binary Log和Redo Log是以怎样的顺序执行的,正好回答上面 “为啥需要Redo Log?”章节 中的第二个问题。
- 根据ID=2查询数据行,会根据主键索引树拿到该行在磁盘的地址,不过不会直接去磁盘读取会查看内存中是否该行所在的数据页,有的话就直接返回执行器,没有就从磁盘读取数据页。
- 执行器拿到该行数据进行更新,得到一行新的数据,再调用引擎接口写入这行数据。
- 引擎将这行新数据更新到内存中,同时将这个update需要更新的数据页操作记录到Redo Log中,此时Redo Log处于prepare阶段。然后告诉执行器执行完成了,随时可以提交事务。
- 执行器生成这个update的逻辑操作记录存放于Binary Log中。
- 执行器调用引擎的提交事务接口,引擎把刚刚写入的Redo Log的状态改成commit,更新完成。
这里你已经发现了先记录Redo Log再记录Binary Log,这是因为引擎属于更底层,那么为啥RedoLog分成了两阶段呢?
必要性
-
还是按照先写Redo Log再写Binary Log的顺序,假设写完Redo Log发生了crash了Bianry Log还没来得及写,要怎么回滚Redo Log?调用存储引擎相关接口抹除吗?如果你是开发人员你会设计出这样的接口吗?答案是肯定不会,首先crash是异常情况极少发生其次该接口不能随意调用。
-
如果先写Binary Log再写Redo Log,假设写完Binary Log发生了crash了Redo Log写不了了内存的数据可能都不存在了,只能使用Binary Log进行还原再写入Redo Log,这个效率比较低也比较耗时,如果有从库可能从库使用Binary Log可能都已经同步好了数据了。
所以为啥不设置一个标识来做二阶段提交呢?那么标识设置在Binary Log还是Redo Log呢?还记得我们前面讲的吗,Redo Log只是落盘前数据修改的缓存,影响范围较小并且数据量相对Binary Log小,所以在Redo Log上做标识比较妥当。
两阶段提交异常情况
上面已经提到过Redo Log和Binary Log使用的是两阶段提交,下面我们讨论下中出现crash异常重启时mysql是怎么处理的。
如果发生在“时刻A”上mysql是怎么处理的呢?时刻A Redo Log还处于prepare状态,Binary Log还没开始写,发生了crash异常重启,如果恢复要怎么恢复呢?Binary Log该怎么写入呢?答案是不能,因为语句已经找不到了事务只能回滚了。
如果发生在“时刻B”上mysql是怎么处理的呢?时刻B Redo Log还处于prepare状态,Binary Log已经开始写,发生了crash异常重启,如果要恢复要怎么恢复呢?这需要判断Binary Log是否完整。完整的话就会恢复。
怎么判断Binary Log是否是完整的
一个事务的Binary Log是有完整的格式的:
- statement格式的最后会有COMMINT;
- row格式的会有一个XID event;
Binary Log完整的话恢复后怎么把Redo Log置为commit状态
Binary Log和Redo Log有个公共的数据字段-XID。可以顺序扫描出处于prepare状态的Redo Log拿到XID去Binary Log中寻找就能关联上进行恢复。
使用
- innodb_flush_log_at_trx_commit:这个参数设置成1的时候,表示每次事务的redo log都直接持久化到磁盘。这个参数我建议你设置成1,这样可以保证 MySQL异常重启之后数据不丢失。