登录
首页 >  数据库 >  MySQL

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 Poolredo 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_sizeredo log buffer的大小16MB
innodb_log_file_size每个redo log文件的大小48MB
innodb_log_files_in_groupredo log文件数量2
innodb_log_group_home_dirredo log文件存放路径
MySQL
目录/data

所以

InnoDB
引擎中,默认情况下磁盘上的redo log文件个数为2,每个redo log文件大小为48MB,这两个redo log文件组成了一个日志文件组,整体是一个环形结构,从头到尾进行循环写入,示意图如下。

在日志文件组中,有两个属性用于标识当前写入位置当前清除位置,说明如下。

  • write pos。记录当前写入位置,写入后会向后推移;
  • checkpoint。记录当前清除位置,清除后会向后推移。

write poscheckpoint之间的位置,表示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是以事件的形式记录了所有的DDLData Definition Language,数据定义语言)和DMLData Manipulation Language,数据操纵语言)语句,即binlog记录的是操作而不是数据值,因此binlog属于逻辑日志。

不同于redo log的循环写入,binlog是追加写入,且没有固定大小限制。也不同于redo log属于

InnoDB
存储引擎,binlog是属于
MySQL
Service层,无论使用什么存储引擎,都会在Service层记录binlog

binlog可以用于数据恢复主从复制,主要就是通过读取binlog并将binlog中记录的操作再执行一遍。

下面将从几个方面对binlog进行说明。

1. 写入时机

当开启事务后,在事务执行过程中,会将DDLDML的操作记录到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设置为nn > 1),表示每次提交事务时,会将binlog cache的内容写入page cache,当向page cache写入数据的事务达到n个,此时调用
    fsync
    函数将page cache的内容写到binlog

用下图进行概括示意。

2. 两阶段提交

现在已知,在开启事务后,由于后台线程的存在,事务执行过程中是可以不断向redo log写入内容的,而binlog只能在事务提交时被写入,所以实际上redo logbinlog的写入时机是不相同的,这就导致当发生

MySQL
宕机时可能会出现redo logbinlog所包含的逻辑内容不一致的问题。

比如现在执行一条更新

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记录数据 + 提交状态组成,提交状态可以是preparecommit,当第一次将一个数据修改写入redo log时,这条redo log记录的状态为prepare,这是第一阶段提交,后续提交事务时,会在redo log中将这个事务对应的记录的状态置为commit,这是第二阶段提交。根据上述的两阶段提交的写入方式,再结合binlog,可以在发生

MySQL
宕机导致redo logbinlog逻辑内容不一致时判断事务是否需要进行回滚。具体的判断策略如下所示。
  • 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 logredo 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设置为nn > 1),表示每次提交事务时,会将binlog cache的内容写入page cache,当向page cache写入数据的事务达到n个,此时调用
    fsync
    函数将page cache的内容写到binlog

对于undo log,其用于事务回滚,可以参见MySQL-事务隔离级别与MVCC一文来了解其特性,本文不再赘述。

参考

MySQL官方文档-Redo Log
JavaGuide-MySQL三大日志详解

终于介绍完啦!小伙伴们,这篇关于《MySQL-三大日志》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布数据库相关知识,快来关注吧!

声明:本文转载于:SegmentFault 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>