纠纷奇闻社交美文家庭
投稿投诉
家庭城市
爱好生活
创业男女
能力餐饮
美文职业
心理周易
母婴奇趣
两性技能
社交传统
新闻范文
工作个人
思考社会
作文职场
家居中考
兴趣安全
解密魅力
奇闻笑话
写作笔记
阅读企业
饮食时事
纠纷案例
初中历史
说说童话
乐趣治疗

Undo日志用什么存储结构支持无锁并发写入?

5月9日 相见欢投稿
  redo日志只有崩溃恢复的时候才能派上用场,undo日志不一样,它承担着多重职责,MySQL崩溃恢复、以及正常提供服务期间,都有它的身影。
  按照使用频次,undo日志的多重职责如下:
  职责1,为MVCC服务,减少读写事务之间的相互影响,提升数据库的并发能力。
  职责2,保证数据库运行过程中的数据一致性。事务回滚时,把事务中被修改的数据恢复到修改之前的状态。
  职责3,保证数据库崩溃之后的数据一致性。崩溃恢复过程中,恢复没有完成提交的事务,并根据事务的状态和binlog日志是否写入了该事务的xid信息,共同决定事务是提交还是回滚。
  undo日志需要为数据一致性和MVCC服务,除了要支持多事务同时写入日志,还要支持多事务同时读取日志。
  为了有更好的读写并发性能,它拥有与redo日志完全不一样的存储结构。
  本文我们就来聊聊undo日志的存储结构,它是怎么写入undo日志文件的,以及事务二阶段提交过程中和它有关的操作。
  本文内容基于MySQL8。0。29源码。
  目录1。概述2。undo表空间3。回滚段3。1什么是回滚段?3。2分配回滚段4。undoslot4。1什么是undoslot?4。2寻找undoslot5。undo段5。1什么是undo段?5。2复用缓存的undo段5。3创建undo段6。undologheader6。1什么是undologheader?6。2复用undologheader6。3创建undologheader7。inode7。1什么是inode?7。2分配inode8。写undo日志9。二阶段提交之undo日志9。1prepare阶段9。2commit阶段10。总结
  正文1。概述
  undo日志的存储结构比较复杂,我们先以倒序的方式来介绍一下存储结构的各个部分,以便大家有个整体了解。
  undologheader:一个事务可能产生多条undo日志,也可能只产生一条undo日志,不管事务产生了多少条undo日志,这些日志都归属于事务对应的日志组,日志组由undologheader负责管理。
  undo页:undologheader和undo日志都存储于undo页中。
  undo段:为了多个事务同时写undo日志不相互影响,undo日志也使用了无锁设计,InnoDB会为每个事务分配专属的undo段,每个事务只会往自己专属undo段的undo页中写入日志。
  一个undo段可能会包含一个或多个undo页,多个undo页会形成undo页面链表。
  inode:每个undo段都会关联一个inode。
  undo段本身并不具备管理undo页的能力,有了inode这个外挂之后,undo段就可以管理段中的undo页了。
  回滚段:一个事务可能会需要14个undo段,很多个事务同时执行,就需要很多个undo段,这些undo段需要有一个地方统筹管理,这个地方就是回滚段。
  undoslot:一个回滚段管理着1024个undo段,每个回滚段的段头页中都有1024个小格子,用来记录undo段中第一个undo页的页号,这个小格子就叫作undoslot。
  重要说明:一个回滚段管理1024个undo段,这是基于innodbpagesize是16K(默认值)的情况,本文涉及到页大小的内容,都以16K的页为前提,后面就不再单独说明了。
  undo表空间:,一个回滚段能够管理1024个undo段,看起来已经很多了,假设每个事务都只需要1个undo段,如果只有一个回滚段也只能支持1024个事务同时执行。
  对于拥有几十核甚至百核以上CPU的服务器来说,这显然会限制它们的发挥。
  为了充分发挥服务器的能力,有必要支持更多事务的同时执行,所以就有了undo表空间,一个undo表空间最多可以支持128个回滚段。
  不止于此,InnoDB还能够最多支持127个undo表空间,这样算起来,所有回滚段总共能够管理的undo段数量是:102412812716646144。
  这么多undo段,是不是瞬间就有了地主家有余粮的感觉?
  看了上面的介绍,相信大家能对undo日志有个整体了解,接下来我们就按照这个整体结构,自顶向下来详细介绍其中的每个部分。2。undo表空间
  一个独立的undo表空间对应磁盘上的一个文件。
  MySQL8。0开始,强制开启了独立的undo表空间,支持创建2127个undo表空间,默认数量为2,可以通过CREATEUNDOTABLESPACE增加undo表空间,通过DROPUNDOTABLESPACE减少undo表空间。
  每个undo表空间都可以配置1128个回滚段,可以通过系统变量innodbrollbacksegments来控制每个undo表空间中的回滚段数量,默认值为128。
  每个undo表空间中,pageno3的页专门用于保存回滚段的段头页的页号,这个页的类型是FILPAGETYPERSEGARRAY,从Offset56开始,保存着128个回滚段的段头页的页号,如下图所示:
  3。回滚段3。1什么是回滚段?
  InnoDB中凡是被称为段的东西,都是用来管理数据页的一种逻辑结构。
  回滚段也不例外,它也是管理数据页的一种逻辑结构。
  回滚段管理了什么页呢?
  回滚段有一点点特殊,它只管理一个页,就是回滚段的段头页。
  每个回滚段中只有段头页这一个数据页,由此可见,管理数据页并不是它最重要的职责。
  在概述小节,我们介绍过,每个回滚段中都有1024个undoslot,可以统筹管理1024个undo段,这才是回滚段最重要的职责。
  基于前面的介绍,我们可以给回滚段下一个定义了:回滚段是一种逻辑结构,它负责段头页的分配,以及管理其中1024个undoslot对应的undo段。3。2分配回滚段
  开启一个读写事务,或者一个只读事务转换为读写事务时,InnoDB会为事务分配一个回滚段。
  默认配置下,2个undo表空间总共有256个回滚段,这么多回滚段,就涉及到怎么均衡使用的问题了。
  2个undo表空间,在内存是中一个数组,下标分别为0、1。
  每个undo表空间中128个回滚段,在内存中也是一个数组,下标为0127。
  以undo表空间下标、回滚段下标组成一个元组,用于表示默认配置下的256个回滚段,如下:
  (0,0)、(0,1)、、(0,126)、(0,127)
  (1,0)、(1,1)、、(1,126)、(1,127)
  分配回滚段的逻辑是按照undo表空间、回滚段轮流着来,顺序是这样的:
  (0,0)、(1,0)、(0,1)、(1,1)、、(0,126)、(1,126)、(0,127)、(1,127)
  分配顺序用图片展示是这样的(按照箭头顺序分配):
  每次分配时,都会记录这次分配的是哪个回滚段。
  下次再分配时,按照上面的顺序,把最后一次分配的回滚段之后的那个回滚段分配给事务。
  InnoDB中的回滚段,分为普通表回滚段、用户临时表回滚段,前面介绍的是普通表回滚段分配逻辑。
  用户临时表只有一个独立undo表空间,默认128个回滚段,需要分配临时表回滚段时,只要轮流分配就行了。4。undoslot4。1什么是undoslot?
  每个回滚段的段头页中都有1024个小格子,每个小格子就是一个undoslot,用于记录分配给事务的undo段的段头页页号,如下图所示:
  4。2寻找undoslot
  一条DML语句即将要修改数据之前,会先记录undo日志。
  记录undo日志之前,需要先创建一个undo段。
  undo段要把自己交给回滚段管理,这需要在回滚段的段头页中找一个undoslot占个位。
  寻找undoslot的过程简单粗暴,从回滚段1024个slot中的第一个slot开始遍历,读取slot的值,只要slot的值等于FILNULL,就说明这个slot没有被别的undo段占用,当前undo就可以占上这个位置。
  FILNULL是32位无符号整数的最大值,十六进制表示为0xFFFFFFFFUL,十进制表示为4294967295。
  如果遍历到最后一个slot,都没有发现值FILNULL的slot,那就说明分配给当前事务的回滚段没有可用的slot了。
  这种情况下,InnoDB并不会再重新给事务分配一个回滚段,而是直接报错:Toomanyactiveconcurrenttransactions。5。undo段5。1什么是undo段?
  undo段,也是段字辈,那它自然也是管理数据页的一种逻辑结构了。
  undo段管理的数据页就是用来存放undo日志的页,也就是undo页。
  按照对于表的增、删、改操作是否需要记录redo日志来分类,undo段可以分为2种类型:
  临时表undo段,对于用户临时表的增、删、改操作,数据库崩溃之后重新启动,不需要恢复这些表里的数据,也就是说临时表里的数据不需要保证持久性,因此不需要记录redo日志。
  但是,如果事务对用户临时表进行了增、删、改操作,事务回滚时,用户临时表中的数据也需要回滚,所以需要记录undo日志。
  普通表undo段,对于普通表的增、删、改操作,数据库崩溃之后重新启动,需要把这些操作修改过的数据,恢复到数据库崩溃时的状态,所以需要记录redo日志。
  事务回滚时,对于普通表进行的增、删、改操作,表中的数据也需要回滚,所以需要记录undo日志。
  按照增、删、改操作来分类,undo段也可以分为2种类型:
  insertundo段,用于保存insert语句产生的undo日志的undo段。
  updateundo段,用于保存update、delete语句产生的undo日志的undo段。
  为什么要区分insertundo段和updateundo段?
  因为insert语句产生的undo日志,在事务提交时,如果undo段不能被缓存起来复用,就会直接释放。
  update、delete语句产生的undo日志,在事务提交时,如果undo段不能被缓存起来复用,也不会直接释放,而是要服务于MVCC。
  等到undo日志中的历史版本数据不再被其它事务需要时,这些undo日志才能被清除。
  关于undo日志什么时候能被清除的细节,留到purge线程清理undo日志的文章再写。
  此时,如果undo日志所在的undo段中没有其它有效的undo日志时,undo段才能被释放。
  按照前面的2种维度分类,可以形成4种类型的undo段:插入记录到用户临时表,是临时表insertundo段。更新、删除用户临时表中的记录,是临时表updateundo段。插入记录到普通表,是普通表insertundo段。更新、删除普通表中的记录,是普通表updateundo段。
  在同一个事务中,以上4种类型的undo段都有可能出现,所以,一个事务中就可能会需要14个undo段。5。2复用缓存的undo段
  每创建一个undo段,需要经过一系列的操作:从inode页中找到一个未被使用的inode。分配一个inode页(可能需要)。为undo段分配一个undo页。初始化内存中的undo段对象。初始化内存中的undologheader对象。其它操作。。。
  这些初始化操作都是需要时间的,频繁创建就有点浪费时间了。为此,InnoDB设定了一个规则,在事务提交时,符合规则的undo段就可以被缓存起来,给后面的事务重复使用。
  undo段可缓存复用的规则,本文后面二阶段提交的commit阶段会介绍。
  前面介绍过,事务中使用undo段时,按照2种维度分类会形成4种类型的undo段,这是不是有点复杂?
  undo段缓存就比较简单了,只分了2种:insertundo段、updateundo段。
  有了undo段缓存之后,就不需要每次分配undo段时都从头开始创建一个了。
  如果要为用户临时表、普通表的insert语句分配一个undo段,就去insertundocached链表中(缓存insertundo段的链表)看看有没有undo段可以复用。
  如果有,就取链表中的第一个undo段来用;如果没有,就创建一个新的insertundo段。
  如果要为用户表、普通表的update、delete语句分配一个undo段,就去updateundocached链表中(缓存updateundo段的链表)看看有没有undo段可以复用。
  如果有,就取链表中的第一个undo段来用;如果没有,就创建一个新的updateundo段。5。3创建undo段
  InnoDB给事务分配一个undo段时,如果没有缓存的undo段可以复用,需要创建一个新的undo段。
  创建一个新的undo段,会经历以下几个主要步骤:
  第1步,找到一个inode,undo段会关联一个inode,用于管理段中的页。
  inode后面会有一个小节单独介绍,这里先不展开。
  第2步,从表空间0号页的FileSpaceHeader中读取FSPSEGID,作为新创建的undo段的ID(segid),把segid写入inode的FSEGID字段,表示这个inode已经被占用了。
  第3步,通过inode分配一个新的空闲页作为undo段的段头页。
  每个undo段都会有一个UndoSegmentHeader,位于undo段的段头页中,如下图所示:
  第4步,把inode的地址信息写入UndoSegmentHeader的TRXUNDOFSEGHEADER字段。
  inode的地址由3个部分组成:inode所在页的表空间IDinode所在页的页号inode在页中的Offset
  第5步,把段头页加入undo页面链表的最后,undo页面链表的基结点位于UndoSegementHeader的TRXUNDOPAGELIST字段中。
  第6步,把undo段的段头页页号写入回滚段中分配给当前undo段的undoslot中,表示这个undoslot被占用了。
  经过以上步骤后,undo段就创建成功了,可以继续进行接下来的操作了。6。undologheader6。1什么是undologheader?
  一个事务产生的undo日志属于一个日志组,undologheader是日志组的头信息,各字段如下图所示:
  介绍几个主要字段:
  TRXUNDOTRXID,产生这组undo日志的事务ID。
  TRXUNDOTRXNO,事务的提交号,事务提交时会修改这个字段的值。
  TRXUNDONEXTLOG,undo段中下一组undo日志的undologheader在页中的Offset。
  TRXUNDOPREVLOG,undo段中上一组undo日志的undologheader在页中的Offset。
  TRXUNDOHISTORYNODE,表示这组undo日志是history链表的一个结点,purge线程清理TRXUNDOUPDATE类型的undo日志时会用到这个字段。6。2复用undologheader
  如果分配给事务的insertundo段,是从insertundocached链表中获取的,undo段中的undologheader是可以直接复用的,但是其中4个字段需要重新初始化:
  TRXUNDOTRXID,写入新的事务ID。
  TRXUNDOLOGSTART,重置为undologheader之后的位置,表示可以写undo日志的位置。
  TRXUNDOFLAGS,undo日志组的标记重置为0。
  TRXUNDODICTTRANS,表示当前这组undo日志是否由DDL语句事务产生。
  由于updateundo段中的undo日志未被清理之前都需要为MVCC服务,如果分配给事务的updateundo段是复用的undo段,不能复用其中的undologheader,而是会生成一个新的undologheader,追加到上一个事务生成的undo日志之后的位置。6。3创建undologheader
  新创建一个insertupdateundo段,或者复用一个updateundo段时,都需要创建一个undologheader。
  创建一个新的undologheader,就是把undologheader中的每个字段值按顺序写入undo页中,然后在内存中也会生成一个对应的结构(structtrxundot),并初始化其中的各个字段。
  需要单独拿出来说的字段有两个:
  TRXUNDOPREVLOG,指向updateundo段中上一个事务生成的undo日志在updateundo段的段头页中的Offset。
  TRXUNDONEXTLOG,指向updateundo段下一个事务生成的undo日志在updateundo段的段头页中的Offset。
  通过这两个字段,updateundo段中的多组undo日志就形成了链表,purge线程清理undo日志时就可以通过链表找到undo页中的所有undo日志了。7。inode7。1什么是inode?
  不管是回滚段、undo段,还是索引段,只要是段,都会关联一个inode。
  inode是真正用于管理与它关联的段中数据页的逻辑结构,undo段之所以能够管理其中的undo页,关键就是因为undo段关联了inode。
  inode结构如下图所示:
  由上图可见,inode中有32个fragmentpageslot,可以管理32个碎片页。
  还有3个以extent为单位管理数据页的链表:
  页大小为16K时,1个extent中有64个页。
  FSEGFREE,这个链表的每个extent中,所有页都没有被使用,全都是空闲页。
  FSEGNOTFULL,这个链表的每个extent中,都有一部分页已被使用,另一部分页是空闲页。
  FSEGFULL,这个链表的每个extent中,所有页都已经被使用,没有空闲页。
  根据页的分配规则,inode关联的段每分配一个页,既有可能从32个fragmentpageslot中找一个空闲页,也有可能从FSEGFREE、FSEGNOTFULL中找一个空闲页。7。2分配inode
  undo表空间中,有专门的inode页用于存放inode,每个inode占用192字节,16K的inode页最多能够存放85个inode,如下图所示:
  undo表空间0号页的FileSpaceHeader中,有2个管理inode页的链表:
  FSPSEGINODESFULL,这个链表中的所有inode页都存放了85个inode,不能再存入新的inode。
  FSPSEGINODESFREE,这个链表中的所有inode页都还有空闲空间可以存入新的inode。
  FileSpaceHeader中的各字段如下图所示:
  每次为事务创建一个新的undo段之前,都会先从FSPSEGINODESFREE链表的第一个inode页中获取一个可用的inode。
  从inode页中获取inode的逻辑简单粗暴:
  从inode页中的第一个inode开始遍历,直到找到一个FSPSPACEID字段值为0的inode,表示这个inode未被其它undo段占用,可以分配给当前undo段。
  不过,有可能会出现一个意外情况,就是FSPSEGINODESFREE链表中没有可用的inode页。
  这种情况下,需要先从undo表空间中分配一个碎片页,用作inode页,然后再按照前面介绍的分配inode逻辑,给当前undo段分配一个inode。8。写undo日志
  本文不会详细介绍undo日志的格式,但是,每一种类型的undo日志中,都有2个字段,用于把undo日志组中的多条日志组成日志链表,需要介绍一下。
  每一条undo日志中,第一个字段是nextrecordstart,占用2字节,保存着下一条undo日志的第一个字节在undo页中的Offset。
  每一条undo日志中,最后一个字段是recordstart,占用2字节,保存着当前这条undo日志第一个字节在undo页中Offset。
  nextrecordstart、recordstart是为了描述方便而取的名字。
  通过这2个字段,同一个undo页中的多条undo日志可以形成一个双向链表,如下图所示:
  从前往后遍历undo日志时,通过nextrecordstart就可以直接读取到下一条undo日志的Offset了。
  从后往前遍历undo日志时,通过recordstart只能读取到本条日志的Offset,再读取本条日志的Offset2处的字段内容,就能得到上一条undo日志的Offset了。
  为什么要把nextrecordstart作为undo日志的第一个字段,recordstart作为undo日志的最后一个字段?
  我第一次看到undo日志的这个结构,是在看《MySQL是怎样运行的》这本书的时候,当时感觉这样的结构很不好理解。
  研究完源码写本文的时候,我试图为这个结构找到一个合理的解释,以方便大家理解。
  但是,想了几种不使用这个结构可能会带来的坏处(例如:占用更多存储空间,遍历undo日志的时候需要更多时间等等),都没有找到必须要这样设计的合理解释。
  所以,大家也不用纠结为什么会这样设计,就当它是个普通的链表指针就好了。
  当然了,如果有哪位小伙伴发现了这么设计的好处,也欢迎在文末留言或者微信交流。
  正常写入undo日志的过程比较简单:先写undo日志数据。再写nextrecordstart、recordstart在其所处undo页中的Offset。
  undo日志是每产生一条就往undo日志文件中写入一条(只是写到BufferPool中undo页,由刷盘操作统一刷新到磁盘)。
  写undo日志中每个字段的细节就不再展开了。
  写undo日志的过程中可能会面临一个临界点:
  前面我们提到过,undo段是可以复用的,对于复用的insertundo段,逻辑比较简单,直接覆盖undo段中原来的日志数据就可以了。
  对于复用的updateundo段,由于其中的undo日志还需要为MVCC服务,不能被覆盖,需要把新的undo日志追加到原来的undo日志之后。
  这样一来就可能会出现2种情况:
  情况1,undo页中剩余空间足够写入一条新的undo日志,这种情况就简单了,直接把新的undo日志写入undo页中剩余的空间。
  情况2,undo页中剩余空间不够写入一条新的undo日志了,这种情况稍微复杂点,会分三步进行:把undo页中剩余空间的所有字节全部填充为0xff。创建一个新的undo页。把undo日志写入到新的undo页中。
  还有一种不应该出现的情况:
  由于一条undo日志内容太长,一个空闲的undo页都存不下一条undo日志。
  正常情况下不会发生这样的事情,只有MySQL源码有bug的时才会出现。
  text、blob系列大字段,存储的内容长度可能超过undo页的大小,更新操作的undo日志有可能会超过undo日志的大小吗?
  如果源码没bug的话,不会超过的,对于text、blob系列大字段,记录undo日志时并不是直接把字段内容原封不动的写到undo日志里,而是会做一些处理,只会有少量内容写到undo日志里。
  关于text、blob系列大字段具体会往undo日志里写入什么,如果有小伙伴感兴趣,可以留言或者微信交流,后续我可以再进一步研究这些细节,然后写篇文章单独介绍。9。二阶段提交之undo日志9。1prepare阶段
  在prepare阶段,undo日志为事务做的最重要的2件事:修改undo段状态,把UndoSegementHeader的TRXUNDOSTATE字段值从TRXUNDOACTIVE修改为TRXUNDOPREPARED,表示undo日志对应的事务已经进入prepare阶段。把事务xid信息写入UndologHeader中。
  undo段状态用于崩溃恢复过程中,标记哪些事务需要恢复,哪些事务不用恢复。
  xid信息用于崩溃恢复过程中,决定数据库崩溃时处于prepared阶段的事务,是要回滚还是要提交。9。2commit阶段
  到了commit阶段,insertundo日志的使命就已经结束了,updateundo日志还需要为MVCC服务。
  不管是insertundo段还是updateundo段,只要满足以下2个条件都可以被缓存起来复用:undo段中只有一个undo页。包括FileHeader、PageHeader在内,undo页已使用空间必须小于undo页总字节数的四分之三。
  对于insertundo段,如果能复用,会进行以下操作:
  步骤1,UndoSegmentHeader的TRXUNDOSTATE字段值由TRXUNDOPREPARED变为TRXUNDOCACHED。
  步骤2,事务对应undo日志组的undologheader对象加入回滚段insertundocached链表的最前面,以备下一个事务复用。
  如果不能复用,会进行以下操作:
  步骤1,UndoSegementHeader的TRXUNDOSTATE字段值由TRXUNDOPREPARED变为TRXUNDOTOFREE。
  步骤2,undo段关联的inode的FSEGID字段改为0,表示inode可以被其它undo段使用,然后释放undo段中分配的所有undo页。
  步骤3,把insertundo段占用的undoslot值会改为FILNULL,表示这个undoslot处于空闲状态,可以被其它事务使用了。
  对于updateundo段,如果能复用,会进行以下操作:
  步骤1,UndoSegmentHeader的TRXUNDOSTATE字段值由TRXUNDOPREPARED变为TRXUNDOCACHED。
  步骤2,通过undologheader字段TRXUNDOHISTORYNODE把undo日志组加入historylist链表。
  purge线程通过遍历historylist链表来清除undo日志。
  步骤3,把事务提交号写入undologheader字段TRXUNDOTRXNO。
  purge线程用这个字段来判断undo日志是否能够被清除、标记删除的记录是否能够彻底删除。
  步骤4,事务对应undo日志组的undologheader对象加入回滚段updateundocached链表的最前面,以备下一个事务复用。
  如果不能复用,会进行以下操作:
  步骤1,UndoSegementHeader的TRXUNDOSTATE字段值由TRXUNDOPREPARED变为TRXUNDOTOPURGE。
  步骤2,updateundo段占用的undoslot的值改为FILNULL,表示这个undoslot处于空闲状态,可以被其它事务使用了。
  步骤3,从回滚段RollbackSegnemtHeader中读取TRXRSEGHISTORYSIZE,加上undo段中undo页的数量,然后回写到TRXRSEGHISTORYSIZE中,作为historylist链表中最新的undo页数量。
  undo能够复用时,不会修改TRXRSEGHISTORYSIZE字段值。
  步骤4,通过undologheader字段TRXUNDOHISTORYNODE把undo日志组加入historylist链表。
  purge线程通过遍历historylist链表来清除undo日志。
  步骤5,把事务提交号写入undologheader字段TRXUNDOTRXNO。
  purge线程用这个字段来判断undo日志是否能够被清除、标记删除的记录是否能够彻底删除。
  小结一下,commit阶段,就是undo段能复用就复用,不能复用就直接清理释放(insertundo段),或者等待purge线程清理释放(updateundo段)。10。总结
  InnoDB支持2127个独立表空间,每个表空间支持1128个回滚段,每个回滚段支持1024个undoslot,可以管理1024个undo段。
  undo段可以分为4种类型:临时表insertundo段、临时表updateundo段、普通表insertundo段、普通表updateundo段。
  如果undo段中只有1个undo页,并且undo页中已使用空间小于undo页大小的四分之三,undo段可以被缓存起来复用。
  可以复用的insertundo段缓存到insertundocached链表,可用复用的updateundo段缓存到updateundocached链表。
  每个undo段都会关联一个inode,用于管理段中的页,inode存放于表空间的inode页中。
  一个事务产生的一条或多条undo日志会形成一个日志组,日志组由undologheader负责管理。
  多条undo日志通过日志中的nextrecordstart、recordstart形成双向链表。
  写undo日志时,如果复用的updateundo段的段头页中剩余空间不够存放一条undo日志时,会分配一个新的undo页,并把undo日志写入到新的undo页中。
  以上就是本文的全部内容了,如果本文对你有所帮助,还请帮忙转发、点赞,谢谢

刘亦菲和她妈妈谁好看?刘亦菲曾经说过她是她们家里最丑的那一个,这肯定是谦虚啦。刘亦菲跟妈妈绝对是颜值超高的一对母女啦,她们的颜值经常被放在一起讨论。刘亦菲的妈妈精致大气又端庄,稳妥妥的大……就在云澜湾!嘉兴首家羊驼主题公园大年初一迎客来源:【嘉兴日报嘉兴在线】瑞兔呈祥,春节将至,想好去哪儿玩了吗?1月22日至2月5日,嘉兴首家羊驼主题公园与你有约,嘉善云澜湾四季花海乐园将在这个假期圆你一个关于欢乐和自……中国股市2022年新能源汽车概念,有望突破高成长的五大潜力股什么是新能源汽车?顾名思义,新能源汽车就是采用新动力来源的汽车。什么是新能源,那就是用非常规石化燃料作为动力来源。也就是不使用汽油、柴油、天然气、甲醇等这些燃料作为动力来……上市八个月降价2100元,4K屏IP68防水,微单手机依然无我们每次推荐手机的时候,都会不厌其烦地介绍手机的摄像头,因为对于现在的智能手机来说,手机摄像头绝对是目前智能手机中非常重要的一个配置,同样的这也是中高端手机中,非常容易拉开举例……微信2022新年的第一波红包封面来了IT之家12月23日消息,据微信官方发布,2022新年的第一波红包封面来了。微信搜一搜品牌官方区联合17个品牌,带来一大波跨年抢跑福利。今年特意提供开抢倒计时功能,可提前……得过两种癌症,却活到98岁,季羡林的三不养生法,值得借鉴生活中,有的人一直在坚持的养生方法,最后却使得自己的身体越来越差,而也有的人一生都多灾多病,最后却比较高寿,甚至比常人的寿命还高。是因为他们更加幸运吗?其实并非如此,很大的原因……以人为本是桂城高质量发展的本质柳立子之视点篇本次桂城深调研能够充分感受到‘以人为本’四个字,从我的角度理解,桂城的发展抓住了高质量发展的本质,充分践行着坚持以人民为中心的发展思想。在桂城走企业、……求求了,别再逼刘亦菲了!头条创作挑战赛最近,全网都被杨紫琼刷屏了!在第95届奥斯卡颁奖典礼上,她夺得了奥斯卡影后的桂冠。至此,成为了该历史上第一位华人影后。而与此同时,内娱也掀……落后48分,落后44分!西部豪门创耻辱纪录,波波维奇要抢文班NBA官方公布了第7周球队战力榜,排名第1的是绿军,排名倒数第1的是马刺。这是绿军连续4周霸占榜首,也是马刺连续2周垫底联盟。绿军的强势崛起与双探花正式步入巅峰期有很大的关系,……烟台市区将添一处亲海公园!五一前开放!来源:【烟台日报大小新闻】好消息烟台市区海边将添亲海公园市民游客又多了一处周末假期游玩的好去处到底在什么地方快来一起看看吧作为中心区市……奚梦瑶晒女儿衣物,富养女儿超用心,给娃穿同款衣服难掩喜悦生完二胎宝宝,一直在家带娃的奚梦瑶又来跟大家分享好物了,这次奚梦瑶主要跟大家分享的是给娃买的收纳箱跟衣物,奚梦瑶大有育儿博主的味道呢,身为两个孩子的妈妈,在选择东西方面还是很有……衣服在配不在贵!今冬配齐这2裤2裙,玩转一整个季节的时髦众所周知,秋冬的时尚感与其它季节完全不一样,我们需要很好的兼顾美丽和低温保暖,这使得我们不得不适当的放弃一些心仪的选择。比如薄款的裤装和上衣,虽然好穿,但完全不御寒,短款……
苹果混合现实头显计划于2023年初推出次年有望具备通话功能据ETNews报道,苹果传闻中的混合现实头显将于2023年初推出,但具有实质性升级的第二代型号已经在开发中,将于2024年推出。据报道,供应商正准备在今年第四季度启动苹果头显的……美宇航局与SpaceX公司因故取消发射任务航天员在太空舱等待海外网作者李芳据美国有线电视新闻网报道,当地时间2月27日,在检测到地面系统出现问题后,美国太空探索技术公司(SpaceX)和美国宇航局取消将4名航天员送往国际空间站的发……聊MSN的看不起聊QQ的!微信之父是如何帮助QQ击败微软MS头条创作挑战赛导读2005年左右,当时的网络环境有着一条上网鄙视链:聊MSN的看不起聊QQ的。因为QQ当时更多的被人们认为是一款年轻人娱乐的软件,而MSN才是真正的社交工……猪油是世界上最健康的油?猪油渣能吃吗?现在终于有答案了立冬将至,漫长的冬天就要到了,冬天是蔬菜匮乏的季节,每到这个时候,很多人家里都习惯熬一盆猪油,洁白如雪,炒菜的时候来一勺,出锅香气扑鼻。记得小时候,最爱吃的就是熬猪油剩下……聊聊C右值引用和移动构造函数一:背景最近在看C的右值引用和移动构造函数,感觉这东西一时半会还挺难理解的,可能是没踩过这方面的坑,所以没有那么大的深有体会,不管怎么说,这一篇我试着聊一下。二:右值引用……冬天多给孩子吃这菜,贵也买,别舍不得,好消化长个不长胖入冬后,多给孩子吃这菜,贵也买,别舍不得,常食长个又健脑入冬后,天气寒冷,气候干燥,除了注意保暖,饮食调理非常重要,尤其是处于生长发育期的孩子,合理调配饮食,顺时进补更为……川西甘白路自驾攻略(异域星球大道)头条创作挑战赛如果说省道434是川西的景观大道,那么甘白路就是川西的异域星球大道。它起始于甘孜县,终于白玉县,全长约200多公里,今天这期菲夜就给喜欢自驾川西的朋友介绍这条甘白……人类登入太阳有哪些困难需要克服导语:人类如果要登陆太阳,需要克服哪些困难?在登陆月球之后,人类就像吃了兴奋剂,想着登陆其他行星进行实地探测。但通过各种器械观测和研究后,科学家们发现,除了月球可以允许宇……莫文蔚中式旗袍带老公参加珠宝展,何超琼杨紫琼尽显高贵香港卡地亚时尚珠宝艺术大展举行,各界名媛与娱乐圈人士纷纷出席。4月13日,莫文蔚带着德国老公Johannes现身珠宝展台,夫妻一起共同出席活动。Johannes此次……暗黑2重制版开荒时期各职业通用的符文之语大家好,我是小迷弟,刚入坑的小伙伴们有没有在开荒期被安达利尔、督瑞尔这俩货虐的不要不要的经历〔奸笑〕这里就介绍几件通用的特别强力的符文之语帮助小伙伴更加轻松的度过开荒期。……辽篮女友团大比拼!小高捡到宝金金樱木全都高颜值艾伦眼光不错赵继伟的婚礼已经过去了三天时间,欢乐和谐的气氛让人记忆犹新,算上赵继伟,辽宁队的众多老将大多数都已经有了归宿,韩德君和李晓旭已经娶妻生子,在总决赛最后时刻出场的五名球员当中,只……广言以消费开门红力拼全年红多个部门与支付平台日前发布的数据显示,在刚刚过去的春节假期里,我国出行旅游火爆,城市商圈客流量增多,春节档掀起观影热潮春节消费的表现为市场复苏开了个好头。与此同时,中央和地方纷……
友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找美丽时装彩妆资讯历史明星乐活安卓数码常识驾车健康苹果问答网络发型电视车载室内电影游戏科学音乐整形