zoukankan      html  css  js  c++  java
  • mysql-undo

    为了回滚而记录日志,我们称之为undo log

    事务id

    事务对一个表进行了增删改操作,就会为这个事务分配一个唯一的id,事务id是一个全局变量,存在系统表空间中,值是自增的

    trx_id聚簇索引中存储的隐藏列,存储的是某个事务对这条记录做修改时的事务id

    roll_pointer 聚簇索引中的隐藏列,存储的是记录对应的undo log指针

    在对一条记录进行增删改操作的时候都要先生成一条或两条undo log

    undo log类型:

    1. 插入记录时 回滚的时候直接删除对应主键记录就可以

     undo no在每个事务中都是从0开始递增的

     table id 表的唯一id值

     2. 删除记录 

      首先捋一遍删除记录的流程

      1. 被插入的记录会使用记录头信息的next_record属性组成一个单向链表,被删除的记录也会使用记录头信息的next_record头插生成一个单向链表,page Header部分有一个page_free属性作为删除链表的头结点 删除链表中的空间是可以重复利用的

      删除阶段一: 将记录的delete_mask设置为1 这个中间状态一直保持到事务提交之前

      删除阶段二: 当事务提交之后,会有后台线程把该记录从正常记录链表中移到垃圾链表中,采用头插法page free指向这个最新被删除的记录

      当有记录要插入的时候,会先查看垃圾链表的头结点占的空间是否能覆盖新纪录的大小,如果可以就占用不行就申请新的空间,而不是遍历,当页的空间快满的时候会,复制整理算法迁移一下,重新整理一下碎片空间

       在对一个记录进行delete_mark之前,把该记录的trx_id和roll_pointer存储到undo log中,可以通过old roll_pointer查找到删除前,对该记录最新操作的undo日志

       update操作

      - 不更新主键的情况

        1. 就地更新

          被更新的记录与更新前 没有任何列的长度发生变化,就可以在原记录上修改对应的列

        2. 先删除旧记录再插入新记录

          先从聚簇索引中删除掉记录由用户线程去同步移动记录到垃圾链表,然后用修改后的列创建一条新的记录插入到页面中

       - 更新主键的情况

        1. 删除对应的记录delete_mark

        2. 修改后的记录插入到聚簇索引中

        在对记录更新前会生成一条删除undo 日志, 然后在生成一条插入undo日志

      undo日志是存储在表空间的类型为FIL_PAGE_UNDO_LOG的页中 下面是存储undo日志页的结构

    存储undo日志页的结构

     TRX_UNDO_PAGE_TYPE 存储着什么类型的undo日志

     TRX_UNDO_PAGE_START 第一条undo日志在本页的偏移量

     TRX_UNDO_PAGE_FREE 最后一条undo日志结尾在本页的偏移量 从这个位置可以写入新的日志

     TRX_UNDO_PAGE_NODE undo日志页连接使用

    不同页面存储不同类型的undo日志 所以一个事务会有两个链表存储着undo日志页 又因为对临时表和普通表需要区分所以会有四个链表

    在需要用到对应的链表时才创建

    undo日志的具体写入过程

      undo页面重用

        为了并发能力,每个事务都会创建最多4个undo页链表,有些sql只是更新一个页中的数据就容易造成频繁创建链表造成浪费,在某些情况下undo页面链表是可以被重用的

      1. 链表中只有一个页面

      2. 页面被占用的空间小于页面容量的3/4

      

      - 针对insert链表 复用的时候清空从头写入新的日志就行

      - 针对update undo链表来说,由于mvcc需要用到,不能覆盖旧的日志,可以从后面接着写undo日志

      

      回滚段

      为了管理事务产生的undo页链表,mysql使用rollback segment header的页面保存着每个链表的头结点(first undo page)的页号,称为undo slot

      可以这样理解:

        每个undo页面链表代表一个班 链表第一个node代表班长从班长可以找到其余的同学,学校需要传达一些精神,只需要把班长叫到会议室,回滚段就是这个会议室

     TRX_RSEG_UNDO_SLOTS 各个undo页面链表的头结点页号集合

      当没有事务创建undo log,回滚段里面所有的undo slot都为FIL_NULL,当事务产生undo 日志的时候,从段里申请undo slot存储undo链表头结点页号,如果undo slot不为FIL_NULL说明被占用了,去下一个slot查看,一个rollback segment header页面包含1024个undo slot,如果slot都被占了,当有新事务来申请slot就会报错

      如果事务提交了,被占用的slot会被处理:

      1. 如果slot中的undo页可以被复用,把undo slot根据undo log 操作类型,加入到一个缓存的链表中,以便新事务复用的时候去查找复用的链表

      2. 如果slot中的undo页不可复用

        - 如果是insert类型的事务,会修改链表的属性,被undo占用的页会被释放,slot槽设置为FIL_NULL可以让给其他事务用

        - 如果是update类型的事务,将slot设置为FIL_NULL,将本次事务写入到history链表,这里并不会释放旧的undo占用的页

     多回滚段

      一个回滚段有1024个槽不能满足需求,所以mysql创建了128个回滚段,这些段对应着128个页,这些页的地址信息保存在系统表里,

      每个页又1024个槽存储事务链表

      mysql把128个回滚段分成两组,一组存储对普通表操作,一组存储对临时表的操作,目的是 在创建undo日志的时候需要记录redo日志 为了系统崩溃恢复undo日志,对

      临时表操作产生的undo日志不需要记录redo日志不需要崩溃恢复.所以分成两组

      那么现在总结一下undo日志的分配过程

      1. 事务执行过程中,对普通表进行了操作,会在表空间中分配回滚段,因为有128个回滚段可以分配,每个事务会循环使用这些回滚段

      2. 分配了回滚段之后,查看是否有缓存的slot,如果有缓存的slot根据事务的类型去获取slot 成功后使用缓存的slot存储undo

      3. 如果没有缓存slot就从回滚段(rollback segment header页)中遍历找可用的slot,如果1024个都不可用就报错

      4. 如果没有缓存,就重新分配一个undo log segment申请一个页面存储undo链表的头结点地址信息

      5. 然后就可以把事务产生的undo日志存入链表中

      

  • 相关阅读:
    XSLT的Replace函数
    Predicate<T> 委托
    《人生的智慧》第二章 人的自身
    Kmeans文本聚类:获取weka计算的聚类中心,完成文本聚类
    VCKbase转载:C++调用ADO
    Kmeans文本聚类系列之如何调用Preprocess类
    Kmeans文本聚类系列之全部代码
    近期计划
    Kmeans 聚类之建立文档向量模型(VSM)
    LibSVM文本分类之结果统计
  • 原文地址:https://www.cnblogs.com/isnotnull/p/14475554.html
Copyright © 2011-2022 走看看