侧边栏壁纸
博主头像
再见理想博主等级

只争朝夕,不负韶华

  • 累计撰写 112 篇文章
  • 累计创建 64 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

MySQL 的二阶段提交

再见理想
2022-08-10 / 0 评论 / 0 点赞 / 706 阅读 / 1,802 字

一,binlog 和 redolog

1.1,binlog

binlog 我们中文一般称作归档日志,是 MySQL Server 层的日志,而不是存储引擎自带的日志,它记录了所有的 DDL 和 DML 语句,而且是以事件形式记录,还包含语句所执行的消耗的时间等,需要注意的是:

  • binlog 是一种逻辑日志,他里边所记录的是一条 SQL 语句的原始逻辑,例如给某一个字段 +1,注意这个区别于 redo log 的物理日志(在某个数据页上做了什么修改)。
  • binlog 文件写满后,会自动切换到下一个日志文件继续写,而不会覆盖以前的日志,这个也区别于 redo log,redo log 是循环写入的,即后面写入的可能会覆盖前面写入的。
  • 一般来说,我们在配置 binlog 的时候,可以指定 binlog 文件的有效期,这样在到期后,日志文件会自动删除,这样避免占用较多存储空间。

根据 MySQL 官方文档的介绍,开启 binlog 之后,大概会有 1% 的性能损耗,不过这还是可以接受的,

一般来说,binlog 有两个重要的使用场景:

  • MySQL 主从复制时:在主机上开启 binlog,主机将 binlog 同步给从机,从机通过 binlog 来同步数据,进而实现主机和从机的数据同步。
  • MySQL 数据恢复,通过使用 mysqlbinlog 工具再结合 binlog 文件,可以将数据恢复到过去的某一时刻。

1.2,redolog

在 MySQL 中就存在两类日志 binlog 和 redo log,前面说的 binlog 是 MySQL Server 层的日志,而 redo log 是存储引擎 InnoDB 层的日志

InnoDB 作为 MySQL 的存储引擎,数据是存放在磁盘中的,但如果每次读写数据都需要磁盘 IO,效率会很低。为此,InnoDB 提供了缓存(Buffer Pool),Buffer Pool 中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:

当从数据库读取数据时,会首先从 Buffer Pool 中读取,如果 Buffer Pool 中没有,则从磁盘读取后放入Buffer Pool;当向数据库写入数据时,会首先写入 Buffer Pool,Buffer Pool 中修改的数据会定期刷新到磁盘中(这一过程称为刷脏)。

这种先写日志再写磁盘的技术就是 MySQL 里经常说到的 WAL(Write-Ahead Logging) 技术(预写日志)。


问题一

Buffer Pool 的使用大大提高了读写数据的效率,但是也带来了新的问题:如果 MySQL 宕机,而此时Buffer Pool 中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。

于是,redolog 被引入来解决这个问题:当数据修改时,除了修改 Buffer Pool 中的数据,还会在 redo log 记录这次操作。

当事务提交时,会对 redolog 进行刷盘。如果 MySQL 宕机,重启时可以读取 redo log 中的数据,对数据库进行恢复。
redolog 采用的是 WAL 预写日志,,再更新到 Buffer Pool,保证了数据不会因 MySQL 宕机而丢失,从而满足了持久性要求。


问题二

既然 redolog 也需要在事务提交时将日志写入磁盘,为什么它比直接将 Buffer Pool 中修改的数据写入磁盘(刷脏)要快呢?主要有以下两方面的原因:

  1. 刷脏是随机IO,因为每次修改的数据位置随机,但写 redolog 是追加操作,属于顺序IO
  2. 刷脏是以数据页为单位的,MySQL默认页大小是16KB,一个 Page 上一个小修改都要整页写入;而 redolog 中只包含真正需要写入的部分,无效IO大大减少。

二,MySQL 二阶段提交

2.1,二阶段提交流程

在 MySQL 中,两阶段提交的主角就是 binlog 和 redolog,由于 redo log 的提交分为 prepare 和 commit 两个阶段,所以称之为两阶段提交。

我们来看一个两阶段提交的流程图:

从上图中可以看出,在最后提交事务的时候,有 3 个步骤:

  1. 写入 redo log,处于 prepare 状态。
  2. 写 binlog。
  3. 修改 redo log 状态变为 commit。

2.2,二阶段提交的目的

binlog 默认都是不开启的状态!

也就是说,如果你根本不需要binlog带给你的特性(比如数据备份恢复、搭建MySQL主从集群),那你根本就用不着让MySQL 写 binlog,也用不着什么两阶段提交。

只用一个 redolog 就够了,无论你的数据库如何 crash,redolog 中记录的内容总能让你 MySQL 内存中的数据恢复成 crash 之前的状态。

所以说,两阶段提交的主要用意是:为了保证 redolog 和 binlog 数据的安全一致性。

只有在这两个日志文件逻辑上高度一致了,你才能放心地使用 redolog 帮你将数据库中的状态恢复成 crash 之前的状态,使用 binlog 实现数据备份、恢复、以及主从复制,而两阶段提交的机制可以保证这两个日志文件的逻辑是高度一致的,没有错误、没有冲突。


2.3,二阶段提交如何保证数据一致性

MySQL 的二阶段提交可以保证 redolog 和 binlog 数据的一致性。

如果没有两阶段提交,那么 binlog 和 redolog 的提交,无非就是两种形式:

  1. 先写 binlog 再写 redolog。
  2. 先写 redolog 再写 binlog。

看看二阶段提交是如何保证数据的一致性:

情况一:一阶段提交之后崩溃了,即写入 redo log,处于 prepare 状态`的时候崩溃了,此时:

由于 binlog 还没写,redo log 处于 prepare 状态还没提交,所以崩溃恢复的时候,这个事务会回滚,此时 binlog 还没写,所以也不会传到从库。

情况二:假设写完 binlog 之后崩溃了,此时:

redolog 中的日志是不完整的,处于 prepare 状态,还没有提交,那么恢复的时候,首先检查 binlog 中的事务是否存在并且完整,如果存在且完整,则直接提交事务,如果不存在或者不完整,则回滚事务。

情况三:假设 redolog 处于 commit 状态的时候崩溃了,那么重启后的处理方案同情况二。

由此可见,两阶段提交能够确保数据的一致性。

0

评论区