1、MySQL的日志分类
MySQL的结构大体可以分成Server层和存储引擎层,Server层负责语法分析和逻辑处理,而存储引擎则负责数据的读取与写入。而日志也相对可以分两种,其中Server层的日志是固定的,而存储引擎有多种,有的存储引擎不具备日志,本次探讨的是使用Innodb存储引擎时的日志使用情况。
- binlog:又名归档日志,它是Server层的日志,用于记录逻辑修改,具体是记录修改了那一条记录的哪些字段;一共有两种模式,statement 格式的话是记sql语句, row格式会记录行的内容,记两条,更新前和更新后都有。
- redo log:Innodb存储引擎特有日志,用于记录在哪些页上做了哪些修改,等到空闲时再将这些修改真正写入到磁盘中的表中。这是因为对磁盘中的数据进行修改需要先进行搜索,再进行写入的IO成本较大,如果系统繁忙,那么对系统的整体响应速度会有影响,而选择先写到redo日志,等到空闲再进行实际写入,由于redo log是顺序写入磁盘,相比于实际更新磁盘表数据,会省时很多。
2、两种日志的区别
(1)首先是应用层次不同
- redo log是Innodb所用日志,属于存储引擎层的物理日志。
- bin log则是Server层使用的逻辑日志
(2)其次是写入模式不同
- redo log是使用固定大小的空间,可以通过擦除已经真正同步到数据库表的日志记录来进行空间的循环利用;方法是使用write pos和check point两个指针进行写入和擦除,当写入指针wirte pos追上擦除指针check point时,则说明日志已满,需要将日志中的修改内容同步到数据库表中
- binlog是使用追加的方式写入,也即不会对空间重复利用,而是在日志尾部进行直接添加
(3)再者是功用不同
- redo log是Innodb用来暂时记录修改内容的,一方面可以在系统繁忙时减轻系统压力,另一方面记录在redo log中的修改内容可以在系统崩溃后进行自动恢复
- binlog则是用于对数据库进行回档,通常于数据库备份搭配使用。假设昨天中午错误删除了一个表,而昨天晚上有进行备份,那么就可以根据binlog日志将昨天晚上的备份恢复到昨天中午的状态,再将备份库数据按需同步到原库。而redo log由于会对日志内容进行擦除,因此做不到这一点。
3、日志的实际写入流程
假设当前执行了一条update语句,修改了一个表中主键字段id = 1的记录,那么将会发生以下流程:
- 1、Server层中的执行器调用引擎接口查找该记录,引擎根据b+tree树查找该记录,若存在缓存中则直接返回,不存在则进行IO后返回
- 2、执行器对该记录进行修改,然后调用引擎接口写入新数据
- 3、引擎拿到更新后的新行后,先将新行更新到内存,再将更新内容生成一条redo log,并写入到磁盘日志文件当中。此时这条redo log处于prepare状态,然后告知执行器执行完成
- 4、执行器得知写入完成后,会为该操作记录在binlog当中
- 5、当事务提交时,引擎会之前生成一条commit状态的redo log,更新完成
- 流程图如下:黑色边框是Server层的执行器,红色边框是引擎层
4、redo log两阶段提交的作用
在第三点中,redo log的写入实际上有两个步骤,即prepare状态和commit状态,是什么原因呢?
-
实际上是为了保证两个日志的数据一致,由于两个日志是写入过程是独立的,倘若redo log只有一个阶段,那么redo log 和 binlog 中一个日志写入成功而另外一个日志写入不成功,就会发生数据不一致的问题,以上面回档恢复的例子入手,我们对两个日志前后顺序的不同进行问题分析:
-
- 先写redo log再写binlog,倘若在写binlog过程中系统崩溃,由于存在自我恢复机制,因此redo log会将修改后的结果写入到原库中,而binlog则不会有该修改记录,而我们在对备份库进行回档恢复时使用的是binlog恢复,就会使备份库比原库少去一部分内容
-
- 先写binlog再写redo log,倘若写redo log发生崩溃,那么binlog的修改内容就不会应用到原库当中,而备份库恢复时根据binlog则会将该部分内容重新进行写入,造成备份库和原库数据不一致
-
而当采用了两阶段提交时,则流程如下:
-
- 假如在一阶段系统崩溃,则由于prepare与binlog不一致,会进行回滚
-
- 假如在二阶段系统崩溃,则由于prepare与binlog一致,会进行提交,也即commit
也就是说,两阶段提交可以使得redo log与binlog始终保持一致。在进行回档恢复时,不会出现binlog有记录但是原库中没有应用修改,或是binlog没有记录而原库应用了修改,从而使得按照binlog恢复的备份库与原库数据一致!