3000帧动画图解MySQL为什么需要binlog、redo log和undo log
版权申明:本文为原创文章,转载请注明原文出处
从一条SQL的执行讲起:
就算是一条简简单单的更新语句,也要经过MySQL的Server层和存储引擎层
InnoDB存储引擎
从上面的SQL执行图可以看出,数据的落盘和日志的记录都与InnoDB存储引擎有着密切的关系。
那存储引擎到底是什么呢?
“引擎”一词来源于发动机,它是发动机中的核心部分,后面引申到软件工程领域,代表的是软件或者系统的核心。
在MySQL中,核心的存储数据的模块被称为存储引擎。
在MySQL中,存储引擎是插件式的,用户可以根据不同的需求去使用不同的存储引擎。
把存储引擎插件化有什么好处呢?
我认为有以下三点好处:
- 将Server层与存储引擎解耦,留下扩展点,方便后续的迭代与发展
- 让用户有更多的选择,根据不同的需求选择合适的存储引擎
- 让MySQL便于结合更多优秀的存储引擎
我这三点推断并不是空穴来风,不信我和你一起回顾一下MySQL的发展史:
MySQL最初搭载的存储引擎是自研的只支持简单的查询的MyISAM的前身ISAM
后来与Sleepycat合作研发了Berkeley DB引擎,支持了事务
但尽管如此,MySQL这些自研的存储引擎在很大一部分场景都干不过InnobaseOy公司的InnoDB存储引擎。
怎么办呢?打不过就加入呗。
我有我的SQL技术,你有你的存储技术,我们何不合作共同发展呢?
于是在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,那执行就是这样的:
每次更新都需要从磁盘拿数据,修改完了需要刷到磁盘,3次更新一共有6次磁盘IO。
而有了Buffer Pool,执行就成了这样:
一共执行了1次IO操作,20次内存操作。
上面这个例子,执行10条SQL,有了Buffer Pool之后,节省了19次的磁盘IO。
当然,Buffer Pool真正的运转流程没有这么简单,具体实现细节和优化技巧还有很多,由于篇幅有限,本文不做详细描述。
我们需要明白的是,Buffer Pool就是将磁盘IO转换成了内存IO,提高了执行效率。
Buffer Pool是提高了效率没错,但是出现了一个问题,Buffer Pool存在于内存中,而只要一断电或者MySQL所在服务器挂掉,内存里面的数据就会全部丢失。
如果断电的时候Buffer Pool的数据还没来得及刷到磁盘,那么这些数据就丢失了。
试想一下,如果这些丢失的数据里面有你给女神买包的订单信息,你能不能接受?
反正我是不能接受。
那InnoDB是如何做到数据不会丢失的呢?
今天的第一个日志——redo log登场了。
redo log
从名字上面来看,redo是重做的意思,redo log就是重做日志的意思。
具体来说,就是在修改之后,先将修改后的值记录到磁盘上的redo log中,就算突然断电了,Buffer Pool中的数据全部丢失了,来电的时候也可以根据redo log恢复Buffer Pool,这样既利用到了Buffer Pool的内存高效性,也保证了数据不会丢失。
那么问题来了,redo log文件也在磁盘上,数据文件也在磁盘上,都是磁盘操作,何必多次一举?为什么不直接将修改的数据写到数据文件里面去呢?
一句话回答就是:redo log是磁盘顺序写,数据刷盘是磁盘随机写。磁盘的顺序写比随机写高效的多。
所以啊,这一波先记redo log还是有用的。
在关系数据库中,这种先预写日志后面再将数据刷盘的机制,有一个高大上的专业名词——WAL(Write-ahead logging),翻译成中文就是预写式日志。
面试的时候,你要是满嘴这些高大上的名词,面试官很难不抬头打量你呀。
言归正传,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刷新到磁盘,那么该事务对页面的修改会丢失。
innodb_flush_log_at_trx_commit = 1:实时写,实时刷
这种策略会在每次事务提交之前,将对应的redo log刷到磁盘中去,理论上只要磁盘不出问题,数据就不会丢失。
innodb_flush_log_at_trx_commit = 2:实时写,延迟刷
这种策略在事务提交之前会把redo log写到os cache中,但并不会实时地将日志刷新到磁盘。
这种情况下如果MySQL进程挂了,操作系统没挂的话,操作系统还是会将os cache刷到磁盘,数据不会丢失。
但如果MySQL所在的服务器挂掉了,也就是操作系统也挂了,那么os cache也会被清空,数据就丢失了。
信我,别整那么花里胡哨的,参数innodb_flush_log_at_trx_commit你就老老实实用默认的1就行了,不丢数据不背锅,非常的香。
undo log
我们都知道,InnoDB是支持事务的,而事务是可以回滚的。
假如一个事务将age=1修改成了age=2,在事务还没有提交的时候,后台线程已经将age=2刷入了磁盘。这个时候,不管是内存还是磁盘上,age都变成了2,如果事务要回滚,找不到修改之前的age=1,无法回滚了。
那怎么办呢?
很简单,把修改之前的age=1存起来不就行了吗,回滚的时候根据存起来的age=1回滚就行了。
这个存修改之前的age=1的过程,就叫做记录undo log。
undo翻译成中文是撤销、回滚的意思。事实上,undo log最大的作用就是回滚。
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存储引擎的基础上展开的。
事实上,这三种日志还有很多细节我没有提到,我花了6000字3000帧动画想要告诉你的只有三个词:恢复、回滚、归档。
而这每个词都对应着一种日志:
- redo log——恢复
- undo log——回滚
- binlog——归档
这三种日志非常重要,是学习MySQL体系的基础,后面关于事务和MVCC的文章都会再次提到这些日志。
好了,今天的文章就到这里了。
感谢你的阅读!我是CoderW,我们下期再见。
参考资料
- 《MySQL实战45讲》
- 《从根儿上理解MySQL》
- 《MySQL技术内幕—InnoDB存储引擎》第2版
3000帧动画图解MySQL为什么需要binlog、redo log和undo log