MySQL-三大日志
来源:SegmentFault
时间:2023-02-16 15:37:03 325浏览 收藏
本篇文章主要是结合我之前面试的各种经历和实战开发中遇到的问题解决经验整理的,希望这篇《MySQL-三大日志》对你有很大帮助!欢迎收藏,分享给更多的需要的朋友学习~
MySQL
版本:5.7
正文
一. MySQL中的页
在学习三大日志之前,先了解一下
MySQL中的页的概念。由于
InnoDB引擎中将数据存储在磁盘上,所以为了避免读取数据时与磁盘频繁产生IO,
InnoDB引擎采用页(Page)作为磁盘与内存交互的基本单位。每次会将一页数据加载到内存中,以提升后续操作数据的效率。
InnoDB
引擎,操作系统的文件管理系统,磁盘扇区三者之间的关系可以用下图进行示意。
注:后续称
MySQL中的页为数据页。
二. Buffer Pool
尽管
InnoDB每次读取数据都会将数据页加载到内存中,以减少IO开销,但是如果每次都从磁盘读取数据页,效率也不会高,所以在
InnoDB中加入了一个内存缓冲区,叫做Buffer Pool。
读取数据时,先判断数据所在数据页是否存在于Buffer Pool中,如果存在于Buffer Pool中,则读取Buffer Pool中的数据,如果不存在于Buffer Pool中,则从磁盘上读取数据页,并将读取到的数据页写入Buffer Pool。
修改数据时,也会先将修改写入到Buffer Pool,而不会直接写入磁盘,此时就会出现Buffer Pool中的数据页内容和磁盘上的数据页的内容不一致的情况,此时称这样的数据页为脏页,因此
InnoDB引擎提供了一个后台线程每隔一定时间将Buffer Pool中的内容写入磁盘,这样的过程叫做刷脏,通过Buffer Pool + 刷脏,可以实现一次性将多个修改同步到磁盘,从而减少IO次数,提高
InnoDB引擎的读写效率。
修改数据时
InnoDB引擎中基于Buffer Pool的交互流程图如下所示。
三. Redo Log
InnoDB存储引擎为了提高读写效率,引入了Buffer Pool,当要操作的数据所在的数据页在Buffer Pool中不存在时,会先将数据页从磁盘加载到Buffer Pool中,然后对Buffer Pool中的数据进行操作,此时会出现脏页,所以
InnoDB引擎提供了一个后台线程以一定的周期执行刷脏操作。但是刷脏并不是实时的,当Buffer Pool中存在脏页,此时
MySQL服务端发生故障而宕机或重启,此时Buffer Pool中脏页所对应的那一部分数据修改就丢失了。
为了解决数据丢失的问题,
InnoDB引入了redo log(重做日志)来进行保护。当对Buffer Pool中的数据进行修改后,还会将对数据所做的修改写入redo log中,如果出现了因为
MySQL服务端宕机或者重启而导致数据丢失的情况,此时可以在
MySQL服务端启动的时候,从redo log进行数据的恢复。实际上redo log也有一个缓冲区,叫做redo log buffer,当要向redo log记录信息时,会先记录到redo log buffer中,然后在适当的时机(可配)把redo log buffer的内容写入到磁盘上的redo log中。那么修改数据时
InnoDB引擎中基于Buffer Pool和redo log buffer的交互流程图如下所示。
那么现在对于redo log有如下几点需要进行说明。
1. 刷盘时机
注:下面提及的page cache可以理解为OS文件管理系统的缓冲区,在向磁盘写入数据时,会先写入到page cache中,然后再通过
fsync函数将page cache中的内容刷到磁盘上。
InnoDB引擎中使用innodb_flush_log_at_trx_commit来设置redo log的刷盘时机策略,其可以设置支持三种策略。
- innodb_flush_log_at_trx_commit为0,表示每次提交事务时不会刷盘;
- innodb_flush_log_at_trx_commit为1,表示每次提交事务时会刷盘,即先将redo log buffer内容写入page cache,然后调用
fsync
函数将page cache中的内容刷到磁盘上; - innodb_flush_log_at_trx_commit为2,表示每次提交事务时,仅将redo log buffer内容写入page cache。
同时,
InnoDB引擎有一个后台线程,其1秒执行一次,会将redo log buffer的内容写入page cache,然后再调用
fsync函数将page cache中的内容写到磁盘的redo log文件中,所以innodb_flush_log_at_trx_commit的值无论设置为0,1或者2,最终都是可以将redo log buffer的内容写到磁盘的redo log文件中的。下面给出innodb_flush_log_at_trx_commit被设置为不同值时的刷盘示意图。
- innodb_flush_log_at_trx_commit = 0
- innodb_flush_log_at_trx_commit = 1
- innodb_flush_log_at_trx_commit = 2
因此可以知道,innodb_flush_log_at_trx_commit设置为0或者2时,在
MySQL宕机时可能会造成1秒内的数据丢失,而innodb_flush_log_at_trx_commit设置为1时,不会造成数据丢失,innodb_flush_log_at_trx_commit=1也是
InnoDB的默认设置。
最后,用一个图进行刷盘时机的总结。
2. redo log文件形式
可以通过
SHOW VARIABLES LIKE 'innodb_log%'语句查看
InnoDB引擎中的redo log相关配置。下表是部分重要参数的说明。
Variable_name | 说明 | 默认值 |
---|---|---|
innodb_log_buffer_size | redo log buffer的大小 | 16MB |
innodb_log_file_size | 每个redo log文件的大小 | 48MB |
innodb_log_files_in_group | redo log文件数量 | 2 |
innodb_log_group_home_dir | redo log文件存放路径 | MySQL目录/data |
所以
InnoDB引擎中,默认情况下磁盘上的redo log文件个数为2,每个redo log文件大小为48MB,这两个redo log文件组成了一个日志文件组,整体是一个环形结构,从头到尾进行循环写入,示意图如下。
在日志文件组中,有两个属性用于标识当前写入位置和当前清除位置,说明如下。
- write pos。记录当前写入位置,写入后会向后推移;
- checkpoint。记录当前清除位置,清除后会向后推移。
write pos与checkpoint之间的位置,表示redo log上可以写入的部分,如下所示。
如果write pos追上checkpoint,此时需要清除redo log内容以使得后续内容能够写入,具体做法是会先触发Buffer Pool的刷盘,然后就可以清除checkpoint之后的部分内容(这部分内容对应Buffer Pool刷到磁盘上的脏页),最后checkpoint向后推移,也就是说checkpoint之前的内容其实已经被写入到了磁盘上,所以一旦
MySQL宕机重启后需要根据redo log进行数据恢复时,只需要对checkpoint之后的内容进行恢复。
3. 写redo log和写dbfile的区别
当innodb_flush_log_at_trx_commit参数设置为1时,每次提交事务后,会将redo log buffer中的内容写到redo log中,既然写到redo log也是向磁盘写数据,那么为什么不在提交事务后,直接将Buffer Pool中的内容写到dbfile呢。原因就是向dbfile写数据是随机IO,向redo log写数据是顺序IO,顺序IO的读写效率要高于随机IO。
4. redo log存储的数据组成
redo log中每条记录的组成为:表空间号 + 数据页号 + 偏移量 + 修改数据长度 + 修改的数据。
redo log属于物理日志。
四. Binlog
binlog是以事件的形式记录了所有的DDL(Data Definition Language,数据定义语言)和DML(Data Manipulation Language,数据操纵语言)语句,即binlog记录的是操作而不是数据值,因此binlog属于逻辑日志。
不同于redo log的循环写入,binlog是追加写入,且没有固定大小限制。也不同于redo log属于
InnoDB存储引擎,binlog是属于
MySQL的Service层,无论使用什么存储引擎,都会在Service层记录binlog。
binlog可以用于数据恢复和主从复制,主要就是通过读取binlog并将binlog中记录的操作再执行一遍。
下面将从几个方面对binlog进行说明。
1. 写入时机
当开启事务后,在事务执行过程中,会将DDL和DML的操作记录到binlog cache中,当提交事务时,就会将binlog cache中的内容先写到page cache,然后通过
fsync函数将page cache的内容写到磁盘上的binlog。
MySQL提供了sync_binlog参数来控制具体的写入策略,可以通过
SHOW VARIABLES LIKE 'sync_binlog%'语句进行查看。sync_binlog参数的取值和说明如下所示。
- sync_binlog设置为0,表示每次提交事务时,会将binlog cache的内容写入page cache,然后由操作系统决定什么时候将page cache的内容写到binlog;
- sync_binlog设置为1,表示每次提交事务时,会将binlog cache的内容写入page cache,然后调用
fsync
函数将page cache的内容写到binlog; - sync_binlog设置为n(n > 1),表示每次提交事务时,会将binlog cache的内容写入page cache,当向page cache写入数据的事务达到n个,此时调用
fsync
函数将page cache的内容写到binlog。
用下图进行概括示意。
2. 两阶段提交
现在已知,在开启事务后,由于后台线程的存在,事务执行过程中是可以不断向redo log写入内容的,而binlog只能在事务提交时被写入,所以实际上redo log和binlog的写入时机是不相同的,这就导致当发生
MySQL宕机时可能会出现redo log和binlog所包含的逻辑内容不一致的问题。
比如现在执行一条更新
SQL语句
UPDATE student SET stu_age=22 WHERE id=1,如果这条语句的修改写到了redo log中,但是在写到binlog前
MySQL发生宕机,然后
MySQL重启之后会根据redo log进行数据恢复,由于redo log中有更新
SQL语句的修改数据,所以这条更新
SQL语句的修改结果会写到磁盘中,但是binlog中是没有这条更新
SQL语句的,就会导致后续基于binlog进行主从同步等操作时会出现主和从数据不一致的问题。
为了解决上述的问题,在
InnoDB引擎中,使用了两阶段提交来解决。具体的实现就是将redo log的写入分为两个阶段,示意图如下所示。
由上图可知,一条redo log记录可以由事务ID + redo log记录数据 + 提交状态组成,提交状态可以是prepare和commit,当第一次将一个数据修改写入redo log时,这条redo log记录的状态为prepare,这是第一阶段提交,后续提交事务时,会在redo log中将这个事务对应的记录的状态置为commit,这是第二阶段提交。根据上述的两阶段提交的写入方式,再结合binlog,可以在发生
MySQL宕机导致redo log和binlog逻辑内容不一致时判断事务是否需要进行回滚。具体的判断策略如下所示。
- binlog无记录,redo log无记录。表示在第一阶段提交前发生宕机,此时需要回滚事务;
- binlog无记录,redo log记录状态为prepare。表示在binlog写完之前发生宕机,此时需要回滚事务;
- binlog有记录,redo log记录状态为prepare。表示在binlog写完之后,事务完成提交之前发生宕机,此时需要提交事务;
- binlog有记录,redo log记录状态为commit。表示是正常完成的事务,此时无需进行操作。
五. Undo Log
undo log叫做回滚日志,属于
InnoDB引擎。undo log记录了某条数据变更前的旧数据,当事务需要回滚时,可以通过undo log将数据恢复为事务修改前的数据,所以
InnoDB引擎中使用undo log来保证了事务的原子性。
通常情况下,一条更新语句执行,写入三大日志的顺序为undo log先于redo log,redo log先于binlog。
总结
本篇文章讨论的
MySQL中的三大日志,指用于数据恢复的redo log,用于事务回滚的undo log,以及用于数据恢复和主从同步的binlog。
对于redo log,其属于物理日志,由
InnoDB记录。在
InnoDB引擎中向redo log写入数据时会先将数据写入redo log buffer内存中,并提供innodb_flush_log_at_trx_commit参数来设置刷盘时机,其可以设置支持三种策略,小节如下所示。
- innodb_flush_log_at_trx_commit为0,表示每次提交事务时不会刷盘;
- innodb_flush_log_at_trx_commit为1,表示每次提交事务时会刷盘,即先将redo log buffer内容写入page cache,然后调用
fsync
函数将page cache中的内容刷到磁盘上; - innodb_flush_log_at_trx_commit为2,表示每次提交事务时,仅将redo log buffer内容写入page cache。
对于binlog,其属于逻辑日志,在
MySQL的Service层记录。当向binlog写入数据时,会先将数据写入binlog cache内存中,并提供了sync_binlog参数来控制具体的写入策略,小节如下所示。
- sync_binlog设置为0,表示每次提交事务时,会将binlog cache的内容写入page cache,然后由操作系统决定什么时候将page cache的内容写到binlog;
- sync_binlog设置为1,表示每次提交事务时,会将binlog cache的内容写入page cache,然后调用
fsync
函数将page cache的内容写到binlog; - sync_binlog设置为n(n > 1),表示每次提交事务时,会将binlog cache的内容写入page cache,当向page cache写入数据的事务达到n个,此时调用
fsync
函数将page cache的内容写到binlog。
对于undo log,其用于事务回滚,可以参见MySQL-事务隔离级别与MVCC一文来了解其特性,本文不再赘述。
参考
MySQL官方文档-Redo Log
JavaGuide-MySQL三大日志详解
终于介绍完啦!小伙伴们,这篇关于《MySQL-三大日志》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布数据库相关知识,快来关注吧!
-
499 收藏
-
244 收藏
-
235 收藏
-
157 收藏
-
101 收藏
-
475 收藏
-
266 收藏
-
273 收藏
-
283 收藏
-
210 收藏
-
371 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习