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