MySQL的CrashSafe和Binlog的关系

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

1、什么是CrashSafe

CrashSafe指MySQL服务器宕机重启后,能够保证:
- 所有已经提交的事务的数据仍然存在。
- 所有没有提交的事务的数据自动回滚。
前面的文章讲过,Innodb通过Redo Log和Undo Log可以保证以上两点。为了保证严格的CrashSafe,必须要在每个事务提交的时候,将Redo Log写入硬件存储。这样做会牺牲一些性能,但是可靠性最好。为了平衡两者,InnoDB提供了一个系统变量,用户可以根据应用的需求自行调整。

- innodb_flush_log_at_trx_commit
  0 - 每N秒将Redo Log Buffer的记录写入Redo Log文件,并且将文件刷入硬件存储1次。N由innodb_flush_log_at_timeout控制。
  1 - 每个事务提交时,将记录从Redo Log Buffer写入Redo Log文件,并且将文件刷入硬件存储。
  2 - 每个事务提交时,仅将记录从Redo Log Buffer写入Redo Log文件。Redo Log何时刷入硬件存储由操作系统和innodb_flush_log_at_timeout决定。这个选项可以保证在MySQL宕机,而操作系统正常工作时,数据的完整性。

那么CrashSafe和Binlog有什么关系呢?

2、带Binlog的CrashSafe

当启动Binlog后,事务会产生Binlog Event,这些Event被看做事务数据的一部分。因此要保证事务的Binlog Event和InnoDB引擎中的数据的一致性。所以带Binlog的CrashSafe要求MySQL宕机重启后能够保证:
- 所有已经提交的事务的数据仍然存在。
- 所有没有提交的事务的数据自动回滚。
- 所有已经提交了的事务的Binlog Event也仍然存在。
- 所有没有提交事务没有记录Binlog Event。

  这些要求很好理解,如果重启后数据还在,但是Binlog Event没有了,就没办法复制到其他节点上了。如果重启后,数据没了,但是Binlog Event还在,那么不存在的数据就会被复制到其他节点上,从而导致主从的不一致。

为了保证带Binlog的CrashSafe,MySQL内部使用的两阶段提交(Two Phase Commit)。

3、MySQL的Two Phase Commit(2PC)

在开启Binlog后,MySQL内部会自动将普通事务当做一个XA事务来处理:

- 自动为每个事务分配一个唯一的ID

- COMMIT会被自动的分成Prepare和Commit两个阶段。

- Binlog会被当做事务协调者(Transaction Coordinator),Binlog Event会被当做协调者日志。

想了解2PC,可以参考文档:https://en.wikipedia.org/wiki/Two-phase_commit_protocol。

- 分布式事务ID(XID)
使用2PC时,MySQL会自动的为每一个事务分配一个ID,叫XID。XID是唯一的,每个事务的XID都不相同。XID会分别被Binlog和InnoDB记入日志中,供恢复时使用。MySQ内部的XID由三部分组成:
- 前缀部分
  前缀部分是字符串"MySQLXid"
- Server ID部分
  当前MySQL的server_id
- query_id部分
  为了保证XID的的唯一性,数字部分使用了query_id。MySQL内部会自动的为每一个语句分配一个query_id,全局唯一。
参考代码:sql/xa。h的 struct xid_t结构。

- 事务的协调者Binlog
Binlog在2PC中充当了事务的协调者(Transaction Coordinator)。由Binlog来通知InnoDB引擎来执行prepare,commit或者rollback的步骤。事务提交的整个过程如下:
1. 协调者准备阶段(Prepare Phase)
    告诉引擎做Prepare,InnoDB更改事务状态,并将Redo Log刷入磁盘。
2. 协调者提交阶段(Commit Phase)
    2.1 记录协调者日志,即Binlog日志。
    2.2  告诉引擎做commit。

注意:记录Binlog是在InnoDB引擎Prepare(即Redo Log写入磁盘)之后,这点至关重要。

在MySQ的代码中将协调者叫做tc_log。在MySQL启动时,tc_log将被初始化为mysql_bin_log对象。参考

sql/mysqld.cc中的init_server_components():
if (opt_bin_log)
   tc_log= &mysql_bin_log;

而在事务提交时,会依次执行:

tc_log->prepare();
tc_log->commit();

参考代码:sql/handler.cc中的ha_commit_trans()。

当mysql_bin_log是tc_log时,prepare和commit的代码在sql/binlog.cc中:

MYSQL_BIN_LOG::prepare()
MYSQL_BIN_LOG::commit()

- 协调者日志Xid_log_event
作为协调者,Binlog需要将事务的XID记入日志,供恢复时使用。Xid_log_event有以下几个特点:
- 仅记录query_id
  因为前缀部分不变,server_id已经记录在Event Header中,Xid_log_event中只记录query_id部分。
- 标志事务的结束  
  在Binlog中相当于一个事务的COMMIT语句。
  一个事务在Binlog中看起来时这样的:   

Query_log_event("BEGIN");
DML产生的events;               
Xid_log_event;                    

- DDL没有BEGIN,也没有Xid_log_event。

- 仅InnoDB的DML会产生Xid_log_event
  因为MyISAM不支持2PC所以不能用Xid_log_event,但会有COMMIT Event。

Query_log_event("BEGIN");
DML产生的events;
Query_log_event("COMMIT");

问题:Query_log_event("COMMIT")和Xid_log_event有不同的影响吗?
- Xid_log_event中的Xid可以帮助master实现CrashSafe。
- Slave的CrashSafe不依赖Xid_log_event

事务在Slave上重做时,会重新产生XID。所以Slave服务器的CrashSafe并不依赖于Xid_log_event。Xid_log_event和Query_log_event("COMMIT"),只是作为事务的结尾,告诉Slave Applier去提交这个事务。因此二者在Slave上的影响是一样的。


4、恢复(Recovery)

这个机制是如何保证MySQL的CrashSafe的呢,我们来分析一下。这里我们假设用户设置了以下参数来保证可靠性:

sync_binlog=1
innodb_flush_log_at_trx_commit=1

- 恢复前事务的状态
在恢复开始前事务有以下几种状态:
- InnoDB中已经提交
  根据前面2PC的过程,可知Binlog中也一定记录了该事务的的Events。所以这种事务是一致的不需要处理。
- InnoDB中是prepared状态,Binlog中有该事务的Events。
  需要通知InnoDB提交这些事务。
- InnoDB中是prepared状态,Binlog中没有该事务的Events。
  因为Binlog还没记录,需要通知InnoDB回滚这些事务。
- Before InnoDB Prepare
  事务可能还没执行完,因此InnoDB中的状态还没有prepare。根据2PC的过程,Binlog中也没有该事务的events。 需要通知InnoDB回滚这些事务。

- 恢复过程
从上面的事务状态可以看出:恢复时事务要提交还是回滚,是由Binlog来决定的。
- 事务的Xid_log_event存在,就要提交。
- 事务的Xid_log_event不存在,就要回滚。

恢复的过程非常简单:
- 从Binlog中读出所有的Xid_log_event
- 告诉InnoDB提交这些XID的事务
- InnoDB回滚其它的事务
疑问1:如果事务的Binlog Event只记录了一部分怎么办?
只有最后一个事务的Event会发生这样的情况。在恢复时,binlog会自动的将这个不完整的事务Events从Binlog文件中给清除掉。

疑问2:随着长时间的运行,Binlog中会积累了很多Xid_log_event,读取所有的Xid_log_event会不会效率很低?

当然很低,所以Binlog中有一个机制来保证恢复时只用读取最后一个Binlog文件中的Xid_log_event。这种机制很像一个简单的Xid_log_event的checkpoint机制。

- Xid_log_event Checkpoint

这个机制和binlog的文件切换有关,在切换到一个新的Binlog文件前:
- 要等待当前Binlog文件中的所有事务都已经在InnoDB中提交了。
- 告诉InnoDB刷Redo Log到硬件存储。
通过这个机制可以保证在做恢复时,除了最后一个Binlog文件中的事务,其他文件中的事务在InnoDB中一定是已经提交的状态。

参考代码:

sql/binlog.cc中:

MYSQL_BIN_LOG::recovery()

MYSQL_BIN_LOG::new_file_impl()

MYSQL_BIN_LOG::inc_prep_xids()

MYSQL_BIN_LOG::dec_prep_xids()

5、CrashSafe的写盘次数

前面说道要想保证CrashSafe就要设置下面两个参数为1:
sync_binlog=1
innodb_flush_log_at_trx_commit=1
下面我们来看看这两个参数的作用。
- sync_binlog
sync_binlog是控制Binlog写盘的,1表示每次都写。由于Binlog使用了组提交(Group Commit)的机制,它代表一组事务提交时必须要将Binlog文件写入硬件存储1次。
- innodb_flush_log_at_trx_commit的写盘次数
这个变量是用来控制InnoDB commit时写盘的方法的。现在commit被分成了两个阶段,到底在哪个阶段写盘,还是两个阶段都要写盘呢?
- Prepare阶段时需要写盘
 2PC要求在Prepare时就要将数据持久化,只有这样,恢复时才能提交已经记录了Xid_log_event的事务。
- Commit阶段时不需要写盘
 如果Commit阶段不写盘,会造成什么结果呢?已经Cmmit了的事务,在恢复时的状态可能是Prepared。由于恢复时,Prepared的事务可以通过Xid_log_event来提交事务,所以在恢复后事务的状态就是正确的。因此在Commit阶段不需要写盘。

总的来说保证MySQL服务的CrashSafe需要写两次盘。在2PC的过程中,InnoDB只在prepare阶段时,写一次盘。Binlog在commit阶段,会设置一个参数告诉InnoDB不要写盘。这个参数是thd->durability_property= HA_IGNORE_DURABILITY;代码在sql/binlog.cc的MYSQL_BIN_LOG::ordered_commit()中。

- Prepare阶段写盘优化

我们知道Binlog使用了Group Commit机制来减少IO,提高性能。Prepare有没有可能做Group Commit呢?只要我们能保证任何事务的Redo Log是在它的Binlog Event写入Binlog文件前,被刷入了持久存储就可以。优化后的做法是:
1. 协调者准备阶段(Prepare Phase)
   设置thd->durability_property告诉InnoDB不写盘。 告诉引擎做Prepare,InnoDB更改事务状态。
2. 协调者提交阶段(Commit Phase)
   2.1.1 获取一组事务。
   2.1.2 通知InnoDB将Redo Log写入硬件存储。
   2.1.3 将这组事务的Binlog Event写入Binlog文件。
   2.2 告诉引擎做commit。

这个结合了Binlog Group Commit机制的改进对性能的提升还是很显著的。而且这个改进是中国的社区用户阿里云的翟卫祥同学提出并提供的代码补丁。详情可参考MySQL的bug页面:http://bugs.mysql.com/bug.php?id=73202。

参考代码:sql/binlog.cc中的MYSQL_BIN_LOG::process_flush_stage_queue()

6、总结

MySQL通过两阶段提交的方式来保证CrashSafe。CrashSafe需要Server层、Binlog和InnoDB的协同工作才能完成。由于DDL和MyISAM不支持事务性,因此没办法保证CrashSafe。

注意:本文的代码都是指MySQL-5.7中的代码,其他版本可能会有不一致。

本文来自云栖社区合作伙伴“DBGEEK”

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
12天前
|
存储 安全 关系型数据库
Mysql 的binlog日志的优缺点
MySQL的binlog(二进制日志)是一个记录数据库更改的日志文件,它包含了所有对数据库执行的更改操作,如INSERT、UPDATE和DELETE等。binlog的主要目的是复制和恢复。以下是binlog日志的优缺点: ### 优点: 1. **数据恢复**:当数据库出现意外故障或数据丢失时,可以利用binlog进行点恢复(point-in-time recovery),将数据恢复到某一特定时间点。 2. **主从复制**:binlog是实现MySQL主从复制功能的核心组件。主服务器将binlog中的事件发送到从服务器,从服务器再重放这些事件,从而实现数据的同步。 3. **审计**:b
|
21天前
|
SQL 关系型数据库 MySQL
mysql的binlog恢复数据
mysql的binlog恢复数据
20 0
|
2月前
|
存储 SQL 安全
浅谈MySQL Binlog
浅谈MySQL Binlog
45 0
|
2月前
|
SQL 存储 关系型数据库
解析MySQL Binlog:从零开始的入门指南【binlog入门指南】
解析MySQL Binlog:从零开始的入门指南【binlog入门指南】
931 0
|
2月前
|
监控 关系型数据库 MySQL
MySQL Binlog实战:在生产环境中的应用与最佳实践【实战应用】
MySQL Binlog实战:在生产环境中的应用与最佳实践【实战应用】
35 0
|
2月前
|
SQL 监控 关系型数据库
MySQL Binlog深度解析:进阶应用与实战技巧【进阶应用】
MySQL Binlog深度解析:进阶应用与实战技巧【进阶应用】
42 0
|
2月前
|
存储 SQL 关系型数据库
Mysql专栏 - mysql、innodb存储引擎、binlog的工作流程
Mysql专栏 - mysql、innodb存储引擎、binlog的工作流程
75 0
|
11天前
|
关系型数据库 MySQL 数据库
mysql卸载、下载、安装(window版本)
mysql卸载、下载、安装(window版本)
|
1月前
|
关系型数据库 MySQL 数据库连接
关于MySQL-ODBC的zip包安装方法
关于MySQL-ODBC的zip包安装方法
|
29天前
|
关系型数据库 MySQL 数据库
rds安装数据库客户端工具
安装阿里云RDS的数据库客户端涉及在本地安装对应类型(如MySQL、PostgreSQL)的客户端工具。对于MySQL,可选择MySQL Command-Line Client或图形化工具如Navicat,安装后输入RDS实例的连接参数进行连接。对于PostgreSQL,可以使用`psql`命令行工具或图形化客户端如PgAdmin。首先从阿里云控制台获取连接信息,然后按照官方文档安装客户端,最后配置客户端连接以确保遵循安全指引。
82 1