一文搞懂MySQL之redo日志(含字节面试题)-创新互联
前几天看到一道面试题,题目是:事务还没提交的时候,redo log 能不能被持久化到磁盘呢?
“只有客户发展了,才有我们的生存与发展!”这是创新互联的服务宗旨!把网站当作互联网产品,产品思维更注重全局思维、需求分析和迭代思维,在网站建设中就是为了建设一个不仅审美在线,而且实用性极高的网站。创新互联对成都网站设计、网站制作、外贸营销网站建设、网站制作、网站开发、网页设计、网站优化、网络推广、探索永无止境。在学习和了解redo日志前,我就在想,这题目啥玩意啊?一个开发岗都考这么细吗?
后来,学习完redo日志后,发现这个面试题确实挺有意思的。就是考查你对redo日志的了解到底有多深,是停留在表面的理解呢?还是真的修炼到一定程度了,这个题目真的就可以反映出来。
所以,你的答案是什么呢?(答案在文末)
相信读完本篇文章,理解redo日志后,就会有你自己的答案了…
1. 什么是日志?按照普通字面意思来理解的话,日志是日记中的一种,多指非个人的,一般是记载每天所做的工作。书面日志如"教学日志",“班级日志”,"工作日志"等…
而我们这里所指的日志,则是程序中的日志。对程序的运行过程进行详细的记录;是为了保障系统的正常运行,是在程序出现问题时,方便程序员去查阅日志发现错误的重要手段之一。
1.1 数据库日志在数据库系统中,对数据的任何更新操作(如:增加、修改、删除),都要把相关操作的命令、执行时间、数据的更新等信息保存下来。这些被保存的信息就是数据库日志。也就是说,数据库日志是数据库系统中所有更新活动的操作序列。
更为重要的是,数据库日志是系统正常运行、保持数据一致性的重要手段。
redo log(重做日志)则是数据库日志中的一种。它支持再写入,恢复提交事务修改的数据页操作,以此来保证数据的持久性。
2. 为什么需要redo日志?众所周知,事务有四种特性:原子性、一致性、隔离性、持久性。
那么事务的四种特性到底是基于什么机制实现的呢?
- 事务的隔离性由锁机制实现
- 事务的原子性、一致性、持久性由事务的redo日志和undo日志来保证。
InnoDB存储引擎是以页为单位来管理存储空间的。在真正访问页面之前需要把在磁盘上的页缓存到内存中的Buffer Pool
之后才可以访问。所有的变更都必须先更新缓冲池
中的数据,然后缓冲池中的脏页
会以一定的频率被刷入磁盘(checkPoint
机制),通过缓冲池来优化CPU和磁盘之间的鸿沟,这样就可以保证整体的性能不会下降太快。
但是,由于checkPoint机制并不是在每次修改数据时就将脏页刷入磁盘,这个时候,就怕数据库宕机,一旦宕机,那么缓冲池中的所有脏页都会被清除;就会出现,我们commit亲自提交的事务,数据库竟然没有改变(表情:大惊失色)!
也就是说,事务的持久性失效了!!
那么如何保证事务的持久性呢?这个时候,redo log就应运而生了。
2.1 redo log 的作用我们只是想让已经提交的事务对数据库中数据所做的修改永久生效,即使系统崩溃,在重启后也能把修改的数据写入数据库中。我们只需要将修改了哪些数据记录一下就好。比如:某个事务将系统表空间中第10号页面中地址偏移量为100处那个字节的值1改为2;则只需要记录:将第0号表空间的10号页面的地址偏移量为100处的值更新为 2 。
InnoDB引擎的事务采用了WAL技术(Write-Ahead Logging 预写日志系统),这种技术的思想就是先写日志,再写磁盘,只有日志写入成功,才算事务提交成功,这里的日志就是redo log。当发生宕机且数据未刷到磁盘的时候,可以通过redo log来恢复,保证ACID中的D,这就是redo log的作用。
3. redo日志的好处、特点 3.1 好处- redo日志降低了刷盘频率。若没有redo日志,数据库系统则必须确保每次事务对数据库进行修改时,必须马上同步到磁盘(不管修改数据量的多少)
- redo日志占用的空间非常小。它只保存没有刷新到磁盘上的操作内容。
- redo日志是顺序写入磁盘的。在执行事务的过程中,每执行一条语句,就可能产生若干条redo日志,这些日志是按照产生的
顺序写入磁盘
的,也就是使用顺序IO,效率比随机IO快。 - 事务执行过程中,redo log不断记录。
redo log可以简单分为以下两个部分:重做日志的缓冲、重做日志文件。
4.1 重做日志的缓冲 (redo log buffer)在服务器启动时就向操作系统申请了一大片称之为redo log buffer的连续内存
空间,翻译成中文就是redo日志缓冲区。这片内存空间被划分成若干个连续的redo log block
。一个redo log block占用512字节
大小。redo log buffer是属于内存层面,是易丢失的。
参数设置:innodb_log_buffer_size
redo log buffer 大小,默认16M
,大值是4096M,最小值为1M。
mysql>show variables like '%innodb_log_buffer_size%';
+------------------------+----------+
| Variable_name | Value |
+------------------------+----------+
| innodb_log_buffer_size | 16777216 |
+------------------------+----------+
4.2 重做日志文件(redo log file)redo log file是保存在硬盘中,是持久化的。
4.2 redo log的流转过程以一个更新事务为例,redo log 流转过程,如下图所示:
第1步:先将原始数据从磁盘中读入内存中(数据库缓冲池)
第2步:生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值
第3步:当事务commit时,将redo log buffer中的内容刷新到 redo log file,对 redo log file采用追加写的方式
第4步:定期将内存中修改的数据刷新到磁盘中
redo log的写入并不是直接将数据写入磁盘的,InnoDB引擎会在写redo log的时候先写将数据写入redo log buffer,之后以一定的频率
刷入到真正的redo log file中。这里的一定频率怎么看待呢?这就是我们要说的刷盘策略。
注意:redo log buffer刷盘到redo log file的过程并不是真正的刷到磁盘中去,只是刷入到
文件系统缓存
(page cache)中去(这是现代操作系统为了提高文件写入效率做的一个优化),真正的写入会交给系统自己来决定(比如page cache足够大了时)。
那么对于InnoDB来说就存在一个问题,如果交给系统来进行写入,同样如果系统宕机,那么数据也丢失了(虽然整个系统宕机的概率还是比较小的)。
针对这种情况,InnoDB给出innodb_flush_log_at_trx_commit
参数,该参数控制 commit 提交事务时,如何将 redo log buffer 中的日志刷新到 redo log file 中。它支持三种策略:
- 值为0时:表示每次事务提交时不进行刷盘操作,都只是把 redo log 留在 redo log buffer 中。(系统默认master thread每隔1s进行一次重做日志的同步)
- 值为1时:每次事务提交的时候,都执行 fsync (同步) 将 redo log 直接持久化到磁盘( 默认值 )
- 值为2时:每次事务提交的时候,都只执行 write(写) 将 redo log 写到文件系统的 page cache 中。由OS自己决定什么时候fsync(同步)到磁盘文件(redo log file)。
show variables like 'innodb_flush_log_at_trx_commit';
mysql>show variables like 'innodb_flush_log_at_trx_commit';
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 1 |
+--------------------------------+-------+
1 row in set (0.00 sec)
另外,InnoDB存储引擎有一个后台线程(master thread),每隔1秒,就会把redo log buffer
中的内容写到文件系统缓存(page cache
),然后调用刷盘操作。
也就是说,一个没有提交事务的redo log
记录,也可能会被刷入磁盘。因为在事务执行过程中,只要发生数据的更改,redo log记录是会马上写入到redo log buffer
中,这些redo log记录会被后台线程
刷盘。所以说,对于上面那个字节的面试题来说。
相信你们已经有答案了。
除了后台线程每秒1次
的轮询操作,还有一种情况,当redo log buffer
占用的空间即将达到innodb_log_buffer_size
(这个参数默认是16M)的一半的时候,也就是redo log buffer的容量达到8M时,后台线程会主动写盘(由于这个事务可能并没有提交,所以这个写盘动作只是 write 到了文件系统的 page cache,仍然是在内存中,并没有调用 fsync 真正落盘)。
innodb_log_group_home_dir:指定redo log文件所在的路径,默认值为
./
。表示在数据库的数据目录下。MySQL的默认数据目录(linux):
var/lib/mysql
;window下则是C:\ProgramData\MySQL\MySQL Server 8.0\Data
,文件下有两个名为ib_logfile0
和ib_logfile1
的文件。redo log buffer中的日志默认情况下就是刷新到这两个磁盘文件中。innodb_log_files_in_group:指明redo log file的个数,命名方式如:ib_logfile0,ib_logfile1…ib_logfilen。默认2个,大100个。
mysql>show variables like 'innodb_log_files_in_group'; +---------------------------+-------+ | Variable_name | Value | +---------------------------+-------+ | innodb_log_files_in_group | 2 | +---------------------------+-------+
innodb_flush_log_at_trx_commit:控制 redo log 刷新到磁盘的策略,默认为1(前面已经介绍过)。
innodb_log_file_size:单个 redo log file文件设置大小,默认值为
48M
。大值为512G,注意大值指的是整个 redo log file系列文件之和,即(innodb_log_files_in_group * innodb_log_file_size )不能大于大值512G。mysql>show variables like 'innodb_log_file_size'; +----------------------+----------+ | Variable_name | Value | +----------------------+----------+ | innodb_log_file_size | 50331648 | +----------------------+----------+
从上边的描述中可以看到,磁盘上的redo
日志文件不只一个,而是可以以一个日志文件组
的形式出现的。这些文件以ib_logfile[数字]
(数字
可以是0、1、2…)的形式进行命名,每个的redo日志文件大小都是一样的。
在将redo日志写入日志文件组时,是从ib_logfile0
开始写,如果ib_logfile0
写满了,就接着ib_logfile1
写。同理,ib_logfile1
写满了就去写ib_logfile2
,依此类推。如果写到最后一个文件该咋办?那就重新转到ib_logfile0
继续写,所以整个过程如下图所示:
总共的redo log file的大小其实就是:innodb_log_file_size × innodb_log_files_in_group
(即单个文件大小 * 文件个数)。
有人可能会问,ib_logfile0都已经写完了,怎么最后还可以重新转到ib_logfile0继续写呢?那不就把之前的数据给覆盖了吗?
对于这个问题,InnoDB的设计者早就想到了,并提出了checkPoint的概念。
6.2.1 checkpoint机制在整个日志文件组中还有两个重要的属性,分别是write pos、checkpoint。
write pos
是当前记录的位置,也就是当前使用到的位置,一边写一边后移checkpoint
是当前要擦除的位置,也是往后推移
每次刷盘redo log记录到日志文件组中,write pos位置就会从0开始后移更新。每次MySQL通过加载日志文件组恢复数据时(也就是通过redo log恢复数据库的数据),则会清空加载过的redo log记录(已经恢复的数据),并把 checkpoint后移更新。write pos和checkpoint之间的还空着的部分可以用来写入新的redo log记录。
如果 write pos 追上 checkpoint ,表示日志文件组满了,这时候不能再写入新的 redo log记录,MySQL 得停下来,清空一些记录,把 checkpoint 推进一下。
7. 总结本文讲解了redo日志的出现解决了事务的持久性的问题;以及redo日志的好处和它的特点,还分析了redo日志的组成:redo log buffer 和redo log file,并且介绍了它们各自的职责以及相应的作用。
还有比较重要的一点:redo日志的刷盘策略,这对我们之后的数据库调优有一定的帮助和经验。
以及最后redo日志在文件中到底是如何存储的,它的格式、它的运行机制等。
InnoDB的更新操作采用的是Write Ahead Log(预先日志持久化)策略,即先写日志,再写入磁盘。重点掌握redo的作用、组成和刷盘策略等。
面试答案:答案是:事务还没有提交的时候,redo log 是有可能被持久化到磁盘的。
redolog 的具体落盘操作是这样的:在事务运行的过程中,MySQL 会先把日志写到 redolog buffer 中,等到事务真正提交的时候,再统一把 redolog buffer 中的数据写到 redo log 文件中。不过这个从 redolog buffer 写到 redo log 文件中的操作也就是 write 并不就是落盘操作了,这里仅仅是把 redolog 写到了文件系统的 page cache 上,最后还需要执行 fsync (同步)才能够实现真正的落盘。
InnoDB 有一个后台线程,每隔 1 秒轮询一次,具体的操作是这样的:调用 write 将 redolog buffer 中的日志写到文件系统的 page cache,然后调用 fsync 持久化到磁盘。而在事务执行中间过程的 redo log 都是直接写在 redolog buffer 中的,也就是说,一个没有提交的事务的 redo log,也是有可能会被后台线程一起持久化到磁盘的。
另外,除了后台线程每秒一次的轮询操作外,还有两种场景会让一个没有提交的事务的 redo log 写盘:
innodb_flush_log_at_trx_commit 设置是 1,这样并行的某个事务提交的时候,就会顺带将这个事务的 redolog buffer 持久化到磁盘
举个例子,假设事务 A 执行到一半,已经写了一些 redo log 到 redo log buffer 中,这时候有另外一个事务 B 提交,按照 innodb_flush_log_at_trx_commit = 1 的逻辑,事务 B 要把 redolog buffer 里的日志全部持久化到磁盘,这时候,就会带上事务 A 在 redolog buffer 里的日志一起持久化到磁盘
redo log buffer 占用的空间达到 redo log buffer 大小(由参数 innodb_log_buffer_size 控制,默认是 48MB)一半的时候,后台线程会主动写盘。不过由于这个事务并没有提交,所以这个写盘动作只是 write 到了文件系统的 page cache,仍然是在内存中,并没有调用 fsync 真正落盘。
具体参考:https://zhuanlan.zhihu.com/p/456411101
写在最后
好了,以上就是本篇文章的全部内容,如果对你有所收获或者启发。希望能点赞+收藏支持一下!
我是胡亦,一名热爱分享技术干货的博主。
我们下次再见!!🙋♂️🙋♂️
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
网页标题:一文搞懂MySQL之redo日志(含字节面试题)-创新互联
文章来源:http://ybzwz.com/article/iejgg.html