• MySQL之 bin log、redo log和undo log 简介


    日志是MySQL数据库的重要组成部分,记录着数据库运行期间各种状态信息。MySQL中日志类型有很多种,但对于开发来说,最常见和最重要的就是binlogredologundolog。本篇文章主要对这三种日志类型做一个简要的介绍。

    前置知识

    • 逻辑日志:可以简单得理解为sql语句;
    • 物理日志:MySQL中数据都是保存在数据页中的,物理日志记录的是数据页上的变更;

    binlog

    binlogMySQL Server层记录的日志,也就是说,不管MySQL使用的什么存储引擎,都会有bin log产生。binlogMySQL中最重要的日志,它记录了所有的DDLDML(除了查询语句)语句,即所有修改数据的操作,以二进制的形式存储在磁盘中,binlog是一种逻辑日志。

    binlog 作用

    • 主从复制:在Mater端开启binlog,然后将binlog发送到各个Slave端,Slave端重放binlog从而达到主从数据一致;
    • 数据恢复:基于时间点,可以通过mysqlbinlog工具来恢复数据;

    binlog 主从复制原理

    MySQL主从同步主要依靠binlog来实现。这里简单介绍一下基本原理。

    • 主节点 binlog dump 线程
      当从节点连接主节点时,主节点会创建一个log dump 线程,用于发送binlog的内容。在读取binlog中的操作时,此线程会对主节点上的binlog加锁,当读取完成,甚至在发动给从节点之前,锁会被释放;

    • 从节点I/O线程
      当从节点上执行start slave命令之后,从节点会创建一个I/O线程用来连接主节点,请求主库中更新的binlogI/O线程接收到主节点binlog dump 进程发来的更新之后,保存在本地relaylog中;

    • 从节点SQL线程
      SQL线程负责读取relaylog中的内容,解析成具体的操作并执行,最终保证主从数据的一致性;

    binlog的内容

    上面说了,binlog是一种逻辑日志,可以简单得理解为sql语句,但是实际上还包含着执行的sql语句的反向逻辑。delete对应着delete本身以及反向的insert信息;update包含着对应的update执行前后数据行的相关信息;insert包含自身的insert以及对应的delete信息。

    binlog的格式

    binlog共有三种格式,分别是statementrow以及mixedMySQL 5.7.7版本之前默认使用的是statementMySQL 5.7.7之后默认使用的是row。日志的格式可以通过my.ini配置文件中的binlog-format来修改。

    • statement:基于sql语句的复制(statement-based replication,SBR),每一条修改数据的sql语句都会记录到binlog中。

      • 优点:不需要具体记录某一行的变化,节约空间,减少io,提高性能;
      • 缺点:在执行sysdate()或者sleep()等操作的时候,可能导致主从数据不一致的情况;
    • row:基于行记录的复制(row-based replication,RBR),不记录sql语句上下文相关信息,而是记录哪条记录被修改的细节。

      • 优点:非常详细地记录每一行记录修改的细节,因而不会出现数据无法被正确复制的情况;
      • 缺点:由于会非常详细地记录每一条记录修改的细节,这样会产生大量的日志内容。假设现在有一条update语句,修改了很多条记录,则每条修改记录都会记录到binlog。特别地,alter table这个操作,由于表结构的变化,每行记录都会发生变化,导致日志量暴增;
    • mixed:根据上面所说的,statementrow各有优缺点,因此出现了mixed这个版本,将这二者进行混合。一般情况下使用statement格式来进行保存,当遇到statement无法解决时,切换为row格式来进行保存。

    特别地,上面说了,新版本(MySQL 5.7.7之后)默认使用的row格式,这里的row也做了相应的优化,在遇到alter table这个操作时采用statement格式进行记录,其余操作仍然使用row格式。

    binlog的刷盘时机

    对于InnoDB存储引擎来说,只有在事务提交的时候才会记录binlog,此时记录还在内存中,MySQL通过sync_binlog来控制binlog的刷盘时机,取值范围为0-N

    • 0:不强制刷到磁盘,由系统自行判断何时写入磁盘中;
    • 1:每次提交后都要将binlog写入磁盘中;
    • N:每N个事务,才会将binlog写入磁盘中;

    binlog的物理文件大小

    my.ini配置文件中,可以通过max_binlog_size来配置binlog的大小。当日志量超过binlog文件的大小时,系统会重新生成一个新的文件来继续保存文件。当一个事务比较大时,或者是当日志越来越多的时候,此时占据的物理空间太大怎么办?MySQL提供了一种自动删除的机制,还是在my.ini配置文件中,可以通过配置expire_logs_days 这个参数来解决,单位为天。当这个参数为0,表示永不删除;为N时,表示第N天后自动删除。

    redolog

    redologInnoDB引擎专有的日志系统。主要是用来实现事务的持久性以及实现crash-safe功能。redolog属于物理日志,记录的是sql语句执行之后数据页上的具体修改内容。

    redolog的作用

    我们都知道,当MySQL运行的时候,会将数据从磁盘中加载到内存当中。当执行sql语句对数据进行修改时,修改后的内容其实都只是暂时保存到内存当中,如果此时断电或者出现其他情况,这些修改就会丢失。因而,当修改完数据之后,MySQL会寻找机会将这些内存中的记录刷回到磁盘当中。但这就出现一个性能问题,主要有两个方面:

    • InnoDB中是以为数据单位与磁盘进行交互的,而一个事务很可能只是修改了一个页上的几个字节,如果将一个完整的数据页刷回磁盘当中,浪费资源;
    • 一个事务可能涉及到多个数据页,这些数据页只是逻辑上连续,在物理上并不连续,使用随机IO性能太差;

    因此,MySQL设计了redolog来记录事务对数据页具体做了哪些修改,之后将redolog再刷回磁盘当中。你可能会有疑惑,本来就是想减少io,这不又加上一次io么?InnoDB的设计者在设计之初就已经考虑到了这些。redolog文件一般都比较小,且在刷回磁盘的过程中是顺序io,相比于随机io来说,性能更好。

    redolog简介

    redolog由两部分组成,一个是内存中的日志缓存redo log buffer,一个是磁盘中的日志文件redo log file。当每次对数据记录进行修改的时候,都会将这些修改内容先写入redo log buffer中,后续等待合适的时机将内存中的修改刷回到redo log file中。这种先写日志,再写磁盘的技术就是WAL(Write-Ahead Logging)技术。需要注意的是redolog比数据页先刷回磁盘,聚簇索引,二级索引,undo页面的修改,均需要记录redolog

    redolog的整体流程

    如图所示,当对数据记录进行修改时,redolog的流程如下:

    • 若数据已在内存中则直接进行修改,否则先将数据从磁盘加载到内存中;
    • 修改完成之后,生成一条redolog,将这条redolog写入redo log buffer中,记录的是修改之后的值;
    • 根据选定的策略,将redo log file中的内容刷回到redo log file中;

    redolog刷回redo log file的策略

    在计算机操作系统中,用户空间的数据一般无法直接写入到磁盘中,中间必须先经过操作系统内核空间缓冲区。因此,redo log buffer写入redo log file实际上是先写入os buffer中,再通过系统调用fsync()刷回到磁盘中,过程如下:

    my.ini配置文件中,可以通过innodb_flush_log_at_trx_commit参数来配置redo log buffer如何刷回redo log file的策略。

    • 0:事务提交后不会将redo log buffer中的日志写入到os buffer,而是每秒将redo log buffer写入到os buffer中,再调用fsync()写入到redo log file中。当系统崩溃时,会丢失1秒钟的数据;

      image-20210918122048058

    • 1:事务提交后都会将redo log buffer中的日志写入os buffer,再调用fsync刷到redo log file中。这样方式即使系统崩溃也不会丢失任何数据,但由于每次事务提交时都要写入磁盘,性能较差;

      image-20210918122125830

    • 2:事务提交后仅仅将redo log buffer中的日志写入os buffer,然后每秒调用fsync()os buffer中的日志写入到redo log file,如果只是MySQL挂了,不会出现数据丢失,但是要是机器宕机则会丢失1秒钟的数据;

      image-20210918122151973

    redo log 格式

    redolog采用固定大小,循环写入的格式,当redolog写满之后,会重新从头开始写。为什么这么设计呢?

    redo log存在的意义主要就是降低对数据页刷盘的要求redolog记录了数据页上的修改,但是当数据页也刷回到磁盘后,这些记录就失去作用了。因此当MySQL判断之前的redolog已经失去作用之后,新数据会将这些失效的数据进行覆盖。那如何判断该不该进行覆盖呢?

    上图是redo log file的示意图,write pos表示redolog当前记录的日志序列号LSN(log sequence number)。当数据页也已经刷回磁盘之后,会更新redo log file中的LSN,表示到这个LSN之前的数据已经落盘,这个LSN就是check pointwrite poscheck point之间的部分是redolog空余的部分,用于记录新的记录;check pointwrite pos之间是redolog已经记录的数据页修改部分,但此时数据页还未刷回磁盘的部分。当write pos追上check point时,会先推动check point向前移动,空出位置再记录新的日志。

    启动innodb的时候,不管上次是正常关闭还是异常关闭,总是会进行恢复操作。恢复时,会先检查数据页中的LSN,如果这个LSN小于redolog中的LSN,即write pos位置,说明在redolog上记录着数据页上尚未完成的操作,接着就会从最近的一个check point出发,开始同步数据。

    那有没有可能数据页中的LSN大于redolog中的LSN呢?答案是当然可能。出现这种情况时,这时超出redolog的部分将不会重做,因为这本身就表示已经做过的事情,无需再重做。

    redolog与binlog区别

    redolog binlog
    文件大小 redo log的大小是固定的。 binlog可通过配置参数max_binlog_size设置每个binlog文件的大小。
    实现方式 redo logInnoDB引擎层实现的,并不是所有引擎都有。 binlogServer层实现的,所有引擎都可以使用 binlog日志
    记录方式 redo log 采用循环写的方式记录,当写到结尾时,会回到开头循环写日志。 binlog 通过追加的方式记录,当文件大小大于给定值后,后续的日志会记录到新的文件上
    适用场景 redo log适用于崩溃恢复(crash-safe) binlog适用于主从复制和数据恢复

    binlogredo log的区别可知:binlog日志只用于归档,只依靠binlog是没有crash-safe能力的。但只有redo log也不行,因为redo logInnoDB特有的,且日志上的记录落盘后会被覆盖掉。因此需要binlogredo log二者同时记录,才能保证当数据库发生宕机重启时,数据不会丢失。

    两阶段提交

    上面简单介绍了redologbinlog,在对数据进行修改时,他们都会对这些修改进行保存落地,只是一个是物理日志,一个是逻辑日志。那他俩具体在修改过程中是如何执行的呢?

    假设现在有一条update语句要执行,update from table_name set c=c+1 where id=2,执行流程如下:

    • 先定位到id=2这一条记录;
    • 执行器拿到引擎给的行数据,把这个值加上 1,得到新的一行数据,再调用引擎接口写入这行新数据;
    • 引擎将这行新数据更新到内存中,同时将这个更新操作记录到redolog里面,此时 redolog 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务;
    • 执行器生成这个操作的 binlog,并把binlog写入磁盘;
    • 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成;

    示意图如下所示:

    这种将redolog的写入拆分成preparecommit两个步骤的过程称之为两阶段提交

    redologbinlog都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。如果不使用两阶段提交,而是先写其中一个再写另外一个可能会带来一些问题。

    此时还是使用update来举例。假设当前id=2,有一个字段c=0,分别分析以下情况:

    先写redolog再写binlog

    假设先写redolog,当redolog写完,但是binlog还未写完的时候,此时MySQL突然出现异常导致重启。由于之前redolog已经写完,系统重启后,修改的记录仍然存在,所以恢复后这一行 c 的值是 1。但由于系统重启,binlog中并未有这条记录。之后备份日志的时候,存起来的binlog里面就没有这条语句。然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的binlog丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。

    先写binlog再写redolog

    假如先写binlog,然后写redolog的时候系统重启。重启之后,redolog中没有对c进行修改的记录,此时c的值还是0。但是 binlog 里面已经记录了“把 c 从 0 改成 1”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。

    因此,综上所述,如果是先写某一个日志再写另一个日志,就会出现数据库的状态与使用binlog恢复出来的库的状态不一致的情况

    undolog

    undolog主要用来记录某条行记录被修改之前的状态,记录的是修改前的数据。这样的话,当事务进行回滚时,就可以通过undolog将记录恢复到事务开始前的样子。事务的原子性和持久性也是依靠undolog来实现的undo log主要记录了数据的逻辑变化,比如一条INSERT语句,对应一条DELETEundo log,对于每个UPDATE语句,对应一条相反的UPDATEundo log,这样在发生错误时,就能回滚到事务之前的数据状态。同时,在进行数据恢复的时候,与binlogredolog结合使用,保证了数据恢复的正确性。

    undolog的作用流程如下所示:

    • 在事务开始之前将修改前的版本写入到undo log 中;
    • 开始进行修改,将修改过的数据保存到内存当中;
    • undolog持久化到磁盘当中;
    • 将数据页刷回到磁盘当中;
    • 事务提交;

    需要注意的是,与redolog一样,undolog也是要先于数据页刷回到磁盘当中。在恢复数据时,如果undolog是完整的,可以根据undolog来回滚事务。

    在一个事务当中,可能会对同一条数据进行多次修改,那么是不是每一次修改前的记录都要记录到undolog中呢?这样的话,会导致undolog日志量太大,此时redolog就要上场了。在一个事务当中,如果是对同一条记录进行修改,undolog只会记录事务开始前的原始记录,当再次对这条记录进行修改时,redolog会记录后续的变化。在数据恢复时,redolog完成前滚,undolog完成回滚,二者相互协调完成数据的恢复。过程如下所示:

    还有一个功能就是MVCC多版本控制链了,这个请参考这篇文章,MVCC 多版本控制链

    总结

    binlogredologundologMySQL中最重要的三个日志,在进行数据恢复时,三者进行协调合作,保证数据恢复的正确性。
    22

    参考

    详细分析MySQL事务日志(redo log和undo log)

    MySQL之binlog日志、undo日志、redo日志

    必须了解的mysql三大日志-binlog、redo log和undo log

    MySQL的undo,redo,二阶段提交思维导图

    MySQL三大日志

  • 相关阅读:
    【以太坊剖析】以太坊虚拟机(EVM)之基本定义
    python时间的处理
    filebeat如何在alpine镜像中运行
    apisix插件开发(lua版)
    docker容器命令安装
    安装 Ceph 对象网关
    ceph常用操作指令
    boto3使用示例
    基于lvs的ftp集群
    python rpc
  • 原文地址:https://www.cnblogs.com/reecelin/p/13504084.html
Copyright © 2020-2023  润新知