zoukankan      html  css  js  c++  java
  • 单点突破:MySQL之日志

    前言

    开发环境:MySQL5.7.31

    日志是 mysql 数据库的重要组成部分,记录着数据库运行期间各种状态信息。若数据库发生故障,可通过不同日志记录恢复数据库的原来数据。因此实际上日志系统直接决定着MySQL运行的鲁棒性和稳健性。MySQL中有六种日志文件,分别是:重做日志(redo log)、回滚日志(undo log)、二进制日志(binlog)、错误日志(errorlog)、慢查询日志(slow query log)、一般查询日志(general log),中继日志(relay log)。

    其中开发中比较关注的是重做日志(redo log)、回滚日志(undo log)、二进制日志(binlog),它们也是面试中关于mysql日志的常客。

    日志需要有事务的基础,如果有想要了解事务,欢迎访问我的另一篇博客:

    重做日志(redo log)

    什么是redo log

    重做日志(redo log)是InnoDB引擎层的日志,用来记录事务操作引起数据的变化,记录的是数据页的物理修改。作用是:确保事务的持久性。防止在发生故障的时间点,尚有脏页未写入磁盘,在重启mysql服务的时候,根据redo log进行重做,从而达到事务的持久性这一特性。

    在 MySQL 中,如果每一次的更新操作都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程 IO 成本、查找成本都很高。为了解决这个问题,MySQL 的设计者就采用了日志(redo log)来提升更新效率。

    而日志和磁盘配合的整个过程,其实就是 MySQL 里的 WAL 技术,WAL 的全称是 Write-Ahead Logging,这就是所谓的预写式技术,它的关键点就是先写日志,再写磁盘这种技术可以大大减少IO操作的频率,提升数据刷新的效率。

    具体来说,当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log(redolog buffer)里面,并更新内存(buffer pool),这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候(如系统空闲时),将这个操作记录更新到磁盘里面(刷脏页)。

    为什么需要redo log

    我们都知道,事务的四大特性里面有一个是持久性,具体来说就是只要事务提交成功,那么对数据库做的修改就被永久保存下来了,不可能因为任何原因再回到原来的状态。

    那么mysql是如何保证一致性的呢?

    最简单的做法是在每次事务提交的时候,将该事务涉及修改的数据页全部刷新到磁盘中。但是这么做会有严重的性能问题,主要体现在两个方面:

    • 因为 Innodb 是以为单位进行磁盘交互的,而一个事务很可能只修改一个数据页里面的几个字节,这个时候将完整的数据页刷到磁盘的话,太浪费资源了!
    • 一个事务可能涉及修改多个数据页,并且这些数据页在物理上并不连续,使用随机IO写入性能太差!

    因此 mysql 设计了 redo log , 具体来说就是只记录事务对数据页做了哪些修改,这样就能完美地解决性能问题了(相对而言文件更小并且是顺序IO)。

    redo log执行过程

    redo log 包括两部分:一个是内存中的日志缓冲( redo log buffer),另一个是磁盘上的日志文件(redo logfile)。

    mysql 每执行一条 DML 语句,先将记录写入 redo log buffer,后续某个时间点再一次性将多个操作记录写到 redo log file。这种技术就是上文提到的 WAL(Write-Ahead Logging) 技术。

    在计算机操作系统中,用户空间( user space )下的缓冲区数据一般情况下是无法直接写入磁盘的,中间必须经过操作系统内核空间( kernel space )缓冲区( OS Buffer )。因此, redo log buffer 写入 redo logfile 实际上是先写入 OS Buffer ,然后再通过系统调用 fsync() 将其刷到 redo log file中,具体如下图:

    看起来很复杂,但只要记住redo log是先写日志再写磁盘就行,这些只是redo log的执行过程,而且上述的过程更接近脏日志刷盘,如果看不明白上面的知识,我们下面从两个方面展开说明redo log

    脏日志刷盘

    我们上面说到的过程就是脏日志刷盘,上文提到了几个概念,这里做一些补充:

    • 日志缓冲(redo log buffer):存在易失性内存中的缓存日志
    • 日志文件(redo logfile):保存在磁盘上的redo log日志文件
    • fsync函数:用于同步内存中所有已修改的文件数据到储存设备。

    为了确保每次记录都能够写入到磁盘中的日志中,每次将redo log buffer中的日志写入redo log file的过程中都会调用一次操作系统的fsync操作。在写入的过程中,还需要经过操作系统内核空间的os buffer。也就是我们上图的操作了。

    脏数据刷盘

    脏数据:指内存中未刷到磁盘的数据。

    redo log的记录形式如下:

    redo log 实际上记录数据页的变更,而这种变更记录是没必要全部保存,因此 redo log实现上采用了大小固定,循环写入的方式,当写到结尾时,会回到开头循环写日志。

    redo log日志的大小是固定的,为了能够持续不断的对更新记录进行写入,在redo log日志中设置了两个标志位置,checkpoint和write_pos,分别表示记录擦除的位置和记录写入的位置。

    • 当write_pos标志到了日志结尾时,会从结尾跳至日志头部进行重新循环写入。所以redo log的逻辑结构并不是线性的,而是可看作一个圆周运动。write_pos与checkpoint中间的空间可用于写入新数据,写入和擦除都是往后推移,循环往复的。
    • 当write_pos追上checkpoint时,表示redo log日志已经写满。这时不能继续执行新的数据库更新语句,需要停下来先删除一些记录,执行checkpoint规则腾出可写空间。
      • checkpoint规则:checkpoint触发后,将buffer中脏数据页和脏日志页都刷到磁盘。

    redo log中最重要的概念就是缓冲池buffer pool,这是在内存中分配的一个区域,包含了磁盘中部分数据页的映射,作为访问数据库的缓冲。

    • 当请求读取数据时,会先判断是否在缓冲池命中,如果未命中才会在磁盘上进行检索后放入缓冲池;
    • 当请求写入数据时,会先写入缓冲池,缓冲池中修改的数据会定期刷新到磁盘中。这一过程也被称之为刷脏 。

    因此,当数据修改时,除了修改buffer pool中的数据,还会在redo log中记录这次操作;当事务提交时,会根据redo log的记录对数据进行刷盘。如果MySQL宕机,重启时可以读取redo log中的数据,对数据库进行恢复,从而保证了事务的持久性,使得数据库获得crash-safe能力。

    总结

    • MySQL中更新操作需要写进磁盘,磁盘也需要找到数据在更新,造成IO操作频繁而导致成本消耗高,所以有redo log,MySQL执行更新操作时先写日志,之后在空闲时写入磁盘,这样就能大大减少IO操作,提升数据刷新效率。
    • redo log记录了事务对数据页做的修改。
    • 脏数据刷盘:循环写入,有checkpoint和write pos标志,分别代表记录擦除的位置和记录写入的位置。write pos追上check point的时候代表日志写满了,这时候就不能继续执行新的数据库更新语句了
    • redo log缓冲池概念,在读取和写入时的不同
    • 保证事务的持久性,使得数据库获得crash-safe能力

    二进制日志(binlog)

    什么是binlog

    二进制日志binlog是服务层的日志,还被称为归档日志。binlog主要记录数据库的变化情况,内容包括数据库所有的更新操作。所有涉及数据变动的操作,都要记录进二进制日志中,以二进制的形式保存在磁盘中。因此有了binlog可以很方便的对数据进行复制和备份,因而也常用作主从库的同步

    binlog 是 mysql的逻辑日志,并且由 Server 层进行记录,使用任何存储引擎的 mysql 数据库都会记录 binlog 日志。

    • 逻辑日志:可以简单理解为记录的就是sql语句 。
    • 物理日志:mysql 数据最终是保存在数据页中的,物理日志记录的就是数据页变更 。

    binlog 是通过追加的方式进行写入的,可以通过max_binlog_size 参数设置每个 binlog文件的大小,当文件大小达到给定值之后,会生成新的文件来保存日志。

    binlog使用场景

    在实际应用中, binlog 的主要使用场景有两个,分别是 主从复制数据恢复

    • 主从复制 :在 Master 端开启 binlog ,然后将 binlog发送到各个 Slave 端, Slave 端重放 binlog 从而达到主从数据一致。
    • 数据恢复 :通过使用 mysqlbinlog 工具来恢复数据。

    binlog日志格式

    binlog 日志有三种格式,分别为 STATMENT 、 ROW 和 MIXED。

    在 MySQL 5.7.7 之前,默认的格式是 STATEMENT , MySQL 5.7.7 之后,默认值是 ROW。日志格式通过 binlog-format 指定。

    • STATMENT:基于SQL 语句的复制( statement-based replication, SBR ),每一条会修改数据的sql语句会记录到binlog 中 。
      • 优点:不需要记录每一行的变化,减少了 binlog 日志量,节约了 IO , 从而提高了性能;
      • 缺点:在某些情况下会导致主从数据不一致,比如执行sysdate() 、 slepp() 等 。
    • ROW:基于行的复制(row-based replication, RBR ),不记录每条sql语句的上下文信息,仅需记录哪条数据被修改了 。
      • 优点:不会出现某些特定情况下的存储过程、或function、或trigger的调用和触发无法被正确复制的问题 ;
      • 缺点:会产生大量的日志,尤其是alter table 的时候会让日志暴涨
    • MIXED:基于STATMENT 和 ROW 两种模式的混合复制(mixed-based replication, MBR ),一般的复制使用STATEMENT 模式保存 binlog ,对于 STATEMENT 模式无法复制的操作使用 ROW 模式保存 binlog

    binlog执行过程

    在MySQL执行更新语句时,都会涉及到redo log日志和binlog日志的读写。一条更新语句的执行过程如下:

    从上图可以看出,MySQL在执行更新语句的时候,在服务层进行语句的解析和执行,在引擎层进行数据的提取和存储;同时在服务层对binlog进行写入,在InnoDB内进行redo log的写入。不仅如此,在对redo log写入时有两个阶段的提交,一是binlog写入之前prepare状态的写入,二是binlog写入之后commit状态的写入。两阶段提交可以保证binlog和redo log中保存的信息是一致的。

    binlog与redo log的区别

    这里binlog所存储的内容看起来似乎与redo log很相似,但是其实不然。redo log是一种物理日志,记录的是实际上对某个数据进行了怎么样的修改;而binlog是逻辑日志,记录的是SQL语句的原始逻辑,比如”给ID=2这一行的a字段加1 "。binlog日志中的内容是二进制的,根据日记格式参数的不同,可能基于SQL语句、基于数据本身或者二者的混合。一般常用记录的都是SQL语句。
    同时,redo log是基于crash recovery,保证MySQL宕机后的数据恢复;而binlog是基于point-in-time recovery,保证服务器可以基于时间点对数据进行恢复,或者对数据进行备份。

    总的来说,两者的区别可以归纳如下:

    • 日志类型
      • redo log是物理日志
      • binlog是逻辑日志
    • 对引擎的支持
      • redo log是innodb引擎特有的
      • binlog是通过MySQL的server层实现的,所有的引擎都能使用
    • 文件空间
      • redo log日志文件的空间是固定的
      • binlog日志文件的空间不固定,写完会切换下一个文件
    • 写入方式
      • redo log是循环写入和擦除
      • binlog日志是追加写入,不会覆盖已写文件

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

    总结

    • binlog是服务层的日志,主要用来记录数据库的变化情况,属于逻辑日志
    • 使用场景一般是主从复制和数据恢复
    • 执行过程是mysql执行更新语句的时候,在服务层进行语句的解析和执行,在引擎层进行数据的提取和存储;同时在服务层对binlog进行写入,在InnoDB内进行redo log的写入。
    • 还要记得binlog和redo log的区别

    回滚日志(undo log)

    回滚日志同样也是InnoDB引擎提供的日志,顾名思义,回滚日志的作用就是对数据进行回滚。当事务对数据库进行修改,InnoDB引擎不仅会记录redo log,还会生成对应的undo log日志;如果事务执行失败或调用了rollback,导致事务需要回滚,就可以利用undo log中的信息将数据回滚到修改之前的样子。

    但是undo log不redo log不一样,它属于逻辑日志。它对SQL语句执行相关的信息进行记录。当发生回滚时,InnoDB引擎会根据undo log日志中的记录做与之前相反的工作。比如对于每个数据插入操作(insert),回滚时会执行数据删除操作(delete);对于每个数据删除操作(delete),回滚时会执行数据插入操作(insert);对于每个数据更新操作(update),回滚时会执行一个相反的数据更新操作(update),把数据改回去。undo log由两个作用,一是提供回滚,二是实现MVCC。

  • 相关阅读:
    Knockout应用开发指南 第八章:简单应用举例(2)
    微软ASP.NET站点部署指南(7):生产环境部署
    Knockout应用开发指南 第七章:Mapping插件
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(6)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(5)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(3)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(9)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(8)
    Microsoft Visual Studio .NET 2003 引导程序插件下载地址(非官方)
    Vs2010在没有安装SQL Server 2005/2008 Express时如何连接MDF数据文件?
  • 原文地址:https://www.cnblogs.com/kylinxxx/p/14407057.html
Copyright © 2011-2022 走看看