3000帧动画图解MySQL为什么需要binlog、redo log和undo log

版权申明:本文为原创文章,转载请注明原文出处

原文链接:https://www.wangheng.online/archives/37862190.html

从一条SQL的执行讲起:

20210602013829

就算是一条简简单单的更新语句,也要经过MySQL的Server层和存储引擎层

InnoDB存储引擎

从上面的SQL执行图可以看出,数据的落盘和日志的记录都与InnoDB存储引擎有着密切的关系。

那存储引擎到底是什么呢?

“引擎”一词来源于发动机,它是发动机中的核心部分,后面引申到软件工程领域,代表的是软件或者系统的核心。

在MySQL中,核心的存储数据的模块被称为存储引擎。

在MySQL中,存储引擎是插件式的,用户可以根据不同的需求去使用不同的存储引擎。

把存储引擎插件化有什么好处呢?

我认为有以下三点好处:

  1. 将Server层与存储引擎解耦,留下扩展点,方便后续的迭代与发展
  2. 让用户有更多的选择,根据不同的需求选择合适的存储引擎
  3. 让MySQL便于结合更多优秀的存储引擎

我这三点推断并不是空穴来风,不信我和你一起回顾一下MySQL的发展史:

  • MySQL最初搭载的存储引擎是自研的只支持简单的查询的MyISAM的前身ISAM

  • 后来与Sleepycat合作研发了Berkeley DB引擎,支持了事务

但尽管如此,MySQL这些自研的存储引擎在很大一部分场景都干不过InnobaseOy公司的InnoDB存储引擎。

怎么办呢?打不过就加入呗。

我有我的SQL技术,你有你的存储技术,我们何不合作共同发展呢?

dac

于是在2001年,MySQL所在的公司和InnobaseOy公司合作,决定在MySQL中集成InnoDB存储引擎。

因为MySQL存储引擎的插件化设计,两个公司合作的非常轻松与愉快,很快就发布了MySQL4.0正式支持功能强大的InnoDB。

虽说不能把MySQL的成功全部归功于存储引擎的插件化设计,但不得不说,存储引擎插件化大大加速了MySQL的发展。

从发展史也可以了解到,InnoDB一开始并不是MySQL的一部分,只是后来被集成到了MySQL里面。这一点很关键,后面还会再次提到。

Buffer Pool

在InnoDB里,有一块非常重要的结构——Buffer Pool。

Buffer Pool是个什么东西呢?

Buffer Pool就是一个缓存磁盘数据的内存空间,将磁盘IO操作转换成内存操作,提高执行效率。

讲白了,就是将磁盘上的数据放到内存中来,有什么操作直接在内存中执行,不需要每次都去磁盘上取数据,然后操作完后再将数据刷到磁盘。

通过一个例子说明,假设要执行3个更新SQL,将age=1这个数据依次更新成2、3、4。

如果没有Buffer Pool,那执行就是这样的:

7

每次更新都需要从磁盘拿数据,修改完了需要刷到磁盘,3次更新一共有6次磁盘IO。

而有了Buffer Pool,执行就成了这样:

8

一共执行了1次IO操作,20次内存操作。

上面这个例子,执行10条SQL,有了Buffer Pool之后,节省了19次的磁盘IO。

当然,Buffer Pool真正的运转流程没有这么简单,具体实现细节和优化技巧还有很多,由于篇幅有限,本文不做详细描述。

我们需要明白的是,Buffer Pool就是将磁盘IO转换成了内存IO,提高了执行效率。

Buffer Pool是提高了效率没错,但是出现了一个问题,Buffer Pool存在于内存中,而只要一断电或者MySQL所在服务器挂掉,内存里面的数据就会全部丢失。

如果断电的时候Buffer Pool的数据还没来得及刷到磁盘,那么这些数据就丢失了。

9

试想一下,如果这些丢失的数据里面有你给女神买包的订单信息,你能不能接受?

反正我是不能接受。

那InnoDB是如何做到数据不会丢失的呢?

今天的第一个日志——redo log登场了。

redo log

从名字上面来看,redo是重做的意思,redo log就是重做日志的意思。

具体来说,就是在修改之后,先将修改后的值记录到磁盘上的redo log中,就算突然断电了,Buffer Pool中的数据全部丢失了,来电的时候也可以根据redo log恢复Buffer Pool,这样既利用到了Buffer Pool的内存高效性,也保证了数据不会丢失。

10

那么问题来了,redo log文件也在磁盘上,数据文件也在磁盘上,都是磁盘操作,何必多次一举?为什么不直接将修改的数据写到数据文件里面去呢?

20190530160425_WPnCfH

一句话回答就是:redo log是磁盘顺序写,数据刷盘是磁盘随机写。磁盘的顺序写比随机写高效的多。

所以啊,这一波先记redo log还是有用的。

在关系数据库中,这种先预写日志后面再将数据刷盘的机制,有一个高大上的专业名词——WAL(Write-ahead logging),翻译成中文就是预写式日志。

面试的时候,你要是满嘴这些高大上的名词,面试官很难不抬头打量你呀。

88b7b2fdaffee627ed2ea65357b30929

言归正传,redo log并不是直接记到磁盘的redo log文件里面,而是先记录在一个叫redo log buffer的地方,redo log buffer和Buffer Pool类似,起到了一个缓冲的作用。

参数innodb_flush_log_at_trx_commit用来控制redo log的刷盘策略,设置不同的值同步redo log的策略也是不一样的。

这个参数 有3个值可以设置:

innodb_flush_log_at_trx_commit = 0:延迟写,延迟刷

这种策略在事务提交时不会向磁盘中同步redo log,这个同步的工作由后台线程定时去做。

这种策略效率是最高的,但是如果事务提交后服务器挂了,后台线程又没有及时将redo log刷新到磁盘,那么该事务对页面的修改会丢失。

17

innodb_flush_log_at_trx_commit = 1:实时写,实时刷

这种策略会在每次事务提交之前,将对应的redo log刷到磁盘中去,理论上只要磁盘不出问题,数据就不会丢失。

18

innodb_flush_log_at_trx_commit = 2:实时写,延迟刷

这种策略在事务提交之前会把redo log写到os cache中,但并不会实时地将日志刷新到磁盘。

这种情况下如果MySQL进程挂了,操作系统没挂的话,操作系统还是会将os cache刷到磁盘,数据不会丢失。

16

但如果MySQL所在的服务器挂掉了,也就是操作系统也挂了,那么os cache也会被清空,数据就丢失了。

19

信我,别整那么花里胡哨的,参数innodb_flush_log_at_trx_commit你就老老实实用默认的1就行了,不丢数据不背锅,非常的香。

undo log

我们都知道,InnoDB是支持事务的,而事务是可以回滚的。

假如一个事务将age=1修改成了age=2,在事务还没有提交的时候,后台线程已经将age=2刷入了磁盘。这个时候,不管是内存还是磁盘上,age都变成了2,如果事务要回滚,找不到修改之前的age=1,无法回滚了。

21

那怎么办呢?

很简单,把修改之前的age=1存起来不就行了吗,回滚的时候根据存起来的age=1回滚就行了。

这个存修改之前的age=1的过程,就叫做记录undo log。

undo翻译成中文是撤销、回滚的意思。事实上,undo log最大的作用就是回滚。

22

undo log默认存在全局表空间里面,你可以简单的理解成undo log也是存在一个MySQL的表里面,插入一条undo log和插入一条普通数据类似。所以,写入undo log的过程中同样也是要写入redo log的。

binlog

undo log记录的是修改之前的值,提供回滚的能力。

redo log记录的是修改之后的值,提供了崩溃恢复的能力。

那binlog是干什么的呢?

binlog记录的是修改之后的值,用于归档。

和redo log日志类似,binlog也有着自己的刷盘策略,通过sync_binlog参数控制:

  • sync_binlog = 0 :每次提交事务前将binlog写入os cache,由操作系统控制什么时候刷到磁盘
  • sync_binlog =1 :采用同步写磁盘的方式来写binlog,不使用os cache来写binlog
  • sync_binlog = N :当每进行n次事务提交之后,调用一次fsync将os cache中的binlog强制刷到磁盘

这个时候你会问了,binlog和redo log都是记录的修改之后的值,这两者有什么区别呢?记录了redo log可以不可以不记binlog呢?

首先看两者的区别:

  • binlog是逻辑日志,记录的是对哪一个表的哪一行做了什么修改;redo log是物理日志,记录的是对哪个数据页中的哪个记录做了什么修改,如果你还不了解数据页,你可以理解成对磁盘上的哪个数据做了修改。
  • binlog是追加写;redo log是循环写。
  • binlog是Server层的日志;redo log是InnoDB的日志。

但说实话,我觉得这些区别并不是redo log不能取代binlog的原因,完全可以调整redo log让他兼容现在redo log和binlog的能力。

我认为不用redo log取代binlog最大的原因是“没必要”。

为什么这么说呢?

第一点,binlog的生态已经建立起来。MySQL系统高可用依赖的就是binlog复制,还有很多公司的数据分析系统,也是依赖的binlog。像阿里巴巴的开源框架Canal,也是基于binlog开发的。

第二点,binlog并不是MySQL的瓶颈,花时间在没有瓶颈的地方没必要。

就像我们常用的Maven,在构建速度上已经被Gradle完爆了,但是我们新建项目的时候还是用着Maven,为什么呢?

也是一样的道理,Maven用的习惯了,构建慢但还没慢到影响项目开发的程度。

总结

需要提前交代清楚的是,因为binlog属于MySQL的Server层,而redo log和undo log是存储引擎InnoDB的日志,所以全文是在MySQL使用了InnoDB存储引擎的基础上展开的。

image-20210616005149115

事实上,这三种日志还有很多细节我没有提到,我花了6000字3000帧动画想要告诉你的只有三个词:恢复、回滚、归档。

而这每个词都对应着一种日志:

  • redo log——恢复
  • undo log——回滚
  • binlog——归档

这三种日志非常重要,是学习MySQL体系的基础,后面关于事务和MVCC的文章都会再次提到这些日志。

好了,今天的文章就到这里了。

感谢你的阅读!我是CoderW,我们下期再见。

参考资料

  • 《MySQL实战45讲》
  • 《从根儿上理解MySQL》
  • 《MySQL技术内幕—InnoDB存储引擎》第2版

3000帧动画图解MySQL为什么需要binlog、redo log和undo log

https://www.wangheng.online/archives/37862190.html

作者

CoderW

发布于

2021-07-10

更新于

2021-11-11

许可协议

评论