本文转载自微信公众号《石山的架构笔记》,作者崔浩。转载本文请联系石山架构笔记公众号。MySQL是一种常用的数据库存储应用程序。我们用它来存储信息、查询信息和处理交易。特别是通过事务一致性、主从复制、数据恢复等功能来提高可用性。我们在使用这些功能的时候,有没有想过它们背后的原理和机制?今天重点介绍MySQL的两种日志机制redolog和binlog,以及它们是如何相互配合来提高MySQL存储可靠性的。今天我将学习以下内容:RedologRedolog解决什么问题?Redolog的执行过程Redolog的写入方法Redolog记录形式为Binloga。Binlog解决什么问题?b.Binlog的日志格式Redolog和Binlog的区别Redolog与Redolog1.Redolog配合解决什么问题?在MySQL应用程序中处理事务是一项重要的工作,而在事务处理(ACID)的四个特性中,有一个是持久性(Durability),这意味着在事务执行的过程中,对数据的所有更改都必须保存到事务成功结束之前的某种物理存储设备。也就是说,只要事务提交成功,对数据库所做的修改就会被永久保存,任何原因都不可能恢复到原来的状态。那么为什么要考虑MySQL中的事务持久化问题呢?假设这样一个场景,当数据存储的事务正在执行但是数据还没有保存时??,数据库宕机了,那么还没有来得及存储在磁盘上的数据就会丢失,如果有一个机制来记录此时这个事务的操作,当数据库服务恢复时,记录的操作会被执行,这样还没有来得及存储的数据就可以正确保存了。重做日志就是通过这种方式来实现事务持久化的。在上述场景中,数据库服务器已关闭。如果出现其他故障,脏页没有写入磁盘,也可以通过Redolog恢复。一、Redolog的执行过程了解了为什么要使用redolog之后,我们来看一下它的执行过程,如图1所示:图1Redolog执行过程泳道图由MySQLclient、MySQLServer层和MySQL组成存储引擎层,由于redolog使用的是Innodb存储引擎,这里假设存储引擎是Innodb。由于MySQLServer层主要负责SQL语句的分析、优化和执行,而MySQL存储引擎层主要负责存储,因此redolog也运行在这一层。按照图中的序号可以看到redolog的运行过程。1、向MySQL客户端请求语句“updateTseta=1whereid=2”,发现MySQLServer层。2.MySQLServer层收到SQL请求后,对其进行分析、优化和执行,并将生成的SQL执行计划发送给存储引擎层执行。3、存储引擎层将“将a修改为1”的操作记录到内存中。4、记录到内存中后,会修改redolog记录,新增一行,内容为“需要在哪个数据页上做哪些修改”。5.之后,设置事务的状态为prepare,表示事务准备好提交。6、MySQLServer层处理完事务后,会设置事务的状态为commit,即提交事务。7、重做日志收到事务提交请求后,会将刚刚写入内存的操作记录写入磁盘,从而完成整个日志记录过程。2重做日志的写入方法从上面介绍的重做日志的执行过程不难看出,重做日志会先将内容写入内存,然后再写入磁盘。所以redolog的写入包括两部分:一部分是内存中的logbuffer,称为redologbuffer;另一部分是磁盘日志文件,称为重做日志文件。MySQL每次执行DML语句时,先将更新记录写入redologbuffer,然后再写入redologfile。我们把这种先写入日志再写入磁盘的方式称为WAL(Write-AheadLogging)技术。如图2所示:图2重做日志写入方式从左到右箭头方向看。日志会先写入存储引擎Innodb的重做日志缓冲区,也就是所谓的用户空间(userspace)),然后将日志保存到操作系统内核空间的缓冲区(OSbuffer)中(内核空间)。最后从OSbuffer写入磁盘上的redolog文件,完成写入操作。这种写入磁盘的操作也称为“磁盘刷新”。了解了redolog的写入方式后,我们发现主要操作是将redolog文件从redologbuffer写入磁盘,需要通过OSbuffer中转。将redologbuffer写入redolog文件的时机可以通过参数innodb_flush_log_at_trx_commit进行配置。各参数值含义如下:当参数为0时,称为“延时写入”。当事务提交时,redologbuffer中的日志并不会写入OSbuffer,而是每秒写入OSbuffer,并调用写入redologfile。也就是说,这个方法每秒都会发起一次写入磁盘的操作。如果系统崩溃,只会丢失1秒的数据。当参数为1时,称为“实时写入,实时刷新”。每次事务提交时,redologbuffer中的日志被写入OSbuffer,并保存到redologfile中。优点是即使系统崩溃也不会丢失数据,缺点是每次事务提交都需要进行磁盘操作,性能较差。当参数为2时,称为“实时写入,延迟刷新”。每个事务提交都会写入OS缓冲区,然后每秒将日志写入重做日志文件。这样性能会更好,但缺点是系统崩溃时1秒内的交易数据会丢失。3.重做日志记录形式重做日志以循环写入的方式保存。如图3所示:图3redolog循环写入(资料来自网络)redologbuffer(在内存中)由四个首尾相连的文件组成,它们分别是:ib_logfile_1、ib_logfile_2、ib_logfile_3、ib_logfile_4。写的方式也是从文件头开始写(假设),在文件末尾添加一条日志记录,直到写满四个文件,然后回到文件开头(ib_logfile_1)继续写入,继续写入时,会覆盖之前的记录。图3中,writepos代表当前写记录位置(数据页写入磁盘的逻辑顺序位置),checkpoint代表磁盘刷新(写入磁盘)后的对应位置。writepos和checkpoint之间的部分用来记录新的日志,也就是留给新记录的空间。checkpoint和writepos之间是要刷入的记录。如果您不刷写磁盘,它将被新记录覆盖。当writepos指针追上checkpoint时(即新记录即将覆盖旧记录的时刻),会推动checkpoint向前移动,即督促其flush记录到磁盘,以便为新记录腾出空间。redologbuffer根据checkpint刷新后,对于InnoDB引擎来说,磁盘是以page为单位存储的。一个事务可能有一个或多个数据页,每一页修改多个字节。当Innodb存储引擎重启时,会进行恢复操作。因为重做日志记录的是数据页的物理变化,所以恢复速度比逻辑日志(binlog)要快。重启InnoDB时,会先检查数据页在磁盘中的逻辑顺序位置。如果数据页的逻辑顺序位置小于日志中的位置,则从检查点恢复。如果宕机时正在刷盘过程中,并且数据页的刷盘进度超过了日志页的刷盘进度,则数据页中记录的逻辑顺序的位置大于日志中的逻辑顺序。position,这个时候,超过日志进度的部分是不会重做的,因为这本身就代表已经做了,没必要重做。Binlog4.Binlog解决什么问题?对于MySQL数据库来说,提高数据可靠性是一个永恒的话题,其中主从复制和数据恢复是增强数据可靠性的两个重要功能。Binlog就是为了实现这两个功能而设置的。在主从复制的场景下,会在Master端开启binlog,然后将binlog发送到各个Slave端,Slave端重放binlog,使得Slave端的数据与Master端的数据。在数据恢复场景下,使用mysqlbinlog工具和对应的binlog将数据恢复到指定时间点。那么binlog解决的问题可以概括为两点,即主从复制和数据恢复。5、Binlog的日志格式从记录方式来看,binlog是通过append方式记录的。当日志文件的大小大于给定值时,后续日志将记录在一个新文件中。这与redolog的循环记录形成了鲜明的对比,binlog可以通过配置参数max_binlog_size来设置每个binlog文件的大小。从日志格式来看,Binlog日志有三种格式,分别是STATMENT、ROW和MIXED。MySQL5.7.7之前,默认格式为STATEMENT,MySQL5.7.7之后,默认为ROW。日志格式由binlog-format指定。三种格式的定义和优缺点如下:lSTATEMENT:基于SQL语句的复制(statement-basedreplication,SBR),记录修改后的SQL语句。n优点:由于不需要记录每一行日志的变化,日志文件小,减少了日志量,节省了IO,提高了性能;n缺点:准确性较差,某些系统的行数无法准确复制,例如:now()、uuid()。lROW:基于行的复制(RBR),不记录每条SQL语句的上下文信息,只记录每一行的实际数据变化。n优点:准确性强,能够准确复制数据变化。n缺点:生成的日志文件较大,导致网络IO和磁盘IO较大。尤其是当altertable会让日志暴涨的时候。lMIXED:基于STATEMENT和ROW两种模式的Mixed-basedreplication(MBR)。默认使用STATEMENT方式保存,STATEMENT方式无法复制的操作使用ROW方式。n优点:准确性强,文件大小适中。n缺点:可能会出现master和slave不一致的情况。在MySQL中,可以通过“showbinlogevents”命令查看binlog日志事件。如代码段1所示,通过上述命令查看“mysql-bin.000002”文件中的binlog日志。mysql>showbinlogeventsin'mysql-bin.000002';CodeSegment1如图4所示:图4通过上述命令显示binlog日志内容,显示对应的binlog日志事件,从左到右依次为:Log_name:描述存放binlog日志名称的文件。Pos:描述日志记录的起始位置。event_type:描述事件类型,例如:查询、插入等。Server_id:对应数据库服务器的ID。end_log_pos:日志结束的位置。信息:执行的SQL语句。以上是查看日志的事件,这里也可以通过mysqlbinlog命令查看binlog的内容。如代码段2所示,通过mysqlbinlog命令查看mysql-bin.000002的内容。mysql>mysqlbinlog'mysql-bin.000002';CodeSegment2如图5所示:图5binlog日志内容我们截取上面查询命令返回的部分结果给大家讲解,我们从上往下看:“at294”表示“事件”的起点,即从文件的第294字节开始。“12033017:54:46”表示事件发生的时间戳信息。“end_log_pos388”表示日志记录的结??束字节位置,即文件的第388字节结束。“exec_time=28”,表示事件执行所花费的时间。“error_code=0”表示错误码为0,即没有错误。“serverid1”表示服务器的标识id。需要注意的是,binlog的事务提交是一次提交事务(一个事务包含一条或多条SQL语句)。重做日志可以在事务开始后逐步写入磁盘。所以,对于事务的提交,即使是比较大的事务,提交(commit)也是很快的,但是当开启了binlog后,比较大的事务的提交可能会变慢。因为binlog事务提交是一次性写入。6、Redolog和Binlog的区别与配合前面介绍了redolog和binlog,所以这里总结一下它们之间的区别如下表。RedologBinlog适用场景适用于崩溃恢复(crash-safe)。适用于主从复制和数据恢复。实现方法由InnoDB引擎层实现,并不是所有引擎都有。在Server层实现,所有引擎都可以使用binlog日志。记录方式redolog以循环写入的方式记录。写到最后,会回到开头,循环写入日志。通过追加的方式记录binlog。当文件大小大于配置值时,后续日志将记录到新文件中。文件大小重做日志的大小是固定的。Binlog可以通过配置参数max_binlog_size来设置每个binlog文件的大小。从binlog和redolog的区别可以看出,binlog日志只是用来归档的,但是单纯依赖binlog是不安全的。但是只有redolog是不行的,因为redolog是InnoDB特有的,日志记录放到磁盘后会被覆盖。所以需要同时记录binlog和redolog,保证数据库宕机重启时不会丢失数据。那么如何让两个日志保持一致呢?如图5所示:图5Redolog和binlog事务是一致的本图沿用图1的例子,只是多了一个步骤。看到绿色虚线框的部分,增加了写入binlog的步骤。当事务处于准备状态时,在提交事务之间,会先将日志保存到binlog,然后再提交到Innodb中的redolog,最后完成commit操作。然后关注redolog和binlog在提交成功和失败两种情况下的状态变化。如图6所示:图6Redolog和binlog状态转换图(素材来自网络)从上往下看,先看红线部分。当写入redolog且事务状态为prepare时,如果写入binlog成功,则redolog的状态设置为commit。如果写入binlog失败,按照红色箭头向上回滚事务。回到顶部,看绿色箭头的部分。如果写入redolog的状态是prepare,此时写入失败,不再写入binlog,直接回滚事务。可以看出,为了保持两个日志的一致性,使用了两次提交。Redolog和binlog是两个独立的逻辑。如果不使用两阶段提交,要么先写redolog再写binlog,要么先写binlog再写redolog。看看这两种方式会有什么问题:先写redolog再写binlog。假设当redolog写完而binlog还没有写完,也就是说binlog在事务中没有语句更新。此时MySQL重启,使用binlog恢复数据。由于之前更新的语句没有保存在数据库中,所以会丢失一次更新,造成数据不一致。先写binlog,再写redolog。如果写完binlog后服务器crash了,是因为redolog还没有写,也就是数据还没有写到数据库中。但是在binlog中已经记录了,也就是说update中记录了不应该更新的数据。这个时候MySQL数据库重启,这条不应该更新的数据就会更新到数据库中,造成数据不一致。小结本文从提高MySQL的可靠性入手,分别介绍redolog和binlog的实现机制,然后结合它们来说明它们之间的区别和配合。redolog部分首先提出redolog的目的是为了解决事务提交的持久化,因为redolog是Innodb存储引擎独有的日志模式,其执行采用两步提交。在写入方式上,会先从redologbuffer写入OSbuffer,最后写入磁盘到redologfile。对应三种写入配置,即延迟写入、实时写入实时刷新、实时写入延迟刷新。在记录形式上,循环写入四个文件,通过writepos和checkpoint推进写入进度。转眼看binlog,就是为了主从复制和数据恢复而生的。日志格式分为三种:STATEMENT、ROW、MIXED。日志内容主要记录开始记录点、结束记录点、记录发生的时间戳、记录所花费的时间以及执行的具体事件操作。介绍完这两种日志后,说明了它们的区别,并提出将两者结合使用可以保证数据库宕机时数据不丢失。介绍两者在事务过程中如何配合,并列举两个场景来描述redolog和binlog进行两阶段提交的必要性。
