• 数据库中的undo日志、redo日志


    MySQL中有六种日志文件,
    分别是:重做日志(redo log)、回滚日志(undo log)、二进制日志(binlog)、错误日志(errorlog)、慢查询日志(slow query log)、一般查询日志(general log),中继日志(relay log)。
    其中重做日志和回滚日志与事务操作息息相关,二进制日志也与事务操作有一定的关系,这三种日志,对理解MySQL中的事务操作有着重要的意义。

    undo是将用户上一步做的操作对程序造成的改动恢复到改动之前,而redo操作是指重新实现这种改动。

    undo/redo操作的实现方式分为两类:记录数据和记录操作。

    记录数据是指将信息编辑窗口打开时,保存原始数据,然后记录用户每次操作后的结果数据,这里的数据是指信息编辑窗口中所有可能发生变动的数据。做undo操作时程序将用户上一步操作前的数据传给信息编辑窗口相应控件。这种做法是以空间来换时间,程序不必考虑用户到底改变了哪些数据,反正每次都是替换的所有可能改变的数据。当每次保存的数据量比较小时,这种做法比较方便快捷,但是如果数据量大,比如包括图形、视频信息等,这种方法就比较耗费内存了。

    记录操作是指信息编辑窗口打开后,记录用户每次的操作,包括具体的操作动作以及操作改变的数据,这里的数据是指既能还原操作的数据又能重复操作的数据。做undo操作时程序根据记录的用户操作进行反向处理,对信息编辑窗口进行改动,而做redo操作的时候程序根据记录的用户操作来重复用户的操作。这种做法是以时间换来空间,程序记录的信息变少了,每次只需要记录用户的操作类型以及相关的操作数据(比如用户编辑的哪个控件,编辑前后的控件内容分别是什么),与操作无关的其他数据则不需要记录。这种做法比起记录数据的方式肯定要复杂,但是胜于节俭内存。

    Oracle中的redo和undo是关键技术的核心, 诸如实例恢复, 介质恢复, DataGuard, 闪回机制等都是给予redo和undo的, 所以很有必要详细梳理这块的知识, 总结记录.

    1. 数据变化日志

      当我们改变一个数据块时, 都记录了哪些日志, 具体是怎么样的流程呢?

      当在Oracle中改变一条数据时, 不仅仅要在数据文件里(可能在buffer里直接找到)找到并修改数据, 更重要的是需要做完善的日志记录, 具体如下:
      • 创建一个重做改变向量, 描述如何往undo块插入一条undo记录(即undo的redo日志)
      • 创建一个重做改变向量, 描述数据块的变化(即data的redo日志)
      • 合并这两个重做改变向量为一条日志记录, 并写到重做日志缓冲区
      • 向undo块插入undo记录
      • 改变数据块中的数据

      图1: 更新操作经历的事件时序图

    2. Redo

      redo处理方式:

      session -> redo日志 -> 写入redo log buffer -> 写到redo日志文件(循环利用) -> 归档到日志中

      写redo log buffer会成为系统的瓶颈

      session每次数据更改都会插入一条redo记录到buffer中, 一个session可能很短时间内做了很多更改, 同时可能有很多session并发操作, 却只有一个redo log buffer.

      • 老机制, redo allocation latch: 保护redo log buffer, 控制对共享区内存的访问.

        session -> 请求redo allocation latch -> 为写入到buffer的信息预留一些空间

        避免了多个进程同时写入buffer相同部分的风险.

          大并发系统会出现latch竞争, cpu空转
      • 新机制, private redo和IMU(In-Memory undo)

        在session的整个事务期间内, 生成所有改变向量, 写入private redo log buffer(PGA中), 当事务提交时, session会将private redo buffer中的记录copy到公共redo log buffer中.

        一个session在一次事务里只需要获取一次公共的redo allocation latch.


        e.g

        脚步更新表记录, 观察期间latch统计信息.

        • 9i的输出:

          LatchGetsIm_Gets
          redo copy 0 51
          redo allocation 53 0
          NameValue
          redo entries 51
          redo size 12,668
        • 10g的输出:

          LatchGetsIm_Gets
          redo copy 0 1
          redo allocation 5 1
          In memory undo latch 53 1
          NameValue
          redo entries 1
          redo size 12,048

        10g里, redo copy latch只命中了一次; redo allocation latch也get很少; 只生成了一个redo entry;

        测试中发现, 貌似竞争问题转移了?


      • 待跟进:
        • 分析v$latch_children, 搞清楚为什么latch活动的变化不是新的威胁(瓶颈);
        • 分析重做日志, 搞清楚那个大的日志条目(redo entry)都记录了什么;
        • 分析动态性能表(x$kcrfstrand和x$ktifp), 理解各种实例活动信息是如何串联到一起的;
      • redo的2组内存结构:
        • x$kcrfstrand, 私有redo区: 处理"前滚"改变向量(私有redo区里也包含传统的"公共"redo log buffer);
        • x$ktifp, IMU区: 处理undo改变向量;

        IMU区中有N多IMU池, N取决于数据库参数transactions/10, 每个池都有自己的latch.

        x$ktifp中的每条记录(即IMU)在x$kcrfstrand中都有对应的一条private redo记录.

        x$kcrfstrand中的每条private redo记录都由其自身的redo allocation latch保护, 每条"公共"的重做记录都由传统的redo copy latch保护.

      • In-Memory undo latch(IMU latch):

        任何一个改变都会产生一次对IMU latch的访问, 用一个latch(IMU latch)代替两个(redo allocation latch与redo copy latch), 至少latch竞争是减半了.

        IMU latch有许多子latch, 每个子latch负责一个IMU内存区域(池).

      • 新机制的redo allocation latch:

        2种latch:
        • 一类保护私有redo线程(private redo thread)
        • 另一类保护公共redo线程(public redo thread)

        每个线程都有自己的latch

    3. undo
      • 读一致性

        块的ITL entries里必须包含一个指向undo记录的指针(指针是有限的)

      • 回滚

  • 相关阅读:
    LeetCode-195
    中文屋子与图灵测试谁对?
    leedcode-122
    Java 网络编程
    Java File类与IO流
    Java 异常
    Java 多线程
    Java Collection集合
    Java 常用API (第二部分)
    Java 日期时间与unix时间戳之间转换
  • 原文地址:https://www.cnblogs.com/klb561/p/10527112.html
Copyright © 2020-2023  润新知