mysql 中 event 找不到 mysql找不到文件_mysql 中 event 找不到


MySQL通过如下策略完成事务的一致性:

  • InnoDB doublewrite buffer。
  • InnoDB crash recovery。

1 InnoDB doublewrite buffer(双写缓冲)

1.1 什么是 InnoDB doublewrite buffer?

[官方定义]InnoDB uses a file flush technique called doublewrite. Before writing pages to the data files, InnoDB first writes them to a contiguous area called the doublewrite buffer. Only after the write and the flush to the doublewrite buffer have completed, does InnoDB write the pages to their proper positions in the data file. If there is an operating system, storage subsystem, or mysqld process crash in the middle of a page write, InnoDB can later find a good copy of the page from the doublewrite buffer during crash recovery.InnoDB使用了一种叫做doublewrite的特殊文件flush技术,在把pages写到date files之前,InnoDB先把它们写到一个叫doublewrite buffer的连续区域内,在写doublewrite buffer完成后,InnoDB才会把pages写到data file的适当的位置。如果在写page的过程中发生意外崩溃,InnoDB在稍后的恢复过程中在doublewrite buffer中找到完好的page副本用于恢复。

1.2 InnoDB doublewrite buffe解决的问题

数据库,操作系统和磁盘读写的基本单位是块,也可以称之为(page size)block size。数据库的块一般为8K,16K;而OS的块则一般为4K;IO块则更小,linux内核要求IO block size<=OS block size。

磁盘IO除了IO block size,还有一个概念是扇区(IO sector),扇区是磁盘物理操作的基本单位,而IO 块是磁盘操作的逻辑单位,一个IO块对应一个或多个扇区,扇区大小一般为512个字节。

各个块大小的关系如下: DB block > OS block >= IO block > 磁盘 sector,而且他们之间保持了整数倍的关系。

下面小编系统各个块的大小(MySQL为5.7,OS以Centos7):

MySQL block size

mysql> show variables like 'innodb_page_size';+------------------+-------+| Variable_name | Value |+------------------+-------+| innodb_page_size | 16384 |+------------------+-------+1 row in set (0.00 sec)

OS block

[root@ecs-prod-my57-fserp-ro ~]# getconf PAGESIZE4096

IO block size

[root@ecs-prod-my57-fserp-ro ~]# blockdev --getbsz /dev/vdb4096

sector size

root@ecs-prod-my57-fserp-ro ~]# fdisk -l | grep SectorSector size (logical/physical): 512 bytes / 512 bytes

从上面的结果可以看到DB page=4*OS page=4*IO page=32*sector size

由于任何DB page的写入,最终都会转为sector的写入,如果在写磁盘的过程中,出现异常重启,就可能会发生一个DB页只写了部分sector到磁盘,进而出现页断裂的情况 

InnoDB的page size一般是16KB,其数据校验也是针对这16KB来计算的,将数据写入到磁盘是以page为单位进行操作的。操作系统写文件是以4KB作为单位的,那么每写一个InnoDB的page到磁盘上,操作系统需要写4个块。而计算机硬件和操作系统,在极端情况下(比如断电)往往并不能保证这一操作的原子性,16K的数据,写入4K时,发生了系统断电或系统崩溃,只有一部分写是成功的,这种情况下就是partial page write(部分页写入)问题。这时page数据出现不一样的情形,从而形成一个"断裂"的page,使数据产生混乱。这个时候InnoDB对这种块错误是无 能为力的.

有人会认为系统恢复后,MySQL可以根据redo log进行恢复,而MySQL在恢复的过程中是检查page的checksum,checksum就是pgae的最后事务号,发生partial page write问题时,page已经损坏,找不到该page中的事务号,就无法恢复。

doublewrite buffer是InnoDB在tablespace上的128个页(2个区)大小是2MB。为了解决 partial page write问题,当MySQL将脏数据flush到data file的时候, 先使用memcopy将脏数据复制到内存中的doublewrite buffer,之后通过doublewrite buffer再分2次,每次写入1MB到共享表空间,然后马上调用fsync函数,同步到磁盘上,避免缓冲带来的问题,在这个过程中,doublewrite是顺序写,开销并不大,在完成doublewrite写入后,再将double write buffer写入各表空间文件,这时是离散写入,详情如下: 。


mysql 中 event 找不到 mysql找不到文件_链表_02


所以在正常的情况下, MySQL写数据page时,会写两遍到磁盘上,第一遍是写到doublewrite buffer,第二遍是从doublewrite buffer写到真正的数据文件中。如果发生了极端情况(断电),InnoDB再次启动后,发现了一个page数据已经损坏,那么此时就可以从doublewrite buffer中进行数据恢复了。

2 InnoDB crash recovery

注: 本节内容来自网易云社区,由网易数据库和大数据资深专家蒋鸿翔分享。

InnoDB的数据恢复是一个很复杂的过程,在其恢复过程中,需要redo log、binlog、undo log等参与,这里把InnoDB的恢复过程主要划分为两个阶段,第一阶段主要依赖于redo log的恢复,而第二阶段,恰恰需要binlog和undo log的共同参与.

2.1 第一阶段: redo log恢复

数据库启动后,InnoDB会通过redo log找到最近一次checkpoint的位置,然后根据checkpoint相对应的LSN开始,获取需要重做的日志,接着解析获取的日志并且保存到一个哈希表中,最后通过遍历哈希表中的redo log信息,读取相关页进行恢复。

大致过程如下:

  • 打开Redo Logs和系统表空间文件(ibdataN)
  • 读取并从中找到最大的Checkpoint LSN
  • 从最近的Checkpoint 开始往后扫描Redo Log
  • 如果能够找到Redo Log记录,说明还有数据页的更改没有刷新到数据文件上,启动Crash Recovery,使用Redo Log来恢复数据的一致性

InnoDB的checkpoint信息保存在日志文件中,即ib_logfile0的开始2048个字节中,checkpoint有两个,交替更新,checkpoint与日志文件的关系如下图:


mysql 中 event 找不到 mysql找不到文件_mysql 中 event 找不到_03


checkpoint信息分别保存在ib_logfile0的512字节和1536字节处,每个checkpoint默认大小为512字节,InnoDB的checkpoint主要有3部分信息组成:

  • checkpoint no

checkpoint no主要保存的是checkpoint号,因为InnoDB有两个checkpoint(上图中的checkpoint1和checkpoint2),通过checkpoint号来判断哪个checkpoint最新

  • checkpoint lsn

checkpoint lsn主要记录了产生该checkpoint是flush的LSN(Log Sequence Number),确保在该LSN前面的数据页都已经落盘,不再需要通过redo log进行恢复。

  • checkpoint offset

checkpoint offset主要记录了该checkpoint产生时,redo log在ib_logfile中的偏移量,通过该offset位置就可以找到需要恢复的redo log开始位置。

通过以上checkpoint的信息,我们可以简单得到需要恢复的redo log的位置,然后通过顺序扫描该redo log来读取数据,比如我们通过checkpoint定位到开始恢复的redo log位置在ib_logfile1中的某个位置,那么整个redo log扫描的过程可能是这样的:


mysql 中 event 找不到 mysql找不到文件_数据_04


  • 从ib_logfile1的指定位置开始读取redo log,每次读取4 * page_size的大小,这里我们默认页面大小为16K,所以每次读取64K的redo log到缓存中,redo log每条记录(block)的大小为512字节
  • 读取到缓存中的redo log通过解析、验证等一系列过程后,把redo log的内容部分保存到用于恢复的缓存recv_sys->buf,保存到恢复缓存中的每条信息主要包含两部分:(space,offset)组成的位置信息和具体redo log的内容,我们称之为body
  • 同时保存在恢复缓存中的redo信息会根据space,offset计算一个哈希值后保存到一个哈希表(recv_sys->addr_hash)中,相同的哈希值不同(space,offset)用链表存储,相同的(space,offset)用列表保存,可能部分事务比较大,redo信息一个block不能保存,所以,每个body中可以用链表链接多body的值
  • redo log被保存到哈希表中之后,InnoDB就可以开始进行数据恢复,只需要轮询哈希表中的每个节点获取redo信息,根据(space,offset)读取指定页面后进行日志覆盖。

redo log全部被解析并且apply完成,整个InnoDB recovery的第一阶段也就结束了,在该阶段中,所有已经被记录到redo log但是没有完成数据刷盘的记录都被重新落盘。然而,InnoDB单靠redo log的恢复是不够的,这样还是有可能会丢失数据(或者说造成主从数据不一致),因为在事务提交过程中,写binlog和写redo log提交是两个过程,写binlog在前而redo提交在后,如果MySQL写完binlog后,在redo提交之前发生了宕机,这样就会出现问题:binlog中已经包含了该条记录,而redo没有持久化。binlog已经落盘就意味着slave上可以apply该条数据,redo没有持久化则代表了master上该条数据并没有落盘,也不能通过redo进行恢复。这样就造成了主从数据的不一致,换句话说主上丢失了部分数据,那么MySQL又是如何保证在这样的情况下,数据还是一致的?这就需要进行第二阶段恢复。

2.2 第二阶段 使用binlog和undo log恢复数据

在第二阶段恢复中,需要用到binlog和undo log,其实在该阶段的恢复中,也被划分成两部分,第一部分,根据binlog获取所有可能没有提交事务的xid列表;第二部分,根据undo中的信息构造所有未提交事务链表,最后通过上面两部分协调判断事务是否可以提交。


mysql 中 event 找不到 mysql找不到文件_链表_05


[图3-2-2-2-0]根据binlog获取xid列表

如上图中所示,MySQL在第二阶段恢复的时候,先会去读取最后一个binlog文件的所有event信息,然后把xid保存到一个列表中,然后进行第二部分的恢复,如下:


mysql 中 event 找不到 mysql找不到文件_数据_06


[图3-2-2-2-1]基于undo构造事务链表

InnoDB当前版本有128个回滚段,每个回滚段中保存了undo log的位置指针,通过扫描undo日志,我们可以构造出还未被提交的事务链表(存在于insert_undo_list和update_undo_lsit中的事务都是未被提交的),所以通过起始页(0,5)下的solt信息可以定位到回滚段,然后根据回滚段下的undo的slot定位到undo页,把所有的undo信息构建一个undo_list,然后通过undo_list再创建未提交事务链表trx_sys->trx_list。

基于上面两步, 我们已经构建了xid列表和未提交事务列表,那么在这些未提交事务列表中的事务,哪些需要被提交?哪些又该回滚?判断条件很简单:凡是xid在通过binlog构建的xid列表中存在的事务,都需要被提交,换句话说,所有已经记录binlog的事务,需要被提交,而剩下那些没有记录binlog的事务,则需要被回滚。

2.3 InnoDB crash recovery流程图


mysql 中 event 找不到 mysql找不到文件_mysql 中 event 找不到_07


参考文献

MySQL ACID及四种隔离级别的解释

事务ACID特性及4种隔离级别详解

数据库事务的四大特性以及事务的隔离级别详解

InnoDB事务模型 (InnoDB Transaction Model)

MySQL的自动提交模式

Mysql元数据分析

MySQL information_schema 详解

MySQL 5.7 INFORMATION_SCHEMA 详解

information_schema系列二(列,列权限,事件,存储引擎)

Innodb三大特性之double write

MySQL InnoDB特性:两次写(DoubleWrite)

InnoDB recovery过程解析

MySQL InnoDB Update和Crash Recovery流程

InnoDB Crash Recovery 流程源码实现分析

MySQL InnoDB事务隔离级别

Innodb中的事务隔离级别和锁的关系

MySQL Server参数优化 - innodb_file_per_table