前言

已知,在关系型数据库中的事务的ACID模型由原子性一致性隔离性持久性组成,对于MySQLInnoDB引擎,隔离性由基于乐观锁的加锁机制和基于无锁的多版本并发管制来反对,而原子性,则由在引擎层生成的undo log来保障,以及持久性,则是由在引擎层生成的redo log和在Service层生成的binlog来独特撑持,除此之外,在Service层生成的binlog还会用于MySQL的主从同步,所以MySQL中的redo logbinlogundo log是非常重要的,本篇文章将对这三大日志进行学习。

MySQL版本:5.7

注释

一. MySQL中的页

在学习三大日志之前,先理解一下MySQL中的页的概念。因为InnoDB引擎中将数据存储在磁盘上,所以为了防止读取数据时与磁盘频繁产生IOInnoDB引擎采纳页(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是属于MySQLService层,无论应用什么存储引擎,都会在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中,然而在写到binlogMySQL产生宕机,而后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,其属于逻辑日志,在MySQLService层记录。当向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三大日志详解