关于后端:Mysql系列二日志系统一条更新语句是如何执行的

37次阅读

共计 5920 个字符,预计需要花费 15 分钟才能阅读完成。

有的时候博客内容会有变动,首发博客是最新的,其余博客地址可能会未同步, 认准https://blog.zysicyj.top

这篇文章是从 Github ReadMe 拷贝的,内容实际下载是没问题的,可能失常发送短信,而且也不须要服务器,本地也能跑起来

首发博客地址

系列文章地址


上篇文章咱们介绍了一个查问语句的执行流程,并介绍了执行过程中波及的解决模块。一条查问语句的执行过程个别是通过连接器、分析器、优化器、执行器等功能模块,最初达到存储引擎。

那么,一条语句的更新流程是什么样的?

MySQL 能够复原到半个月内任意一秒的状态,是怎么做到的?

咱们先温习下查问流程

这里咱们须要留神的是,更新语句的流程和查问流程有两个区别,更新流程波及两个重要的日志模块:

  • redo log(重做日志)
  • binlog(归档日志)

置信大家在这个面试,学习 MySQL 的过程中都重复听到这两个词

WAL 技术

在 MySQL 中,WAL(Write-Ahead Logging)技术是一种罕用的长久化数据的机制,用于确保数据库的事务操作可能长久化到磁盘并保持数据的一致性。WAL 技术的核心思想是在事务进行批改之前,先将批改操作记录到日志中,而后再将批改利用到数据库中

具体来说,MySQL 中的 WAL 技术次要包含以下几个组件和步骤:

  1. Redo Log(重做日志):Redo Log 是一种事务日志,用于记录数据库中产生的批改操作。在事务提交之前,MySQL 会将批改操作写入 Redo Log,而不是间接写入磁盘。这样能够进步性能,因为磁盘写入是绝对较慢的操作。
  2. Write-Ahead Logging(预写式日志):WAL 技术要求在事务提交之前,Redo Log 必须先写入磁盘,而后再将批改操作利用到数据库中。这样即便在事务提交后产生零碎解体,MySQL 也能够通过 Redo Log 来复原数据。
  3. Redo Log Buffer(重做日志缓冲区):Redo Log Buffer 是一个内存缓冲区,用于暂存待写入 Redo Log 的批改操作。当事务提交时,Redo Log Buffer 中的内容会被刷新到磁盘的 Redo Log 文件中。
  4. Checkpoint(检查点):Checkpoint 是一个标记点,示意在这个点之前的所有事务曾经长久化到磁盘。MySQL 会定期将 Checkpoint 的地位更新到磁盘,以确保曾经长久化的数据不会失落。
  5. Crash Recovery(解体复原):当数据库产生解体或重启时,MySQL 会通过读取 Redo Log 来复原数据的一致性。它会依照 Redo Log 中的程序,将每个事务的批改操作从新利用到数据库中,以还原数据的最新状态。

WAL 技术的长处是能够进步数据库的性能和可靠性。通过将批改操作先记录到 Redo Log 中,能够防止频繁地写入磁盘,从而进步性能。同时,WAL 技术还能够确保数据的持久性和一致性,即便在零碎解体或断电的状况下也可能复原数据。

MySQL 中的 WAL 技术通过应用 Redo Log 和预写式日志的机制,确保事务的批改操作可能长久化到磁盘并保持数据的一致性。它是一种进步性能和可靠性的重要技术。

Redo log 执行流程

  1. 当一个事务开始时,MySQL 会为该事务调配一个惟一的事务 ID,并将该事务的相干信息存储在内存中的事务管制块(Transaction Control Block,TCB)中。
  2. 在事务执行过程中,所有的批改操作都会被写入 redo log 缓冲区。这些批改操作包含插入、更新和删除等操作。
  3. 当事务提交时,MySQL 会将该事务的所有批改操作依照程序写入 redo log 文件中。这些批改操作会被写入到 redo log 缓冲区,而后通过后盾线程定期将缓冲区中的内容刷新到磁盘上的 redo log 文件中。这个过程称为 redo log 的刷新。
  4. 在事务提交之前,MySQL 会将 redo log 的刷新操作和数据页的刷新操作进行协调,以保证数据的一致性。这是通过应用 write-ahead logging(预写式日志)的机制来实现的。即在事务提交之前,redo log 必须先写入磁盘,而后再将批改操作利用到数据库中。
  5. 当数据库产生解体或重启时,MySQL 会在启动过程中读取 redo log 文件,并将其中的批改操作从新利用到数据库中,以复原数据的一致性。这个过程称为解体复原。

Write Pos 和 CheckPoint

在 MySQL 的 redo log 中,有两个重要的概念:write pos(写入地位)和 checkpoint(检查点)。

  1. Write Pos(写入地位):Write Pos 是指以后事务写入 redo log 的地位。当一个事务提交时,其批改操作会被写入 redo log 中的某个地位,Write Pos 指向这个地位。下一个事务的批改操作将会从 Write Pos 指向的地位开始写入。
  2. Checkpoint(检查点):Checkpoint 是指一个标记点,示意在这个点之前的所有事务曾经长久化到磁盘。当一个事务提交时,它的批改操作会被写入 redo log,并且会更新 Checkpoint 的地位。这样,在 Checkpoint 之前的 redo log 中的操作能够被认为是曾经长久化到磁盘的。

Checkpoint 的作用是用于数据库的复原和解体复原。当数据库产生解体或重启时,MySQL 会从 Checkpoint 的地位开始,读取 redo log 中的操作,并将其利用到数据库中,以还原数据的一致性。

Write Pos 和 Checkpoint 之间的关系是,Write Pos 会一直向前挪动,指向最新的写入地位,而 Checkpoint 会依据肯定的策略进行更新,以标记曾经长久化到磁盘的操作。

须要留神的是,Write Pos 和 Checkpoint 的地位是绝对于 redo log 文件的偏移量,而不是相对的字节地位。它们的值通常以字节为单位,示意绝对于 redo log 文件起始地位的偏移量。

Write Pos 示意以后事务写入 redo log 的地位,Checkpoint 示意曾经长久化到磁盘的操作的地位。Write Pos 会一直向前挪动,而 Checkpoint 会依据肯定的策略进行更新,用于数据库的复原和解体复原。

Redo log 是固定大小的,超出会产生什么

当 redo log 的固定大小不足以包容新的批改操作时,MySQL 会触发一个称为 ”redo log 空间有余 ” 的谬误。在这种状况下,MySQL 会进行新的事务提交,直到有足够的空间来写入 redo log。

为了解决 redo log 空间有余的问题,能够采取以下几种办法:

  1. 减少 redo log 的大小:能够通过批改 MySQL 的配置参数 innodb_log_file_size 来减少每个 redo log 文件的大小。减少 redo log 的大小能够提供更多的空间来存储批改操作,从而缩短 redo log 的使用寿命。
  2. 减少 redo log 文件的数量:能够通过批改 MySQL 的配置参数 innodb_log_files_in_group 来减少 redo log 文件组中的文件数量。减少文件数量能够减少 redo log 的总大小,从而提供更多的空间来存储批改操作。
  3. 提交事务并清空 redo log:如果以后的事务曾经提交,但 redo log 空间有余,能够尝试手动提交其余未提交的事务,以开释 redo log 空间。这能够通过执行 COMMIT 语句来提交事务。
  4. 优化事务的写入操作:能够通过优化事务的写入操作,缩小对 redo log 的写入量。例如,能够合并多个小事务为一个大事务,缩小 redo log 的写入次数。

须要留神的是,减少 redo log 的大小或数量可能会减少零碎的负载和解体复原的工夫。因而,在调整 redo log 大小时,须要综合思考零碎的性能和可靠性需要,并进行充沛的测试和验证。

什么是 binlog 日志

Binlog(二进制日志)是 MySQL 的服务器层产生的一种日志,用于记录数据库中的所有批改操作,包含数据定义语言(DDL)和数据操作语言(DML)等操作。

Binlog 以二进制格局记录了对数据库的逻辑批改操作,而不是间接记录对数据页的具体批改。它蕴含了一系列的事件(Event),每个事件都代表了一个数据库操作,如插入、更新、删除等。

Binlog 的次要作用是用于 数据复制和复原。通过将 Binlog 传递给其余 MySQL 实例,能够实现数据的复制和同步。其余 MySQL 实例能够读取 Binlog 中的事件,并将其中的批改操作利用到本人的数据库中,从而实现数据的复制和同步。

此外,Binlog 也能够用于数据恢复。在误操作、数据失落或劫难复原的状况下,能够通过读取 Binlog 来还原数据。通过一一回放 Binlog 中的事件,能够将数据库复原到特定的工夫点或特定的操作之前的状态。

Binlog 是追加写入的,不会被重复使用,以保留残缺的批改历史。它能够通过配置参数进行启用和配置,包含指定 Binlog 的存储地位、设置 Binlog 的大小和保留工夫等。

为什么 MySQL 会有两个日志,redo log 和 binlog?

MySQL 之所以同时应用 redo log 和 binlog 两个日志,是因为它们具备不同的性能和用处。

  1. Redo Log(重做日志):

    • 性能:Redo log 是 InnoDB 存储引擎特有的日志,用于保障事务的持久性和一致性。它记录了数据库中产生的批改操作,包含插入、更新和删除等操作。
    • 作用:在数据库解体或重启时,通过读取 redo log 来复原数据的一致性。它能够将未长久化到磁盘的批改操作从新利用到数据库中,以还原数据的最新状态。
    • 特点:redo log 是 物理日志,记录了对数据页的具体批改操作。它是循环写入的,能够重复使用,以缩小磁盘 IO 的开销。
  2. Binlog(二进制日志):

    • 性能:Binlog 是 MySQL 的服务器层产生的日志,记录了数据库中的所有批改操作,包含数据定义语言(DDL)和数据操作语言(DML)等操作。
    • 作用:Binlog 次要用于数据复制和复原。它能够被其余 MySQL 实例读取,并将其中的批改操作利用到本人的数据库中,实现数据的复制和同步。同时,Binlog 也能够用于数据恢复,例如在误操作或数据失落时,能够通过读取 Binlog 来还原数据。
    • 特点:Binlog 是 逻辑日志,记录了对数据的逻辑批改操作。它是追加写入的,不会被重复使用,以保留残缺的批改历史。

redo log 保障了事务的持久性和一致性,而 binlog 则提供了数据复制和复原的性能。它们独特工作,确保了 MySQL 数据库的数据安全和可靠性。

举一个例子

mysql> update T set c=c+1 where ID=2;
  1. 执行器先找引擎取 ID=2 这一行。ID 是主键,引擎间接用树搜寻找到这一行。如果 ID=2 这一行所在的数据页原本就在内存中,就间接返回给执行器;否则,须要先从磁盘读入内存,而后再返回。
  2. 执行器拿到引擎给的行数据,把这个值加上 1,比方原来是 N,当初就是 N+1,失去新的一行数据,再调用引擎接口写入这行新数据。
  3. 引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 外面,此时 redo log 处于 prepare 状态。而后告知执行器执行实现了,随时能够提交事务。
  4. 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
  5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新实现。

最初三步看上去有点“绕”,将 redo log 的写入拆成了两个步骤:prepare 和 commit,这就是 ” 两阶段提交 ”。

MySQL 中的两阶段提交

在 MySQL 中,redo log 和 binlog 是两个不同的日志文件,它们都用于确保数据的一致性和持久性。它们的写入程序和提交程序有所不同。

  1. Redo Log(重做日志):

    • Redo log 是 MySQL 用于解体复原的机制,它记录了事务对数据库所做的批改操作。
    • 当事务执行时,MySQL 首先将批改操作记录到 redo log 中,而后将其写入磁盘。
    • 这样做的目标是为了在零碎解体时,可能通过 redo log 来复原未实现的事务,保证数据的一致性。
  2. Binlog(二进制日志):

    • Binlog 是 MySQL 用于数据复制和复原的机制,它记录了数据库的批改操作。
    • 当事务提交时,MySQL 将批改操作记录到 binlog 中,但不立刻写入磁盘。
    • Binlog 的写入是异步的,可能会有肯定的提早。

当初来解释为什么 MySQL 先写 redo log,而后等 binlog 写完后才提交:

  1. 事务的持久性和恢复能力:

    • 通过将批改操作记录到 redo log 中,MySQL 能够确保即便零碎解体,也可能通过 redo log 来复原未实现的事务,保证数据的一致性。
    • 因而,redo log 的写入是在事务执行期间进行的,以提供更好的性能。
  2. 数据复制和复原:

    • Binlog 用于数据复制和复原,它记录了所有的数据库批改操作。
    • 在事务提交之后,MySQL 将批改操作记录到 binlog 中,以供主从复制等场景应用。
    • 为了保证数据的一致性,MySQL 会期待 binlog 的写入实现,而后才提交事务。

所以,MySQL 先写 redo log,而后等 binlog 写完后才提交的目标是为了 保证数据的一致性和持久性 并提供数据复制和复原的能力。这样的设计能够进步性能,并确保在零碎解体或数据复制场景下的数据完整性。心愿这次解释更加清晰明了。如果还有任何疑难,请随时发问。

没写完产生 Crash 了会呈现什么状况?

依然用后面的 update 语句来做例子。假如以后 ID=2 的行,字段 c 的值是 0,再假如执行 update 语句过程中在写完第一个日志后,第二个日志还没有写完期间产生了 crash,会呈现什么状况呢?

  1. 先写 redo log 后写 binlog。假如在 redo log 写完,binlog 还没有写完的时候,MySQL 过程异样重启。因为咱们后面说过的,redo log 写完之后,零碎即便解体,依然可能把数据恢复回来,所以复原后这一行 c 的值是 1。然而因为 binlog 没写完就 crash 了,这时候 binlog 外面就没有记录这个语句。因而,之后备份日志的时候,存起来的 binlog 外面就没有这条语句。而后你会发现,如果须要用这个 binlog 来复原长期库的话,因为这个语句的 binlog 失落,这个长期库就会少了这一次更新,复原进去的这一行 c 的值就是 0,与原库的值不同。
  2. 先写 binlog 后写 redo log。如果在 binlog 写完之后 crash,因为 redo log 还没写,解体复原当前这个事务有效,所以这一行 c 的值是 0。然而 binlog 外面曾经记录了“把 c 从 0 改成 1”这个日志。所以,在之后用 binlog 来复原的时候就多了一个事务进去,复原进去的这一行 c 的值就是 1,与原库的值不同。

能够看到,如果不应用“两阶段提交”,那么数据库的状态就有可能和用它的日志复原进去的库的状态不统一。

本文由 mdnice 多平台公布

正文完
 0