Java笔试面试Java

MySQL InnnoDB 存储引擎事务

2021-09-25  本文已影响0人  GeorgeMR

事务(Transaction)是数据库区别于文件系统的重要特性之一。事务会把数据库从一种一致状态转换为另一种一致状态。
事务可由一条非常简单的 SQL 语句组成,也可以由一组复杂的 SQL 语句组成。事务是访问并更新数据库中各种数据项的一个程序执行单元。在事务中的操作,要么都做修改,要么都不做。

事务的概念

InnoDB 存储引擎中的事务完全符合 ACID 的特性:

事务的实现

事务的隔离性由锁来实现,关于 InnoDB 存储引擎中的锁,可以参考本人之前的一篇文章
原子性、一致性、持久性通过数据库的 redo log 和 undo log 来完成。redo log 称为重做日志,用来保证事务的原子性和持久性。undo log 用来保证事务的一致性。

redo

redo log 用来实现事务的持久性,记录的是对于每个页的修改
其由两部分组成:

InnoDB 存储引擎通过 Force Log at Commit 机制来实现事务的持久性,即当事务提交(COMMIT)使,必须先讲该事务的所有日志写入到重做日志文件进行持久化,待事务的 COMMIT 操作完成才算完成。为了确保每次日志都写入重做日志文件,在每次将重做日志缓冲写入重做日志文件后,InnoDB 存储引擎都需要调用一次 fsync 操作(确保重做日志从文件系统缓存写入到磁盘)。

与 binlog 的区别

接触过 MySQL 的同学应该多少都会了解 binlog,会发现 binlog 与 redo log 非常相似,都是记录了对于数据操作的日志,为什么还要引入 redo log?binlog的两个重要使用场景:

  1. MySQL 主从复制
  2. 数据恢复

但是两者是存在非常大的区别的:

InnoDB 存储引擎在启动时不管上次数据库运行时是否正常关闭,都会尝试进行恢复操作。redo log 记录的是物理日志,恢复速度比 binlog 要快很多,而且InnoDB 恢复还进行了一定程度的优化-顺序读取及并行应用redo log。

group commit

事务的两阶段提交

  1. 修改内存中事务对应的信息,并且将日志写入重做日志缓冲
  2. 调用 fsync 确保日志都从重做日志缓冲写入磁盘

若事务为非只读事务,则每次事务提交时需要进行一次 fsync 操作,以此保证重做日志已经写入磁盘。

因为磁盘的 fsync 性能时有限的,即上面的第二阶段是个较慢的过程。但是当有事务进行第二阶段提交时,其他事务可以进行第一阶段的提交,正在提交的事务完成提交操作后,再次进行第二阶段,即 group commit ,一次 fsync 可以刷新确保多个事务日志被写入文件。

但是开启 binlog 之后,group commit 功能就会失效。这是因为开启 binlog 之后,需要保证binlog 和 redo log 的一致性,二者之间使用了两阶段事务,步骤如下:

  1. 事务提交时 InnoDB 存储引擎进行 prepare 操作
  2. MySQL 数据库上层写入 binlog
  3. InnoDB 存储引擎将日志写入 redo log
    1. 写入redo log 缓冲
    2. 调用 fsync
      开启 binlog 后 InnoDB 存储引擎的提交过程.png
      为了保证 binlog 的写入顺序和 事务提交顺序一致,MySQL 内部使用了 prepare_commit_mutex 锁,启用该锁后,步骤3.1 不可以在其他事务执行 3.2 时进行,从而导致了 group commit 失效。

MySQL 5.6 采用 Binary Log Group Commit(BLGC) 来提高日志的写入性能:
在 MySQL 数据库上层进行提交时首先按顺序将其放入一个队列中,队列中的第一个事务称为 leader,其他事务称为 follower,leader 控制着 follower 的行为。

  1. Flush 阶段,将每个事务的 binlog 写入内存中
  2. Sync 阶段,内存中的 binlog 刷新到磁盘(如果队列中有多个事务,一次 fsync 操作就完成了多个 binlog 的写入)
  3. Commit 阶段,leader 根据顺序调用存储引擎层事务的提交,InnoDB 可以继续使用 group commit

undo

undo log 用于将数据回滚到修改之前的样子,因为回滚做的事情实际上是与之前相反的工作。所以,对于 INSERT ,undo log 会对应一个 DELETE 记录;对于 DELETE,画丶do log 对应一个 INSERT;对于 UPDATE,undo log 对应一个相反的 UPDATE。
undo log 另一个作用是 MVCC,当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过 undo 读取之前的行版本信息,从而实现 一致性非锁定读。

undo 存储管理

InnoDB 存储引擎对 undo 的管理采用段(segment)的方式,rollback segment 称为回滚段,每个回滚段记录了 1024 个 undo log segment。

在 InnoDB 存储引擎中,undo log 分为 insert undo logupdate undo log

事务提交后并不能马上删除 undo log 及 undo log 所在的页。因为可能还有其他事务需要通过 undo log 来得到行记录之前的版本。事务提交时将 undo log 放入一个链表中,是否可以最终删除 undo log 及 undo log 所在页由 purge 线程来判断。并且提交事务时,还会判断 undo log 页是否可以重用,如果可以重用,则会分配给后面的事务。

purge

purge 操作用于最终完成 deleteupdate 操作。

InnoDB 存储引擎有一个 history 列表,根据事务提交的顺序,将 undo log 进行链接。InnoDB 存储引擎先从 history list 中找 undo log,然后再从 undo page 中找 undo log,避免大量的随机读取操作,从而提高 purge 的效率。

redo log 用来保证事务的持久性,undo log 用来帮助事务回滚以及 MVCC 的功能。redo log 基本上都是顺序写的,在数据运行时不需要对 redo log 的文件进行读取操作,而 undo log 是需要进行随机读取的。

上一篇下一篇

猜你喜欢

热点阅读