• MySQL redo log和undo log


    Redo Log

    REDO LOG 称为重做日志 ,当mysql服务器意外崩溃或者宕机后,保证已经提交的事务,持久化到磁盘中(持久性)。

    InnoDB是以页为单位去操作记录的,增删改查都会加载整个页到buffer pool中,事务中的修改操作并不是直接修改磁盘中的数据,而是先修改buffer pool中的数据,master线程每隔一段时间再异步刷新到磁盘中,因为直接修改磁盘数据的话,消耗大(不是随机IO,且需要刷新整个页到磁盘)。

    buffer pool: 可存放索引缓存、数据缓存等,可加速读写,直接操作数据页,写redo log修改就算完成,有专门的线程去做把buffer pool中的dirty page写入磁盘

    但因为buffer pool是在内存中的区域,系统意外崩溃的话数据有可能会丢失(有些数据还没来得及刷新到磁盘),事务的持久性得不到保证。因此,引入了redo log,修改时,额外记录一次日志,内容是xx页xx偏移量发生了xx变化,当系统崩溃时可以根据日志内容进行恢复。

    写日志和直接刷新磁盘的区别是:写日志是顺序写入,速度更快,且写入的内容相对更小

    redo log由两部分组成:

    1. redo log buffer(内存层面,默认16M,通过innodb_log_buffer_size参数可修改)

    2. redo log file(持久化的,磁盘层面)

    修改操作大致过程:

    第1步:先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝

    第2步:生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值

    第3步:默认在事务提交后将redo log buffer中的内容刷新到redo log file,对redo log file采用追加写的方式

    第4步:定期将内存中修改的数据刷新到磁盘中(这里说的是那些还没及时被master线程刷盘的脏数据)

    好处:

    • 减少了磁盘刷新频率
    • redo log占用空间小
    • redo log写入速度快

    刷盘策略

    redo log buffer中的数据还需要刷新到redo log file,万一,还没有持久化到磁盘就发生了系统崩溃怎么处理?

    InnoDB中给出了innodb_flush_log_at_trx_commit参数控制redo log buffer刷新到redo log file时的三种策略:

    • 值为0:开启一个后台线程,每1s刷新一次到磁盘中,提交事务时就不需要进行刷新了
    • 值为1:commit时再进行同步刷新(默认值)
    • 值为2:commit的时候,只是刷新进os的内核缓冲区,具体的刷盘时机不确定

    值为0的情况:

     因为有1s的间隔,所以最坏情况下会丢失1秒的数据。

    值为1的情况:

     commit时需要先主动刷新redo log buffer到redo log file,如果中途宕机了,事务也就失败了,不会有任何损失,真正能保证事务的一致性。但是效率最差。

    值为2的情况:就是异步持久化redo log,根据os决定。

    可以调整为0或2提高事务的性能,但是丧失了ACID特性

    其他参数

    • innodb_log_group_home_dir:指定 redo log文件组所在的路径,默认值为 ./ ,表示在数据库的数据目录下。MySQL的默认数据目录下默认有两个名为ib_logfile0和ib_logfile1的文件,log buffer中的日志默认情况下就是刷新到这两个磁盘文件中。
    • innodb_log_files_in_group:指明redo log file的个数,命名方式如:ib_logfile0,iblogfile1... iblogfilen。默认2个,最大100个。
    • innodb_log_file_size:单个 redo log 文件设置大小,默认值为 48M 。

    Undo日志

    undo log用于保证事务的原子性和一致性。作用有两个:①提供回滚操作 ②多版本控制MVVC

    • 回滚操作

    前面redo log中说过,master线程会不定时的去刷新buffer pool中的数据到数据库(刷盘),但是如果该事务执行期间出现各种错误(宕机)或者执行rollback语句,那么前面刷进去的操作都是需要回滚的,保证原子性。

    • MVVC

    当读取的某一行被其他事务锁定时,它可以从undo log中分析出该行记录以前的数据版本是怎样的,从而让用户能够读取到当前事务操作之前的数据——快照读。

    快照读:SQL读取的数据是历史版本,不用加锁,普通的SELECT就是快照读。

    undo log的组成部分:

    • 当insert的时候,必须将该记录的主键值记录下来
    • 当update的时候,必须将修改的旧值全部记录下来
    • 当delete的时候,必须将所有的记录都记录下来

    回滚段与undo页

    在InnoDB存储引擎中,undo log使用rollback segment回滚段进行存储,每隔回滚段包含了1024个undo log segment。 MySQL5.5之后,一共有128个回滚段。即总共可以记录128 * 1024个undo操作。

    每个事务只会使用一个回滚段,一个回滚段在同一时刻可能会服务于多个事务。

    回滚段中的数据分类:

    • 未提交的回滚数据(uncommitted undo information):该数据用于都一致性MVCC,所以该数据不能被其他数据覆盖。
    • 已经提交但未过期的回滚数据(committed undo information)
    • 事务已经提交并过期的数据(expired undo information)

    Undo类型

    undo log分为:

    • insert undo log

    因为insert操作的记录,只对事务本身可见,对其他事务不可见(这是事务隔离性的要求),故该undo log可以在事务提交后直接删除。不需要进行purge操作。

    • update undo log

    update undo log记录的是对delete和update操作产生的undo log。该undo log可能需要提供MVCC机制,因此不能在事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行最后的删除。

    undo log的声明周期

    假设有2个数组,分别为A=1和B=2,然后将A修改为3,B修改为4

    1. 1.begin
    2. 2.记录A=1到undo log
    3. 3.update A=3
    4. 4.记录A=3redo log
    5. 5.记录B=2到undo log
    6. 6.update B=4
    7. 7.记录B=4redo log
    8. 8.将redo log刷新到磁盘
    9. 9.commit
    • 在1-8步骤的任意一步系统宕机,事务朱提校,该事务就不会对磁盘上的数据做任何影响。
    • 如果在8-9之间宕机,恢复之后可以选择回滚,也可以选择继续完成事务提交,因为此时redo log已经持久化。
    • 若在9之后系统宕机,内存映射中变更的数据还来不及刷回磁盘,那么系统恢复之后,可以根据redo log把数据刷回磁盘。

     

    详细生成过程

    对于InnoDB引擎来说,每个行记录除了记录本身的数据之外,还有几个隐藏的列:

    • DB_ROW_ID∶主键id。
    • DB_TRX_ID:事务ID,当对某条记录发生变更时,就会将这个事务的事务ID写入trx_id中。
    • DB_ROLL_PTR︰回滚指针,本质上就是指向undo log的指针。

     

    当我们执行INSERT时:

    1. begin;
    2. INSERT INTO user (name) VALUES ("tom");

    插入的数据都会生成一条insert undo log,并且数据的回滚指针会指向它。undo log会记录undo log的序号、插入主键的列和值...,那么在进行rollback的时候,通过主键直接把对应的数据删除即可。

     

    当我们执行UPDATE时:

    对于更新的操作会产生update undo log,并且会分更新主键的和不更新主键的,假设现在执行:

    UPDATE user SET name="Sun" WHERE id=1;
    

     这时会把老的记录写入新的undo log,让回滚指针指向新的undo log,它的undo no是1,并且新的undo log会指向老的undo log (undo no=0)。

    假设现在执行:

    UPDATE user SET id=2 WHERE id=1;

     对于更新主键的操作,会先把原来的数据deletemark标识打开,这时并没有真正的删除数据,真正的删除会交给清理线程去判断,然后在后面插入一条新的数据,新的数据也会产生undo log,并且undo log的序号会递增。

    可以发现每次对数据的变更都会产生一个undo log,当一条记录被变更多次时,那么就会产生多条undo log,undo log记录的是变更前的日志,并且每个undo log的序号是递增的,那么当要回滚的时候,按照序号依次向前推,就可以找到我们的原始数据了。

    undo log是如何回滚的

    以上面的例子来说,假设执行rollback,那么对应的流程应该是这样:

    1. 通过undo no=3的日志把id=2的数据删除

    2. 通过undo no=2的日志把id=1的数据的deletemark还原成0

    3. 通过undo no=1的日志把id=1的数据的name还原成Tom

    4. 通过undo no=0的日志把id=1的数据删除

  • 相关阅读:
    对自动化测试的一些展望与理解
    英语音标中难发的音汇总
    arx 读入块表
    计算机图形学(七)-深度缓存、着色shadding、着色模型、着色频率、渲染管线
    缓存相关问题:雪崩、穿透、预热、更新、降级的深度解析
    正则验证用户名和跨域postmessage
    NoSQL之redis集群(未完待续)
    JavaImprove--Lesson06--正则表达式
    Rancher 系列文章-在腾讯云的 K3S 上安装高可用 Rancher 集群
    【深度学习框架PyTorch】PyTorch的高级使用与优化
  • 原文地址:https://blog.csdn.net/weixin_45902285/article/details/126166660