myrocks crash safe特性

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: myrocks crash safe 特性

title: MySQL · myrocks · myrocks crash safe 特性

author: 张远

crash safe 定义

在主备环境下,实例mysqld crash或机器宕机是常见的事情,而如何快速而安全的从异常中恢复,对高可用来说是非常重要的。常见的恢复方法是从备份集来恢复实例,重建复制关系,这种做法是安全的但一般耗时较长。另外一种方法就是等异常实例恢复,直接启动异常实例来恢复复制关系,这种做法快速但不一定是安全的,下节会详细介绍原因。

crash safe就是指在异常实例恢复后,直接启动异常实例来恢复复制关系,并且保证数据是一致的。

myrocks 是crash safe的。

不一致原因

分为主库异常恢复和备库异常恢复两种情况,这两种情况都可能出现不一致的情况。

主库异常

在myrocks主备环境是开启了loss-less semisync(AFTER-SYNC模式),并且主库sync_binlog=1,innodb_flush_log_at_trx_commit=1(双1模式)。在这种情况下,主库是在sync binlog 之后,engine commit之前等待备库的ACK, 因此主库crash时,主库可能存在部分binlog已经落盘但engine层没有提交,并且这些binlog也没有即时发送到备库的情况。

image

这部分binlog 实际上就是比备库多出来的binlog,而主库启动恢复这部分binlog所在事务是处于prepare状态的,根据binlog XA协议,这部分事务最终会提交。从而,主库比备库会多一些事物,导致不一致。

备库异常

为了加快复制速度,备库一般会设置sync_binlog!=1,innodb_flush_log_at_trx_commit!=1(非双1模式)。这种设置,备库可能出现两种情况:

  • binlog落后于engine层,也就是存在engine 层提交了但binlog没有落盘的情况。
  • binlog超前于engine层,也就是存在binlog落盘了但engine层没有提交(非prepare)的情况。

在这两种情况下直接启动备库,重建复制关系都可能导致主备不一致。

engine层的binlog信息

讨论myrocks如何做到crash safe之前,我们先熟悉下以下背景知识。
engine 层提交时会保存binlog位点信息。
对于innodb, 每次提交时会在系统事务页(第5页)存储binlog 位点和gtid,参考trx_sys_update_mysql_binlog_offset

#define TRX_SYS_MYSQL_LOG_OFFSET_HIGH   4       /*!< high 4 bytes of the offset                                           within that file */
#define TRX_SYS_MYSQL_LOG_OFFSET_LOW    8       /*!< low 4 bytes of the offset within that file */
#define TRX_SYS_MYSQL_LOG_NAME          12      /*!< MySQL log file name */
#define TRX_SYS_MYSQL_GTID              (TRX_SYS_MYSQL_LOG_NAME + TRX_SYS_MYSQL_LOG_NAME_LEN)

对于rocksdb,每次提交时会在数据字典BINLOG_INFO_INDEX_NUMBER中存储binlog 位点和gtid, 参考Rdb_binlog_manager::update

BINLOG_INFO_INDEX_NUMBER
key: Rdb_key_def::BINLOG_INFO_INDEX_NUMBER (0x4)
value: version, {binlog_name,binlog_pos,binlog_gtid}

master crash safe

myrocks可以做到主库crash safe。前面讲过如果直接启动主库,多出来的那部分binlog可能导致不一致。如果我们能够把这部分binlog去掉,那么启动时,prepare的事务就可以回滚了,从而主备又可以一致了。事实上facebook就是这样做的,主库crash时,备库io线程会终止,同时会在errorlog中记录拉取主库binlog的位点信息。取到这位点后,将主库的binlog从此位点truncate掉。

以下图片来自facebook

主库crash
master crash .png

truncate binlog 后
facebook.png

前面提到engine层也保存了位点信息,那么我们为什么不使用这个位点,而是从备库errorlog中获取呢。
engine层位点前的事务在engine 层都是已提交的。
主库crash时,prepare的事务的binlog有可能部分传到了备库,而这些prepare的事务重启动时是应该提交的。而如果用engine层位点来truncate binlog会导致这部分事务回滚。
因此,从备库获取的位点信息才是准确的。

slave crash safe

myrocks可以做到备库库crash safe,这里需考虑两种情况

  • binlog落后于engine层,也就是存在engine 层提交了但binlog没有落盘的情况。

针对此种情况,Facebook mysql增加了新的系统表专门保存每个db最后提交的gtid信息。之所以保存的是db的gtid信息,是因为并行复制还是库级别并发的,不得不吐槽下。

CREATE TABLE `slave_gtid_info` (
  `Id` int(10) unsigned NOT NULL,
  `Database_name` varchar(64) NOT NULL,
  `Last_gtid` varchar(56)DEFAULT NULL,
  PRIMARY KEY (`Id`)
) DEFAULT CHARSET=utf8 STATS_PERSISTENT=0

此gtid信息,在每个事务提交时都会更新。

备库启动时,这部分未落盘事务会重新拉取部分到备库apply, sql线程apply的过程中会根据系统表中的gtid信息来判断此gtid是否应该跳过,这些需跳过的事务只需要记录binlog不需要真正执行。参考Gtid_info::skip_event .

  • binlog超前于engine层,也就是存在binlog落盘了但engine层没有提交(非prepare)的情况。

实例启动时gtid_executed是从binlog中读取的。对于此种情况,先从engine层读取位点信息,更新gtid_executed信息时只读取binlog的到此位点,然后rotate binlog,用新的gtid_executed信息重新拉取binlog就不会不一致了。参考read_gtids_from_binlog。

slave_gtid_info表优化

每个事务提交时都要更新slave_gtid_info表。最初的时候,这个操作是server层提交时由server层更新slave_gtid_info表,后来将这个操作下沉到engine直接由engine层来执行。从而省去了server层接口调用的开销,这个优化对于rocksdb insert 场景有30%左右的提升。详情参考这里。不过,如果需要混用innodb和rocksdb,建议禁用此优化。

关于gtid_execute表

mysql5.7已经支持gtid_executed表(myrocks是基于mysql5.6的), 结构如下

CREATE TABLE `gtid_executed` (
  `source_uuid` char(36) NOT NULL COMMENT 'uuid of the source where the transaction was originally executed.',
  `interval_start` bigint(20) NOT NULL COMMENT 'First number of interval.',
  `interval_end` bigint(20) NOT NULL COMMENT 'Last number of interval.',
  PRIMARY KEY (`source_uuid`,`interval_start`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 STATS_PERSISTENT=0

此表保存了所有已执行的gtid信息,与slave_gtid_info不同,slave_gtid_info只是保存了每个db最后执行的gtid信息。如果将slave_gtid_info换成gtid_executed表,整个代码逻辑将更加清晰,同时gtid_executed表对以后支持表级或事务级的并行复制来说更加便利。

总结

myrocks的crash safe 特性可以帮助我们快速安全的恢复实例,重新建立复制关系,恢复高可用。crash safe 可以解决大多数主备环境下由于实例crash导致的主备不一致问题。然而,前面提到的主库crash safe是在semisync没有退化为异步的情况下才成立,要想真正的做到高可用,可使用mysql5.7.17刚GA的group replication 特性。当然,myrocks理论上也是支持group relication的,rocksdb只是mysql的一个engine而已。另外,业界已经有比较成熟的基于rocksdb的高可用分布式数据库,例如TiDBcockroachDB

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
2月前
|
SQL 存储 关系型数据库
简单聊聊MySQL的三大日志(Redo Log、Binlog和Undo Log)各有什么区别
在MySQL数据库管理中,理解Redo Log(重做日志)、Binlog(二进制日志)和Undo Log(回滚日志)至关重要。Redo Log确保数据持久性和崩溃恢复;Binlog用于主从复制和数据恢复,记录逻辑操作;Undo Log支持事务的原子性和隔离性,实现回滚与MVCC。三者协同工作,保障事务ACID特性。文章还详细解析了日志写入流程及可能的异常情况,帮助深入理解数据库日志机制。
196 0
|
4月前
|
SQL 关系型数据库 MySQL
MySQL事务日志-Undo Log工作原理分析
事务的持久性是交由Redo Log来保证,原子性则是交由Undo Log来保证。如果事务中的SQL执行到一半出现错误,需要把前面已经执行过的SQL撤销以达到原子性的目的,这个过程也叫做"回滚",所以Undo Log也叫回滚日志。
168 7
MySQL事务日志-Undo Log工作原理分析
|
7月前
|
SQL 存储 关系型数据库
美团面试:binlog、redo log、undo log的底层原理是什么?它们分别实现ACID的哪个特性?
老架构师尼恩在其读者交流群中分享了关于 MySQL 中 redo log、undo log 和 binlog 的面试题及其答案。这些问题涵盖了事务的 ACID 特性、日志的一致性问题、SQL 语句的执行流程等。尼恩详细解释了这些日志的作用、所在架构层级、日志形式、缓存机制以及写文件方式等内容。他还提供了多个面试题的详细解答,帮助读者系统化地掌握这些知识点,提升面试表现。此外,尼恩还推荐了《尼恩Java面试宝典PDF》和其他技术圣经系列PDF,帮助读者进一步巩固知识,实现“offer自由”。
美团面试:binlog、redo log、undo log的底层原理是什么?它们分别实现ACID的哪个特性?
|
8月前
|
存储 缓存 关系型数据库
redo log 原理解析
redo log 原理解析
112 0
redo log 原理解析
|
8月前
|
存储 关系型数据库 MySQL
binlog、redolog、undo log底层原理及ACID特性实现分享
在数据库管理系统中,日志机制是确保数据一致性、完整性和可靠性的关键组件。MySQL数据库中的binlog、redolog和undolog作为其核心日志系统,各自扮演着不同但同样重要的角色。本文将深入探讨这三种日志的底层原理以及它们如何分别实现ACID(原子性、一致性、隔离性、持久性)特性的不同方面。
208 0
|
Oracle 前端开发 关系型数据库
使用隐含参数_disable_logging分析oracle写redo logfile的性能
oracle有一个隐含参数_disable_logging可以禁止日志的生成,这个参数当然不能在生产库使用,但我们可以将其因为与测试,例如,如果我们怀疑数据库写redo logfile存在性能问题,我们可以将这个参数设置为true,禁止写日志,看看oracle的性能提高了多少。
127 0
|
存储 固态存储 关系型数据库
MySQL 5.6 change buffer bug导致crash
Insert buffer 内部标识长度的位图没有正确更新,导致问题
207 0
|
存储 SQL 缓存
重用的undo日志 (3)—mysql进阶(六十六)
重用的undo日志 (3)—mysql进阶(六十六)
|
存储 SQL 关系型数据库
redo log-Transaction(2)—mysql进阶(六十)
redo log-Transaction(2)—mysql进阶(六十)
|
SQL 监控 安全
PostgreSQL 谁堵塞了谁(锁等待检测)- pg_blocking_pids, pg_safe_snapshot_blocking_pids
PostgreSQL 谁堵塞了谁(锁等待检测)- pg_blocking_pids, pg_safe_snapshot_blocking_pids
2019 0