MySQL11、redo日志

前言

前面介绍了MySQL中EXPLAIN各个字段具体意义。接下来将介绍MySQL比较重要的功能,平时工作中很少用到,但一听都知道。比如,redo日志、undo日志、MVCC以及非常头疼的锁。接下来和大家一起学习以上内容。这篇文章先学习下redo日志。

 传送门:

MySQL-1、InnoDB行格式

MySQL-2、InnoDB数据页

MySQL-3、索引

MySQL-4、B+树索引的使用

MySQL-5、InnoDB的表空间

MySQL-6、单表访问方法

MySQL-7、连接的原理

MySQL8、各种连接方式

MySQL9、EXPLAIN详解

MySQL-10、缓冲池(Buffer Pool)

学习redo日志,跟前面介绍的行格式、数据页、索引、表空间的相关知识息息相关。不知道的,建议大家先看看前面的文章。

redo日志是啥

我们知道InnoDB存储引擎是以页为单位来管理存储空间的。在访问数据前,需要把磁盘中的页加载到内存中的Buffer Pool中。如果修改了某条数据或者新增一条数据,不会立马就同步到磁盘。因为将内存同步到磁盘中,很消耗性能,假如我们修改一条数据中的一个字符,就将一个页重新刷新到磁盘中,这显然是太浪费了。

这就导致一个问题,假如我们只在内存中的Buffer Pool中修改了页,但还没同步到磁盘中,此时突然断电或者其他故障。这时重启数据库,数据就丢失了。

如何解决以上问题,答案很显然易见,就是redo日志。我们不是修改某条数据后,不会立马同步到磁盘。而是将一批数据一起刷新到磁盘,记录这一批数据的文件就叫做redo日志。

日志的内容大致是某个表空间下的某个页中的偏移量1000处的值更新为6,这只是大概的说一下,具体肯定比这个麻烦很多。在事务提交时,将上面的内容存入到磁盘的redo日志文件中,当系统发生故障需要重启的时候,会按照上面的内容重新执行一遍,这样就能恢复到重启前的数据,这样可以满足数据的持久性。所以redo日志也有另外一个叫法,重做日志

总结一下,什么是redo日志,它记录了对数据页的修改,用于在数据库崩溃后进行恢复。

redo日志格式

redo日志本质上只是记录了一下事务对数据库进行了哪些的修改。InnoDB针对事务对数据库的不同修改场景,定义了多种类型的redo日志。下面展示的是公有的结构:

  • type:这条redo日志的类型。
  • space ID:表空间ID。
  • page number:页号。
  • other:其他不同类型的结构内容。

在工作中,有时执行一条语句时会修改非常多的页,包括聚簇索引和二级索引。在更新索引时,还要考虑数据所属的页,是否有足够的空间。不然还要产生新页,如果空间足够,可能还要修改页中的槽信息、头信息等等,是一个很麻烦的过程。

下面介绍一些redo日志的类型,因为redo日志类型很多,这里只说一部分。

名称描述
MLOG_REC_INSERT表示在插入一条使用非紧凑行格式的记录。
MLOG_COMP_REC_INSERT表示在插入一条使用紧凑行格式的记录。
MLOG_COMP_PAGE_CREATE表示在创建一个存储紧凑行格式记录的页。
MLOG_COMP_REC_DELETE表示在删除一个使用紧凑行格式记录。
..................

这些类型的redo日志既包含物理层面意思,也包含逻辑层面的意思。

  • 从物理层面上看:这些日志都指明了对哪个表空间的哪个页进行修改。
  • 从逻辑层面上看:在系统崩溃后重启时,先调用某个函数,之后再执行redo日志,将数据恢复到崩溃前的样子。

大家看到这里可能有点懵,我们以MLOG_COMP_REC_INSERT为例,介绍一下。

  • n_fields:该记录有多少个字段。
  • n_uniques:决定该记录唯一的字段数量。主要表示在一条记录中,需要几个字段的值才能确保记录的唯一性,这样在插入一条记录时,就可以按照记录的前n_uniques个字段进行排序。对于聚簇索引来说,n_uniques的值就是主键的列数,对于二级索引来说,该值为索引列中包含的列数+主键列数。
  • field1_len~field n_len:各个字段占用的存储空间大小。
  • offset:前一条记录的地址。
  • end_seg_len:从该字段中可以计算出当前记录总共占用存储空间的大小。
  • 一些记录头信息:记录头信息的前4个bit的值 以及record_type的值。
  • extra_size:记录额外信息占用的存储空间。
  • mismatch index:为了节省 redo日志大小而设立的字段,可忽略。

关于redo日志格式,有很多种,所以我认为没必要全部都高清楚。可以大致了解下。主要还是记住redo日志会把事务执行过程中对数据库所做的所有修改都记录下来,在之后系统因崩溃而重启后,可以把事务所做的任何修改都恢复过来

Mini-Transaction

上面说过 ,一个语句执行过程中,可能会修改若干个页。比如INSERT语句,可能修改聚簇索引和二级索引对于的B+树的页。由于对这些的修改,都是发生在Buffer Pool中,所以在修改完页之后,需要记录响应的redo日志。在执行语句过程中产生的redo日志,InnoDB将进行分组,分成若干个不可分割的组。比如:

  • 向聚簇索引对应的B+树中插入一条记录时产生的redo日志是一组,是不可分割的。
  • 向二级索引对应的B+树中插入一条记录时产生的redo日志是一组,是不可分割的。
  • .......

如何理解不可分割呢。我们以向某个索引添加数据为例,在向B+树中插入这条数据之前,需要先定位这条记录应该被插入到哪个叶子节点代表的数据页中。定位到之后,还会有两个可能。

  • 该数据页剩余空间可以存储这条数据,这种情况称为 乐观插入。
  • 该数据页剩余空间不足以存储这条数据,需要进行页分裂,或者需要新增一个页。如果新增一个页还需要新增一个数据项,之前表空间的内容我们知道,还需要修改好多链表。这就意味着会产生多条的redo日志。这种情况称为 悲观插入。

InnoDB认为,向某个索引对应的B+树中插入一条数据的过程必须是原子的,不能说插入一半之后就停止了。这样就会形成一颗不正确的B+树。而我们redo的目的就是为了解决宕机重启后,数据的恢复问题。所以这是不能接受的。

所以InnoDB为了保证原子性,以组的形式来记录redo日志。在进行恢复时,针对某个组中的redo日志,要么全都恢复,要么一条也不恢复。

InnoDB把对底层页面进行一次原子访问的过程称为一个 Mini-Transaction,后面简称叫 MTR

如下图所示:

那大家可能会有一个疑问,InnoDB怎么知道一个组的最后一条是哪条redo日志呢?

InnoDB在该组中的最后一条redo日志后面加上一条特殊类型的redo日志。该类型为MLOG_MULTI_REC_END,结构很简单,只有一个type字段。所以需要保证原子性的操作所产生的一系列redo日志,必须以一条类型为MLOG_MULTI_REC_END的redo日志结尾。

redo日志的写入过程

redo log blok

为了更好地管理redo日志,InnoDB通过MTR生成的redo日志都放在了大小为512字节的block中。为了和前面介绍的表空间中的页做区分,这里把用来存储redo日志的页称为block。

结构如下所示:

  • LOG_BLOCK_HDR_NO:每个block都有一个大于0的唯一编号,该属性就是表示编号值。
  • LOG_BLOCK_HDR_DATA_LEN:表示该block中已经使用了多少字节,初始值为12字节。随着写入的增加,值也随着增加,当写满了时,该值为512字节。
  • LOG_BLOCK_FIRST_REC_GROUP:一条redo日志也可以称为一条redo日志记录(redo log record)。一个MTR会生成多条redo日志记录,叫做redo日志记录组(redo log record group)。该属性表示该block中第一个MTR生成的redo日志记录组的偏移量
  • LOG_BLOCK_CHECKPOINT_NO:表示checkpoint的序号,checkpoint后续是重点介绍的,这里暂时先不说。
  • LOG_BLOCK_CHECKSUM:表示该blocck的校验值,用于校验正确性。

日志缓冲区(log buffer)

前面说过,InnoDB为了解决磁盘速度过慢的问题,引入了Buffer Pool。写入redo日志时也不直接写入磁盘中。MySQL在服务启动时,会向操作系统申请一片连续内存空间,也称为 redo log buffer即redo日志缓冲区,简称为log buffer。如下图所示:

可以指定log buffer的大小,通过启动选项 innodb_log_buffer_size设置。默认大小是16MB。

SHOW VARIABLES LIKE 'innodb_log_buffer_size';

执行结果:

redo日志写入log buffer

向log buffer中写入redo日志是顺序写入的,先写入前面的block,再写后面的block。那就有一个问题,应该写入哪个block的哪个偏移量处呢。InnoDB提供了一个全局变量,称为 buf_free。该变量指明后续写入的redo日志应该写到log buffer中的哪个位置。如下图所示:

 我们前面说过,一个MTR会产生多条redo日志并且是一个不可分割的组。所以并不是每生成一条redo日志就插入到log buffer中,而是每当一个MTR执行完成时,该MTR生成的一组redo日志就会被复制到log buffer中。

在正式环境中,肯定是并发执行的。假设并发两个事务同时执行:

  • 事务T1会产生两个MTR,分别为mtr_t1_1、mtr_t1_2。
  • 事务T2会产生两个MTR,分别为mtr_t2_1、mtr_t2_2。

因为是并发执行的,所以T1、T2可能是交替执行的。每当一个MTR执行完成时,该MTR生成的一组redo日志就会被复制到log buffer中。如下图所示:

从上图可以看出,MTR的写入是交替执行的。并且不同的MTR产生的一组redo日志占用的存储空间大小可能不一样,有的MTR产生的redo日志量很少,有的MTR产生的redo日志量很大。

redo日志文件

redo日志刷盘时机

前面我们说过,MTR会产生一组redo日志,然后会复制到log buffer中。是在内存中,存储空间大小是有限的,默认只有16MB。所以在一些情况下,会将log buffer中的数据刷新到磁盘中,有以下情况:

  • log buffer空间不足,InnoDB规定如果当前log buffer中的redo日志已经占满当前总容量的50%左右,就需要把这些日志刷新到磁盘中。
  • 事务提交时,提交后立马同步到磁盘。
  • 后台有个线程,大约以每秒一次的频率将log buffer中的redo日志刷新到磁盘。
  • 正常关闭服务器时。
  • 做checkpoint时,这个比较重要,后面会详细介绍。

redo日志文件组

InnoDB在磁盘中存储从log buffer中同步过来的redo日志,是以文件组的形式来存放的。文件组默认有两个文件,分别为ib_logfile0、ib_logfile1。文件组中的文件数量是可以设置的,最大只能有100个。每个文件默认大小为48MB。

如下图所示:

通过以下命令可以查询redo日志文件组的配置。

//查看文件组中文件个数
SHOW VARIABLES LIKE 'innodb_log_files_in_group';
//文件组中每个文件的大小
SHOW VARIABLES LIKE 'innodb_log_file_size';

从上面的图也可以看出,往文件组中写入,是顺序写的,先写ib_logfile0,再写ib_logfile1。如果都满了,InnoDB会执行checkpoint操作 ,这个后面会详细介绍。

redo日志文件的总大小其实是:innodb_log_files_in_group * innodb_log_file_size。

redo日志文件格式

上面说过,生成的redo日志先存放在log buffer中,log buffer中又是一个一个block。log buffer又需要同步到磁盘中的redo日志文件中。所以redo日志文件其实也是若干个512字节大小的block组成。

在redo日志文件组中,每个文件大小、格式都是一样的,都有两部分组成:

  • 前2048个字节用来存储一些管理信息。
  • 从2048个字节往后的空间用来存储log buffer中的block。

如下图所示:

下面具体说下每个redo日志文件前4个特殊的block具体是干嘛用的。

首先说下第一个,log file header,描述该redo日志文件的一些整体属性。如下图所示:

属性名字节描述
LOG_HEADER_FORMAT4redo日志的版本
LOG_HEADER_PAD14用于字节填充,没有实际意义
LOG_HEADER_START_LSN8标记该redo日志文件偏移量为2048字节处对应的LSN值(后面介绍)
LOG_HEADER_CREATOR32一个字符串,标记该redo日志文件的创建者是谁,正常创建该值是MySQL的版本号。其他命令创建的值会不同
没用到460
LOG_BLOCK_CHECKSUM4该block的校验值,所有block都有该值

checkpoint1,记录关于checkpoint的一些属性。如下图所示:

属性名字节描述
LOG_CHECKPOINT_NO8服务器执行 checkpoint的编号,每执行一次checkpoint该值就+1
LOG_CHECKPOINT_LSN8服务器在结束checkpoint时对应的值,系统在崩溃重启后从该值开始
LOG_CHECKPOINT_OFFSET8上个属性中的lsn值在redo日志文件组中的偏移量
LOG_CHECKPOINT_LOG_BUF_SIZE8服务器在执行checkpoint操作时对应的log buffer的大小
没用到476
LOG_BLOCK_CHECKSUM4该block的校验值,所有block都有该值

 这里出现了好多次lsn和checkpoint,大家先不要纠结这个到底是啥,后面会详细介绍。

第三个block未使用,忽略。

第四个checkpoint2结构和checkpoin1一样。

log sequence number

log sequence number翻译过来叫做日志序号,简称为lsn。下面我们就来说下lsn具体是干啥用的。由于系统在不断的运行,就会产生很多redo日志,那怎么知道当前总共记录的redo日志量呢。这就是lsn的作用,lsn是一个全局变量,用来记录当前总共写入的redo日志量。初始值是8704,为啥不是0,设计就是如此。。

上面我们说过往log buffer中写redo日志,是以MTR为单位写入的,并且是写在block中的log block body处。所以在计算lsn的增长量的时候,是按照写入的MTR的实际字节数+log block header+log block trailer来计算的。如下图所示:

假如后续有MTR写入,总共占用300字节,那么此时的lsn即为8074+12+300 = 8386。如果后序的MTR占满了第一个block,那么lsn还要加上log block trailer。以此类推。

所以每一组由MTR生成的redo日志都有一个唯一的lsn值与之对应,即lsn值越小,说明redo日志产生的越早

flushed_to_disk_lsn

flushed_to_disk_lsn也是一个全局变量,用来标记当前log buffer中已经有哪些日志被刷新到磁盘中,也可以说有哪些日志已经同步到redo日志文件中。

在系统第一次启动的时候,该变量与lsn的值是一样的,都是8704。后面随着系统的运行,redo日志被不断的写入 log buffer,但不会立即刷新到磁盘中,所以lsn的值与flushed_to_disk_lsn的值就慢慢拉开了。如下图所示:

 如果flushed_to_disk_lsn和lsn值相同,说明log buffer中的所有redo日志都已经刷新到磁盘中了

flush链表中的lsn

这里说的flush链表是Buffer Pool中的flush链表。这里跟大家简单回忆一下Buffer Pool中的flush链表。

当第一次修改某个已经加载到Buffer Pool中的页时(修改的页,俗称脏页,大家肯定都听过),会把这个页面对应的控制块插入到flush链表的头部。之后如果再修改这个页面,因为已经在flush链表中了,所以不会重复插入。也就是说Buffer Pool中的flush链表的脏页是按照第一次修改时间进行排序的。在这个过程中,会在缓冲页对应的控制块中记录两个关于页面何时被修改的属性。

 控制块对应的两个属性:

  • oldest_modification:第一次修改Buffer Pool中的某个缓冲页时,就将修改该页面的MTR开始时对应的lsn值写入这个属性。
  • newest_modification:每一次修改页时,都会将修改该页面的MTR结束时对应的lsn值写入这个属性。也就是说这个属性表示该页最新修改后对应的lsn的值。

为了更好的理解flush链表中的lsn。举个简单的例子:

假设一个事务中mtr_1修改了Buffer Pool中的页a,那么在mtr_1执行结束时,就会将页a对应的控制块加入到flush链表的头部。接着会把mtr_1开始时对应的lsn值写入到页a对应的控制块的oldest_modification属性,假如lsn是最初的值,就是8704,mtr_1一共占用200字节,所以页a对应的控制块的newest_modification属性,值就是8904。

如下图所示:

注: oldest_modification简称o_m,newest_modification简称n_m。

紧接着,又一个事务中有mtr_2修改的是页b和页c,在mtr_2执行结束时,会将页b和页c对应的控制块都加入到flush链表的头部。接着需要把mtr_2开始时对应的lsn(也就是8904)写入页b和页c对应的控制块的o_m属性中,把mtr_2结束时对应的lsn写入页b和页c对应的控制块的n_m属性中,假如mtr_2中的redo日志大小为300字节,所以n_m为9204。如下图所示:

 又紧接着,又有一个事务中有mtr_3,修改了页b和页d,不同的是,页b已经在链表中了。所以在mtr_3执行结束时,只需要将页d对应的控制块加入到flush链表的头部。然后把各个对应的控制块中的o_m和n_m写入对应的属性。唯一区别的是,只需要修改页b控制块中的n_m属性值。假如mtr_3的redo日志大小为350字节。如下图所示:

根据上面的图片,可以看出flush链表中的脏页按照第一次修改发生的时间的顺序进行排序,也就是按照oldest_modification代表的lsn值进行排序。被多次更新的页面不会重复加入到链表中,但会更新newest_modification的值。具体这两个属性有什么作用,会在下面的checkpoint进行介绍 。

checkpoint

下面我们就开始介绍上面老是提到的checkpoint。因为redo日志文件组的大小是有限的,默认每个文件48MB,所以MySQL是循环使用redo日志文件组。把上面的图搬过来:

从上面可以看出,循环写入会造成最开始写入的redo日志“追尾”。这时我们应该想到,redo日志只是为了在系统崩溃后恢复脏页用的,但如果对于的脏页已经刷新到磁盘了。那么即使系统现在崩溃,在重启后也有些redo日志是没用的,因为磁盘里已经是最新的数据了。这时我们可以把脏页已经同步到磁盘的redo日志数据,给删除掉,腾出空间存储后续的redo日志。

前面介绍的 flushed_to_disk_lsn是表示log buffer中的redo日志同步到redo日志文件组中的偏移量。此时虽然同步到了日志文件组中,但是脏页还是在Buffer Pool中,所以对应的redo日志文件组中的磁盘数据,是不能被删除的。那有什么依据,可以删除对应的日志文件组中的数据呢。

InnoDB提出另一个全局变量checkpoint_lsn,用来表示当前系统中可以被覆盖的redo日志总量是多少。这个变量的初始值也是8704。

比如上面的例子,Buffer Pool中脏页,页a已经被刷新到磁盘中,那么mtr_1生成的redo日志就可以被覆盖了,所以可以进行一个增加checkoint_lsn的操作。这个过程称为 执行一次checkoint

注:这里强调一下,上一篇文章Buffer Pool时说过,会有后台线程不停地将脏页刷新到磁盘中,这个操作和执行一次checkpoint 是两回事,一般是不同的线程执行的,也不一定是同时执行。

执行一次checkpoint可以分为两个步骤:

步骤1:计算当前系统中可以被覆盖的redo日志对应的lsn值最大是多少。

redo日志可以被覆盖,这意味着对应的脏页被刷新到磁盘中了。我们需要计算出当前系统中最早修改的脏页对应的oldest_modification值。那么凡是系统在lsn值小于该节点的oldest_modification值时产生的redo日志都可以被覆盖掉。我们把该脏页的oldest_modification值赋值给checkpoint_lsn。

比如,当前系统中页a已经被刷新到磁盘中了,那么flush链表的尾节点就是页b。页b就是目前系统中最早修改的脏页了,它的oldest_modification值为8904。这时我们把8904赋值给checkpoint_lsn。这意味着redo日志对应的lsn值小于8904时,就可以被覆盖掉。

步骤2:将checkpoint_lsn与对应的redo日志文件组的偏移量以及此次checkpoint的编号写到日志文件的管理信息中,也就是checkpoint1或checkpoint2中。(checkpoint1和checkpoint2结构一样)

InnoDB还维护了一个变量,checkpoint_no,用来统计目前系统执行了多少次checkpoint。每执行一次checkpoint该值加1,最后是赋值给checkpoint1还是赋值给checkpoint2中的LOG_CHECKPOINT_NO属性呢?答案就是checkpoint_no是偶数时,就写到checkpoint1中,是奇数时就写到checkpoint2中。LOG_CHECKPOINT_NO具体的作用会在下面崩溃恢复中用到。

日志文件管理信息中LOG_CHECKPOINT_NO和LOG_CHECKPOINT_LSN都说了如何赋值。还有一个是LOG_CHECKPOINT_OFFSET,因为lsn的值代表系统写入的redo日志量的一个总和。一个MTR中产生多少redo日志,lsn就增加多少,初始化时lsn的值是8704,对应的redo日志文件组偏移量就是2048。那么后续lsn如何增加,对应的redo日志文件组偏移量其实是可以算出来的。所以可以通过checkpoint_lsn计算出LOG_CHECKPOINT_OFFSET的值。

最后需要说明的是,redo日志文件组中每个文件都有2048字节的管理信息。但是上述的checkpoint的信息只会存储到日志文件组中第一个日志文件的管理信息中。

崩溃恢复

说了这么多,其实如果在数据库不发生宕机的情况下,redo日志其实没啥作用,多余的操作,反而让性能变差。不怕一万就怕万一,所以redo日志还是不可或缺的。那么下面就来看在数据库崩溃看后redo日志是如何恢复数据的。

确认恢复的起点

首先我们需要找到,哪些redo日志数据需要被执行,所以要先找到checkpoint_lsn。对于值小于checkpoint_lsn的redo日志,不需要执行,因为脏页已经被刷新到磁盘中了。对于大于checkpoint_lsn的redo日志,我们不确定是否已经被刷新到磁盘了,因为刷盘大部分是异步执行的。

那我们怎么找到checkpoint_lsn值呢,大家还记得redo日志文件组中第一个文件的管理信息中,有两个block都存储了checkpoint_lsn的值,那两个取哪个呢?这时候就用到了checkpont_no了,我们需要把两个block中的checkpont_no拿出来对比一下,大的一个,说明是最新的一次checkpoint。这样我们就拿到了最新的checkpoint_lsn值以及它的checkpoint_offset值。

确认恢复的终点

redo日志恢复的起点确定了,那终点呢?前面我们说过,redo日志是顺序写入的,写入到block中,写满了一个block,再写下一个block。在redo日志文件中,前4个block是特殊的,用来存储一些信息,但是后面的都是普通的block,其中block在log block header部分有个LOG_BLOCK_HDR_DATA_LEN的属性,该属性记录该block使用了多少字节。对于填满的block的该值就是512。如果有个block的LOG_BLOCK_HDR_DATA_LEN的属性不为512,那么它就是此次崩溃恢复中需要扫描的最后一个block。也就是说,在崩溃恢复时,从checkpoint_lsn中对应的偏移量开始,一直扫描到某个block的LOG_BLOCK_HDR_DATA_LEN的属性不为512为止。

怎么恢复

确定了起点和重点,就一路沿着起始点,一步一步执行redo日志就好了。不过InnoDB还是想了一些办法来加速这个恢复过程。

  • 使用哈希表:根据redo日志的space ID和page number属性计算出哈希值,把space ID和page number相同的redo日志放到了哈希表的同一个槽中。如果有多个space ID和page number都相同的redo日志,那就把它们用链表连接起来,按照生成的先后顺序连接,之后就可以遍历哈希表。这样做还有个好处,就是对于同一个页进行修改的redo日志可以一次性的恢复完成,避免了读取页的随机I/O。
  • 跳过已经刷新到磁盘中的页面:对于小于checkpoint_lsn的值,很容易找到。但还有一种情况,在执行一次checkpoin之后,后台线程还会不断的从flush链表中刷新脏页,这时突然系统崩溃了。这时还没执行一次checkpoin,所以崩溃前有的脏页被刷盘的redo日志也不需要执行恢复操作。那如何找到呢?大家还记得我们说过oldest_modification的用法,但是new_modification的作用,却没说。在页的结构中File Header部分有个FIL_PAGE_LSN的属性。该属性记录这个页最近一次修改对应的lsn值,其实就是new_modification的值。所以如果执行了某次checkpoint之后,有的脏页被刷新到磁盘中,那么该页的FIL_PAGE_LSN的值肯定大于checkpoint_lsn的值。凡是符合这种情况的,直接跳过 ,进一步提升恢复速度。

innodb_flush_log_at_trx_commit

前面讲到,为了保证事务的持久性,用户在事务提交时需要将该事务执行过程中产生的所有redo日志都刷新到redo日志文件磁盘中。每次提交都会刷新磁盘,这肯定会降低数据库的性能,如果对事务的持久性要求不是很高,可以修改innodb_flush_log_at_trx_commit的系统变量的值。该变量有3个值:

  • 0:为0时 ,表示在事务提交时,不会将redo日志刷新到磁盘,而是交给后台线程。
  • 1:为1时,表示在事务提交时,将redo日志刷新到磁盘。innodb_flush_log_at_trx_commit默认值也是1。
  • 2:为2时,表示在事务提交时需要将redo日志写到操作系统的缓冲区中,但不需要保证将日志真正地刷新到磁盘。在这种情况下,如果数据库挂了,事务的持久性还是可以保证的,但是如果操作系统挂了,事务的持久性就不能保证了。
SHOW GLOBAL VARIABLES LIKE 'innodb_flush_log_at_trx_commit';

查看系统中的各个lsn值

SHOW ENGINE INNODB STATUS;

执行结果:


=====================================
2024-07-22 22:13:05 0x569c INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 36 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 11 srv_active, 0 srv_shutdown, 10924 srv_idle
srv_master_thread log flush and writes: 0
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 30
OS WAIT ARRAY INFO: signal count 30
RW-shared spins 0, rounds 0, OS waits 0
RW-excl spins 0, rounds 0, OS waits 0
RW-sx spins 0, rounds 0, OS waits 0
Spin rounds per wait: 0.00 RW-shared, 0.00 RW-excl, 0.00 RW-sx
------------
TRANSACTIONS
------------
Trx id counter 20789
Purge done for trx's n:o < 20789 undo n:o < 0 state: running but idle
.............. 	其他状态
---
LOG
---
Log sequence number          26033106
Log buffer assigned up to    26033106
Log buffer completed up to   26033106
Log written up to            26033106
Log flushed up to            26033106
Added dirty pages up to      26033106
Pages flushed up to          26033106
Last checkpoint at           26033106
Log minimum file id is       7
Log maximum file id is       7
93 log i/o's done, 0.00 log i/o's/second
----------------------
.............. 	其他状态
END OF INNODB MONITOR OUTPUT
============================

Log sequence number:表示系统中lsn的值,也就是当前系统已经写入的redo日志量。

Log flushed up to:表示flushed_to_disk_lsn的值,也就是当前写入到磁盘的redo日志量。

Last checkpoint at:表示当前系统的checkpoint_lsn的值。

总结

redo日志主要的作用就是在Buffer Pool中如果脏页没有被刷新到磁盘中,数据库突然宕机或者崩溃,redo日志可以恢复到崩溃前的状态。

redo日志有很多类型,通用的组成部分有,type、space ID、page number。

一个MTR可以包含一组redo日志,在进行崩溃恢复时,这一组redo日志作为不可分割的整体来处理。

redo日志存放在大小为512字节的block中,每个block有三部分组成。

log buffer日志缓冲区是一片连续的内存空间,有若干个block组成,默认大小为16MB,可以通过innodb_log_buffer_size来调整。

redo日志文件组是由若干个文件组成,默认有两个文件,每个文件默认大小为48MB,是在存放在磁盘中,主要是为了存储log buffer中的redo日志。这些文件也是由block组成的,只是前四个block是存放日志文件的管理信息。

log sequence number简称lsn,是一个全局变量,用来记录当前总共写入的redo日志量,初始值是8704。

flushed_to_disk_lsn也是一个全局变量,用来标记当前log buffer中已经有哪些日志被刷新到磁盘中,也可以说有哪些日志已经同步到redo日志文件中。

Buffer Pool中flush链表中的控制块,包含两个重要属性,oldest_modification和new_modification。

另一个全局变量checkpoint_lsn,用来表示当前系统中可以被覆盖的redo日志总量是多少,这个变量的初始值也是8704。

又介绍了崩溃恢复的大致过程,以及InnoDB对提升崩溃恢复过程的方法。

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值