MySQL数据库InnoDB存储引擎Log漫游(1)

本文是介绍MySQL数据库InnoDB存储引擎重做日志漫游

00 – Undo Log
Undo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版本并发控制(简称:MVCC)。

- 事务的原子性(Atomicity)
  事务中的所有操作,要么全部完成,要么不做任何操作,不能只做部分操作。如果在执行的过程中发生
  了错误,要回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过。

- 原理
  Undo Log的原理很简单,为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方
  (这个存储数据备份的地方称为Undo Log)。然后进行数据的修改。如果出现了错误或者用户执行了
  ROLLBACK语句,系统可以利用Undo Log中的备份将数据恢复到事务开始之前的状态。

除了可以保证事务的原子性,Undo Log也可以用来辅助完成事务的持久化。

- 事务的持久性(Durability)
  事务一旦完成,该事务对数据库所做的所有修改都会持久的保存到数据库中。为了保证持久性,数据库
  系统会将修改后的数据完全的记录到持久的存储上。

- 用Undo Log实现原子性和持久化的事务的简化过程
  假设有A、B两个数据,值分别为1,2。
  A.事务开始.
  B.记录A=1到undo log.
  C.修改A=3.
  D.记录B=2到undo log.
  E.修改B=4.
  F.将undo log写到磁盘。
  G.将数据写到磁盘。
  H.事务提交
  这里有一个隐含的前提条件:‘数据都是先读到内存中,然后修改内存中的数据,最后将数据写回磁盘’。

  之所以能同时保证原子性和持久化,是因为以下特点:
  A. 更新数据前记录Undo log。
  B. 为了保证持久性,必须将数据在事务提交前写到磁盘。只要事务成功提交,数据必然已经持久化。
  C. Undo log必须先于数据持久化到磁盘。如果在G,H之间系统崩溃,undo log是完整的,
     可以用来回滚事务。
  D. 如果在A-F之间系统崩溃,因为数据没有持久化到磁盘。所以磁盘上的数据还是保持在事务开始前的状态。

缺陷:每个事务提交前将数据和Undo Log写入磁盘,这样会导致大量的磁盘IO,因此性能很低。

如果能够将数据缓存一段时间,就能减少IO提高性能。但是这样就会丧失事务的持久性。因此引入了另外一
种机制来实现持久化,即Redo Log.

01 – Redo Log

- 原理
  和Undo Log相反,Redo Log记录的是新数据的备份。在事务提交前,只要将Redo Log持久化即可,
  不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是Redo Log已经持久化。系统可以根据
  Redo Log的内容,将所有数据恢复到最新的状态。

- Undo + Redo事务的简化过程
  假设有A、B两个数据,值分别为1,2.
  A.事务开始.
  B.记录A=1到undo log.
  C.修改A=3.
  D.记录A=3到redo log.
  E.记录B=2到undo log.
  F.修改B=4.
  G.记录B=4到redo log.
  H.将redo log写入磁盘。
  I.事务提交

- Undo + Redo事务的特点
  A. 为了保证持久性,必须在事务提交前将Redo Log持久化。
  B. 数据不需要在事务提交前写入磁盘,而是缓存在内存中。
  C. Redo Log 保证事务的持久性。
  D. Undo Log 保证事务的原子性。
  E. 有一个隐含的特点,数据必须要晚于redo log写入持久存储。

- IO性能
  Undo + Redo的设计主要考虑的是提升IO性能。虽说通过缓存数据,减少了写数据的IO.
  但是却引入了新的IO,即写Redo Log的IO。如果Redo Log的IO性能不好,就不能起到提高性能的目的。
  为了保证Redo Log能够有比较好的IO性能,InnoDB 的 Redo Log的设计有以下几个特点:

  A. 尽量保持Redo Log存储在一段连续的空间上。因此在系统第一次启动时就会将日志文件的空间完全分配。
     以顺序追加的方式记录Redo Log,通过顺序IO来改善性能。
  B. 批量写入日志。日志并不是直接写入文件,而是先写入redo log buffer.当需要将日志刷新到磁盘时
     (如事务提交),将许多日志一起写入磁盘.
  C. 并发的事务共享Redo Log的存储空间,它们的Redo Log按语句的执行顺序,依次交替的记录在一起,
     以减少日志占用的空间。例如,Redo Log中的记录内容可能是这样的:
     记录1: <trx1, insert …>
     记录2: <trx2, update …>
     记录3: <trx1, delete …>
     记录4: <trx3, update …>
     记录5: <trx2, insert …>
  D. 因为C的原因,当一个事务将Redo Log写入磁盘时,也会将其他未提交的事务的日志写入磁盘。
  E. Redo Log上只进行顺序追加的操作,当一个事务需要回滚时,它的Redo Log记录也不会
     Redo Log中删除掉。

02 – 恢复(Recovery)

- 恢复策略
  前面说到未提交的事务和回滚了的事务也会记录Redo Log,因此在进行恢复时,这些事务要进行特殊的
  的处理.有2中不同的恢复策略:

  A. 进行恢复时,只重做已经提交了的事务。
  B. 进行恢复时,重做所有事务包括未提交的事务和回滚了的事务。然后通过Undo Log回滚那些
     未提交的事务。

- InnoDB存储引擎的恢复机制
  MySQL数据库InnoDB存储引擎使用了B策略, InnoDB存储引擎中的恢复机制有几个特点:

  A. 在重做Redo Log时,并不关心事务性。 恢复时,没有BEGIN,也没有COMMIT,ROLLBACK的行为。
     也不关心每个日志是哪个事务的。尽管事务ID等事务相关的内容会记入Redo Log,这些内容只是被当作
     要操作的数据的一部分。
  B. 使用B策略就必须要将Undo Log持久化,而且必须要在写Redo Log之前将对应的Undo Log写入磁盘。
     Undo和Redo Log的这种关联,使得持久化变得复杂起来。为了降低复杂度,InnoDB将Undo Log看作
     数据,因此记录Undo Log的操作也会记录到redo log中。这样undo log就可以象数据一样缓存起来,
     而不用在redo log之前写入磁盘了。
     包含Undo Log操作的Redo Log,看起来是这样的:
     记录1: <trx1, Undo log insert <undo_insert …>>
     记录2: <trx1, insert …>
     记录3: <trx2, Undo log insert <undo_update …>>
     记录4: <trx2, update …>
     记录5: <trx3, Undo log insert <undo_delete …>>
     记录6: <trx3, delete …>
  C. 到这里,还有一个问题没有弄清楚。既然Redo没有事务性,那岂不是会重新执行被回滚了的事务?
     确实是这样。同时Innodb也会将事务回滚时的操作也记录到redo log中。回滚操作本质上也是
     对数据进行修改,因此回滚时对数据的操作也会记录到Redo Log中。
     一个回滚了的事务的Redo Log,看起来是这样的:
     记录1: <trx1, Undo log insert <undo_insert …>>
     记录2: <trx1, insert A…>
     记录3: <trx1, Undo log insert <undo_update …>>
     记录4: <trx1, update B…>
     记录5: <trx1, Undo log insert <undo_delete …>>
     记录6: <trx1, delete C…>
     记录7: <trx1, insert C>
     记录8: <trx1, update B to old value>
     记录9: <trx1, delete A>
     一个被回滚了的事务在恢复时的操作就是先redo再undo,因此不会破坏数据的一致性.

- InnoDB存储引擎中相关的函数
  Redo: recv_recovery_from_checkpoint_start()
  Undo: recv_recovery_rollback_active()
  Undo Log的Redo Log: trx_undof_page_add_undo_rec_log()

 

MySQL数据库InnoDB存储引擎事务日志系列文章:
MySQL数据库InnoDB存储引擎Log漫游(2)
MySQL数据库InnoDB存储引擎Log漫游(3)

27 thoughts on “MySQL数据库InnoDB存储引擎Log漫游(1)

  1. Pingback: MySQL数据库InnoDB存储引擎Log漫游 | MySQLOPS 数据库与运维自动化技术分享

  2. Pingback: MySQL数据库InnoDB存储引擎Log漫游 | MySQLOPS 数据库与运维自动化技术分享

  3. 精华文章留名。之前一直对 undo、redo、mvcc之间是如何协同工作,ACID的实现的感到困惑。但是还是有一点疑问请教作者:undo数据会写共享表空间,这个写操作是合适发生的?

    • rollback segment的内容被当作数据,和其他数据一样会出现在LRU链表和flush_list中。何时写盘也和普通数据一样由buffer pool来控制。当buffer pool空间不足时或者需要做同步checkpoint时,就会被写入磁盘。

  4. Pingback: MySQL数据库InnoDB存储引擎 多版本控制(MVCC)实现原理分析 | MySQLOPS 数据库与运维自动化技术分享

  5. 你好,请教一个问题哈,网上资料都说myisam的查询性能要比innodb高。 是这样的吗?如果是的话高的原因是什么呢?

    • 若是同一条记录非主键的方式,且从磁盘上读,因存储结构的问题,innodb存储引擎要花费更多的io操作,本站有介绍的资料,请站内搜索

  6. Pingback: MySQL数据库分布式事务XA优缺点与改进方案 | MySQLOPS 数据库与运维自动化技术分享

  7. Pingback: MySQL数据库InnoDB存储引擎 Insert Buffer实现机制详解 | MySQLOPS 数据库与运维自动化技术分享

  8. Pingback: MySQL数据库分布式事务XA的实现原理分析 | MySQLOPS 数据库与运维自动化技术分享

  9. Pingback: MySQL数据库InnoDB存储引擎 innodb_buffer_pool_size的Buffer Pool页面管理 | MySQLOPS 数据库与运维自动化技术分享

  10. Pingback: MySQL数据库InnoDB存储引擎 Buffer Pool页面分配详解 | MySQLOPS 数据库与运维自动化技术分享

  11. 如果undo和redo都写在一个log中,这个我还是不明白,恢复的时候,先做redo,再做undo,没有事务的界限,那岂不是可能会看到一个事务被执行一半的那种情况么?这样事务的原子性不就是不存在了么?
    还有日志都放在内存里,数据库出现故障当掉了,重启的时候数据又是怎么恢复的呢?

    • undo log和 redo log本身是分开的。innodb的undo log是记录在数据文件(ibd)中的。而且innodb将undo log的内容看作是数据,因此对undo log本身的操作(如向undo log中插入一条undo记录等),都会记录redo log.
      因此当插入一条记录或删除一条记录时会作以下几件事:

      1. 向undo log中插入一条“插入|删除数据”的undo log记录。

      2. 向redo log中插入一条”插入undo log记录“的redo log记录。

      3. 插入数据。

      4. 向redo log中插入一条”插入|删除数据”的redo log记录。

      之所以要对undo log上的操作记入redo log,就是为了保证undo log的完整性(和数据是一样的). undo log可以不必立即持久化到磁盘上。即便丢失了,也可以通过redo log将其恢复。
      在重做redo log之后, undo log中的记录就是完整的了。事务性是由事务的undo log控制的,undo log中记录有事务的当前状态,如果事务的状态是active的则会相应的条件进行回滚操作。

  12. “例如,Redo Log中的记录内容可能是这样的:
    记录1:
    记录2:
    记录3:

    redo log好像是记录物理变化,所以不是这种逻辑形式。

    • 不好意思,我没有说清楚。Redo log是页内逻辑的,页外物理的。
      第一部分,没有探讨Redo的格式,这里这样写只是为了,方便理日志处理的基本原理。

  13. Pingback: MariaDB数据库与Percona数据库XtraDB存储引擎的Group Commit实现原理分析 | MySQLOPS 数据库与运维自动化技术分享

  14. Pingback: MySQL数据库InnoDB存储引擎Log漫游2 | EvilCode 邪恶代码

    • 写redo log分为3步:
      1. mtr commit 时写入 redo log buffer.
      2. redo log buffer 写入redo log 文件.
      3. flush redo log 到磁盘。
      每个语句在内部执行时可能会分成多个mtr 来执行。为了保证不丢数据,必须要在事物提交时将该事物的所有redo log写入文件并flush到磁盘。因此事物在提交时会做2和3. 但是redo log buffer 和redo log file是所有并发事物共用的,因此有可能一个事物提交时将另外一个未提交的事物的部分redo log 写入文件并刷入磁盘。
      redo log buffer 是有限的,在buffer空间不够时,redo log也会从buffer中写入文件. 这时不会强制flush到磁盘。

      • 假设有A、B两个数据,值分别为1,2.
        A.事务开始.
        B.记录A=1到undo log.
        C.修改A=3.
        D.记录A=3到redo log.
        E.记录B=2到undo log.
        F.修改B=4.
        G.记录B=4到redo log.
        H.将redo log写入磁盘。
        I.事务提交
        这里面有个疑惑,在事务提交之前 也就是H步骤将redo log写入磁盘,那这样 是靠什么机制来写入的难道是有一行数据改变就立马写入redo log并刷到那这样岂不是IO非常大。我的理解是不是在事务提交的时候,直接把整个 redo buffer 刷到磁盘。但这样的话,就会丢失最后一个事务了,如果在提交后系统崩溃,redo buffer只是刷了一部分。

        • 事务刷新频率是可以设置的,必须考虑性能和数据丢失风险,故一般要求数据库主机的RAID卡电池和UPS,以及InnoDB存储引擎关闭的模式等综合保证

  15. Pingback: sql 优化 - 开发者问答

  16. Pingback: MySQl数据表中的ID设置为自增,为什么中间会出现ID编号不连续的情况 - MySQL - 开发者问答

  17. Hi there! My partner and I often publish guest articles for other weblog owners to
    help gain exposure to our work, as well as provide superb content to site
    owners. It truly is a win win situation! If you happen to be interested feel free to contact me
    at: arnold-cowley@aol.com so we can talk further. Thanks
    alot :) !

    Also visit my web page … trafficker

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>