美文网首页
MySQL中的 redo log、undo log、bin lo

MySQL中的 redo log、undo log、bin lo

作者: 笔记本一号 | 来源:发表于2021-07-12 19:13 被阅读0次

    参考博客:https://qimok.cn/584.html

    重做日志(redo log)、回滚日志(undo log)、二进制日志(binlog),redo log 是物理日志,undo log 和 binlog 是逻辑日志,物理日志的恢复速度远快于逻辑日志,这时因为redo log利用了磁盘的顺序读写,mysql使用redo log提升了整体的io性能

    redo log

    参考博文:https://www.cnblogs.com/hapjin/archive/2019/09/28/11521506.html

    概念
    redo log在mysql中默认以ib_logfile0,ib_logfile1名称存在,可以手工修改参数,调节开启几组日志来服务于当前mysql数据库,mysql采用顺序,循环写方式,每开启一个事务时,会把一些相关信息记录事务日志中(记录对数据文件数据修改的物理位置或叫做偏移量);这个系列文件个数由参数innodb_log_files_in_group控制,若设置为4,则命名为ib_logfile0~3。这些文件的写入是顺序、循环写的,logfile0写完从logfile1继续,logfile3写完则logfile0继续。他们的作用是在系统崩溃重启时,作事务重做;在系统正常时,每次checkpoint时间点,会将之前写入事务应用到数据文件中。

    innodb 事务日志包括 redo log 和 undo log,redo log是物理日志,undo log 是逻辑日志,用来提供回滚操作,redo log保证事务的持久性,undo log保证事务的原子性,两者可以统称为事务日志。而binloh与redo log不同点是:redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

    生命周期:

    事务开始之后就开始产生 redo log 日志了,在事务执行的过程中,当对应事务的脏页会被记录到redo log中,当 redo log file 大小已经达到某个域值快要"不可用"时(日志文件组轮流写文件),触发 checkpoint,及时将缓冲池的脏页刷新到磁盘,并同时将redo log buffer刷新到磁盘,redo log 的使命就完成了,它所占用的空间也就可以被覆盖了。

    存储内容:

    redo log 包括两部分:一是内存中的日志缓冲(redo log buffer),默认大小16MB,可经过参数innodb_log_buffer_size动态的调整它的大小,该部分日志是易失性的;二是磁盘上的重做日志文件(redo log file),该部分日志是持久的,redo log 存储的是物理格式的日志,记录的是物理数据页面的修改信息,即所有innodb表数据的变化,它是顺序写入 redo log file 中的。

    持久化:

    当事务要修改记录时,生成的日志都得先保存起来,但又不能在还没 commit 的时候就直接写到 redo log 文件里。所以,redo log buffer 就是一块内存,用以缓存事务执行过程中的数据,记录的物事务过程产生的修改,redo log buffer中同一个事务可能多次记录,最后一个提交的事务记录会覆盖所有未提交的事务记录。当向MySQL写数据时,先写日志缓冲的redo log,然后根据"某种方式"持久化到磁盘变成redo log file。如果发生宕机,则读取磁盘上的 redo log file 进行数据的恢复。从这个角度来说,MySQL 事务的持久性是通过 redo log 来实现的。

    • 问:redo log buffer中的数据丢失了怎么办?毕竟没有写到磁盘上,MySQL重启后100%没办法将其恢复出来。
    • 答:由于在MySQL的设定中,当你要Commit事务时,redo log才会持久化进磁盘,既然你没有commit,碰巧MySQL又宕机了。那让MySQL正常重启就行了啊,反正你没有commit,MySQL也也没有必要帮你恢复什么。
    MySQL支持用户自定义在commit时如何将redo log buffer 中的日志刷 redo log file 中。这种控制通过变量 innodb_flush_log_at_trx_commit 的值来决定。该变量有3种值:0、1、2,默认为1。但注意,这个变量只是控制commit动作是否刷新redo log buffer到磁盘。redo log 可能存在的三种状态说起。这三种状态,对应的就是下图中的三个颜色块:

    这三种状态分别是:

    1. 存在 redo log buffer 中,物理上是在 MySQL 进程内存中,就是图中的红色部分;
    2. 写到磁盘 (write),但是没有持久化(fsync),物理上是在文件系统的 page cache 里
      面,也就是图中的黄色部分;
    3. 持久化到磁盘,对应的是 hard disk,也就是图中的绿色部分。

    innodb_flush_log_at_trx_commit 的值来决定以下行为:

    1. 设置为 0 的时候,表示每次事务提交时都只是把 redo log 留在 redo log buffer 中 ;
    2. 设置为 1 的时候,表示每次事务提交时都将 redo log 直接持久化到磁盘;
    3. 设置为 2 的时候,表示每次事务提交时都只是把 redo log 写到 page cache。InnoDB 有一个后台线程,每隔 1 秒,就会把 redo log buffer 中的日志,调用 write 写到文件系统的 page cache,然后调用 fsync 持久化到磁盘

    redo日志的工作就是MySQL意外宕机重启时解析redo log中的事务后重放一遍,将Buffer Pool中的缓存页重作成脏页。后续再在合适的时机将该脏页刷入磁盘便可。redo log 值负责重做缓冲池的脏页数据,但是不参与数据库的落盘工作。数据库的落盘是将缓冲池中的脏页刷到硬盘,而这用到的就checkpoint技术
    checkpoint分为两种:
    第一种是sharp checkpoint,就是在数据库关闭时将缓冲池的脏页全部刷到硬盘
    第二种是fuzzy checkpoint,fuzzy checkpoint又分为了好几种checkpoint,其中包括了master thread checkpoint,它会执行每秒和每十秒的任务按照一定的比例将脏页刷回磁盘中

    重做日志缓冲池落盘方式:

    1、MySQL master 线程周期性任务 每秒一次,将 redo log buffer 刷新到重作日志中(即使这个事务尚未提交)
    2、MySQL master 线程周期性任务 每10秒一次,将 redo log buffer 刷新到重作日志中
    3、每个事务提交时会将重做日志缓冲池中相应的数据刷到重作日志中
    4、当redo log buffer size 剩余空间小于1/2时,将 redo log buffer 刷新到重作日志中
    4、当 redo log file 大小已经达到某个域值快要"不可用"时(日志文件组轮流写文件),触发 async/sync flush checkpoint,及时将缓冲池的一些脏页刷新到磁盘,并同时将redo log buffer刷新到重作日志中。注意:最终落盘是由缓冲池刷到磁盘中,redo log file不参与落盘的工作。redo日志唯一的工作就是在崩溃恢复中,InnoDB 如果判断到一个数据页可能在崩溃恢复的时候丢失了更新,就会将它读到缓冲池,然后让 redo log 更新内存内容

    脏页落盘

    1、redo log满了
    2、缓冲池不够用了,将对应淘汰的脏页写入硬盘
    3、mysql在关闭时将脏页刷到磁盘
    4、mysql在空闲时启动线程将部分脏页落盘

    change buffer 和 redo log

    当需要更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InooDB 会将这些更新操作缓存在 change buffer 中,这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行 change buffer 中与这个页有关的操作。change buffer提升了数据库修改操作的性能,但是在数据写入change buffer时,也需要往redo log中进行写入

    为什么要使用redo log持久化,直接写入磁盘不行吗?

    1、redo log在宕机时可以用来恢复数据
    2、redo log持久化效率高。数据页、索引页的刷盘是不容易的,因为底层是一棵B+树结构。而redo log是顺序写,相比数据页、索引页的直接刷盘要效率高很多

    undo log

    参考博文:https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html#auto_id_11

    概念:

    undo用来回滚行记录到某个版本。undo log是逻辑日志,根据每行记录进行记录。undo log有两个作用:提供回滚和多个行版本控制(MVCC)。

    生命周期:

    事务开始之前,将当前事务版本生成 undo log,undo log 也会产生 redo log 来保证 undo log 的可靠性。当事务提交之后,undo log 并不能立马被删除,而是放入待清理的链表,由 purge 线程判断是否有其它事务在使用 undo 段中表的上一个事务之前的版本信息,从而决定是否可以清理 undo log 的日志空间。

    存储内容:

    undo log 存储的是逻辑格式的日志,保存了事务发生之前的上一个版本的数据,可以用于回滚。当一个旧的事务需要读取数据时,为了能读取到老版本的数据,需要顺着 undo 链找到满足其可见性的记录。

    存储位置:
    默认情况下,undo 文件是保存在共享表空间的,也即 ibdatafile 文件中,当数据库中发生一些大的事务性操作的时候,要生成大量的 undo log 信息,这些信息全部保存在共享表空间中,因此共享表空间可能会变得很大,默认情况下,也就是 undo log 使用共享表空间的时候,被“撑大”的共享表空间是不会、也不能自动收缩的。因此,MySQL5.7 之后的“独立 undo 表空间”的配置就显得很有必要了。

    binlog

    概念:

    MySQL 的二进制日志 binlog 可以说是 MySQL 最重要的日志,它记录了所有的 DDL 和 DML 语句(除了数据查询语句select、show等)。bin log 的主要目的是复制和恢复。binlog 用于主从复制中,从库利用主库上的 binlog 进行重播,实现主从同步。用于数据库的基于时间点、位点等的还原操作。binlog 的模式分三种:Statement、Row、Mixed。

    binlog的执行过程:

    事务执行过程中,先把日志写到 binlog cache,事务提交的时候,再把 binlog cache 写到 binlog 文件中。系统给 binlog cache 分配了一片内存,每个线程一个,参数 binlog_cache_size 用于控制单个线程内 binlog cache 所占内存的大小。如果超过了这个参数规定的大小,就要暂存到磁盘。事务提交的时候,执行器把 binlog cache 里的完整事务写入到 binlog 中,并清空 binlogcache
    binlog存在write操作和fsync 操作。write操作是指的就是指把日志写入到文件系统的 page cache,并没有把数据持久化到磁盘,所以速度比较快。fsync 操作才是将数据持久化到磁盘的操作

    write 和 fsync 的时机,是由参数 sync_binlog 控制的:

    1. sync_binlog=0 的时候,表示每次提交事务都只 write,不 fsync;
    2. sync_binlog=1 的时候,表示每次提交事务都会执行 fsync;
    3. sync_binlog=N(N>1) 的时候,表示每次提交事务都 write,但累积 N 个事务后才
      fsync。
      因此,在出现 IO 瓶颈的场景里,将 sync_binlog 设置成一个比较大的值,可以提升性能。
      在实际的业务场景中,考虑到丢失日志量的可控性,一般不建议将这个参数设成 0,比较常
      见的是将其设置为 100~1000 中的某个数值。

    更新事务要写 binlog,而一旦 binlog 所在磁盘的空间占用率达到 100%,那么所有的更新语句和事务提交的 commit 语句就都会被堵住。但是,系统这时候还是可以正常读数据的

    binlog的模式

    Row 模式

    记录的方式是行,即如果批量修改数据,记录的不是批量修改的SQL语句事件,而是每条记录被更改的SQL语句,因此,ROW模式的binlog日志文件会变得很“重”
    优点:row 模式的binlog日志内容会非常清楚的记录下每一行数据被修改的细节。而且不会出现某些特定情况下存储过程或function,以及trigger的调用和触发器无法被正确复制的问题。
    缺点:row 模式下,所有执行的语句当记录到日志中的时候,都以每行记录的修改来记录,这样可能会产生大量的日志内容,产生的binlog日志量是惊人的。

    Statement 模式(默认)

    记录每一条修改数据的SQL语句(批量修改时,记录的不是单条SQL语句,而是批量修改的SQL语句事件)。
    优点:statement模式记录的更改的SQ语句事件,并非每条更改记录,所以大大减少了binlog日志量,节约磁盘IO,提高性能。
    缺点:statement level下对一些特殊功能的复制效果不是很好,比如:函数、存储过程的复制。由于row level是基于每一行的变化来记录的,所以不会出现类似问题

    Row 与Statement 的区别

    Mixed 模式

    实际上就是Statement与Row的结合。
    在Mixed模式下,一般的语句修改使用statment格式保存binlog,如一些函数,statement无法完成主从复制的操作,则采用row格式保存binlog,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在Statement和Row之间选择一种。

    如何选择binlog的模式

    1、 如果生产中使用MySQL的特殊功能相对少(存储过程、触发器、函数)。选择默认的语句模式,Statement Level。
    2、 如果生产中使用MySQL的特殊功能较多的,可以选择Mixed模式。
    3、 如果生产中使用MySQL的特殊功能较多,又希望数据最大化一致,此时最好Row level模式;但是要注意,该模式的binlog非常“沉重”。

    查看binlog模式

    mysql> show global variables like "%binlog_format%";  
    +---------------+-----------+  
    | Variable_name | Value    |  
    +---------------+-----------+  
    | binlog_format | STATEMENT |  
    +---------------+-----------+ 
    

    配置binlog日志模式
    vim my.cnf

    log-bin = /data/3306/mysql-bin  
    binlog_format="STATEMENT"  
    #binlog_format="ROW"  
    #binlog_format="MIXED" 
    
    生命周期:

    事务提交的时候,一次性将事务中的 sql 语句(一个事务可能对应多个 sql 语句)按照一定的格式记录到 binlog 中,这里与 redo log 很明显的差异就是 redo log 并不一定是在事务提交的时候才刷新到磁盘,而是在事务开始之后就开始逐步写入磁盘。binlog 的默认保存时间是由参数 expire_logs_days 配置的,对于非活动的日志文件,在生成时间超过 expire_logs_days 配置的天数之后,会被自动删除。

    日志文件:

    binlog日志包括两类文件:
    1、二进制日志索引文件(文件名后缀为.index)用于记录所有有效的的二进制文件

    2、二进制日志文件(文件名后缀为.00000*)记录数据库所有的DDL和DML语句事件

    相关文章

      网友评论

          本文标题:MySQL中的 redo log、undo log、bin lo

          本文链接:https://www.haomeiwen.com/subject/ajanjltx.html