讲redolog和binlog之前,先要讲一下一条mysql语句的执行过程。
1、client的写请求到达连接器,连接器负责管理连接、验证权限;
2、然后是分析器,负责复习语法,如果这条语句有执行过,在缓存内,那么就从缓存去写;
3、缓存没有的话,那就到了优化器部分。负责优化sql读写,选择索引;
4、接下来是执行器,负责操作引擎,并返回结果。
接下来就要进入正题,说一下第四步是如何执行的。
redo log
与查询不一样的是,更新流程还涉及两个重要的日志模块,它们正是我们今天要讨论的主角:redo log(重做日志)和bin log(归档日志)。
innodb的更新操作不是直接写入磁盘的,而是先记录日志,也就是redo log,然后等到db没那么忙碌的时候刷到磁盘上。这样保证了mysql的高效写操作。为什么会这么设计呢?因为每次更新操作先要查询,这里有磁盘io,查到后修改数据,刷入磁盘,又是一次io,每次都这样做的话会非常慢,所以就先记录在内存中,然后记录在redo-log中,然后按照策略刷入磁盘。这个技术叫做WAL技术。因为如果只记录在内存中的话,mysql崩溃会导致改动的数据丢失。所以redo log保证了innodb的crash-safe。(注意:mysql是没有redo log的,它是innodb特有的。不过现在mysql大部分情况下都会选用innnodb。)mysql crash后,故障恢复会从redo log中读取数据。
不过redo log是有大小限制的,比如设置了4个1G的redo log,那么第3个写满的时候会覆盖第0个的log文件。
bin log
redo log是在存储引擎层面的日志,负责存储和crash恢复。bin log 是server层的日志。
为什么会有两份日志呢?
因为只依靠bin log是没有crash safe能力的。而且redo log是有大小限制的。
redo log和bin log主要有如下几个区别:
1、所属层不一样,bin log是server层,redo log是存储层的日志,只有innodb才有。
2、redo log有大小限制,是循环写的,而binlog没有这样的限制;
3、redo log是物理日志,记录的是“在某个数据页上做了什么修改”,bin log是逻辑日志,记录的是语句的原始逻辑,比如给id=3的这一行的count做加一操作。
接下来看一下一条update语句的执行过程:
1、首先查到这一行或者多行数据;
2、然后修改数据,写入内存,并将修改写入redo log,此时redo log处于prepare阶段,并告诉执行器可以提交了;
3、返回ok给server的执行器,执行器收到ok后在binlog中记录原始语句,写入磁盘;
4、binlog记录完成后,执行器告诉引擎层,引擎层将对应的redo log从prepare状态改成commit状态。
这就是2PC。
如果我们要将数据库恢复到某一个时刻点,比如本周二,那么找到周二前最近的全量备份,然后执行备份时间到本周二的binlog,得到一个临时的数据库。
可不可以不要做两阶段提交,而改成把这两步操作独立做呢?可以考虑一下如下两个场景(把某一行的count列从0改成1):
1、先做redo log,后写bin log
如果先写redo log成功后,还没写binlog就崩了,此时count列已经变为1,而数据恢复时,用的是bin log,bin log里少了这一个操作,那么就出现用bin log恢复出来的db和当前的db比,出现了不一致。
2、如果先做bin log,然后写redo log
如果写完bin log后,redo log还没写,就崩了,也就是说崩溃时的db是没有count=1这个修改的。而用bin log恢复出来的db里是有这行记录的,那么也出现了不一致。
所以,update时针对redo log和bin log做2PC还是很有必要的。
此外。redo log有个配置项innodb_flush_log_at_trx_commit设置成1,保证每次事务后时redo log都能更新到磁盘。这样就不会出现丢失的现象。
innodb_flush_log_at_trx_commit=0表示每秒写入磁盘。这样有可能最多丢失1秒钟的数据。
设置为2时,表示每次事务后将redo log的缓存写入os buffer,然后是每秒调用fsync()将os buffer中的日志写入到log file on disk。这样的话,如果系统崩了会丢失一秒的数据,如果mysql崩了,则不会丢失数据。
sync_binlog也建议设置成1,表示每次事务后的binlog会持久化到磁盘,这样可以保证mysql异常重启后的binlog不会丢失记录。
最有有个问题,写日志也是写磁盘,直接更新数据也是写磁盘,都是写磁盘,效率不是一样的吗?这样innodb的redo log不是多此一举吗?因为redo log是追加的形式写磁盘,所以效率很高,而修改数据库的数据是随即写,效率就低了很多。