myrocks crash safe特性

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 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)
AI 代码解读

对于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}
AI 代码解读

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
AI 代码解读

此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
AI 代码解读

此表保存了所有已执行的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
目录
打赏
0
0
0
0
9167
分享
相关文章
binlog、redolog、undo log底层原理及ACID特性实现分享
在数据库管理系统中,日志机制是确保数据一致性、完整性和可靠性的关键组件。MySQL数据库中的binlog、redolog和undolog作为其核心日志系统,各自扮演着不同但同样重要的角色。本文将深入探讨这三种日志的底层原理以及它们如何分别实现ACID(原子性、一致性、隔离性、持久性)特性的不同方面。
159 0
pg_rewind实现原理简单分析
pg_rewind的功能是在主备切换后回退旧主库上多余的事务变更,以便可以作为新主的备机和新主建立复制关系。本文简单介绍其实现原理。
1909 0
MySQL 的 crash-safe 原理解析
MySQL作为当下最流行的开源关系型数据库,有一个很关键和基本的能力,就是必须能够保证数据不会丢。那么在这个能力背后,MySQL是如何设计才能保证不管在什么时间奔溃,恢复后都能保证数据不会丢呢?有哪些关键技术支撑了这个能力?本文将为我们一一揭晓。
1083 0
replication crash safe
什么是主从复制的replication crash safe? 参数master_info_repository有两个值: FILE (对应的文件master.info),  or TABLE (对应的表mysql.
766 0
PostgreSQL技术周刊第26期:vacuum freeze无法回收事务号问题分析
PostgreSQL(简称PG)的开发者们:云栖社区已有5000位PG开发者,发布了3000+PG文章(文章列表),沉淀了700+的PG精品问答(问答列表)。 PostgreSQL技术周刊会为大家介绍最新的PG技术与动态、预告活动、最热问答、直播教程等,欢迎大家订阅PostgreSQL技术周刊。
3763 0
深入解读MySQL8.0 新特性 :Crash Safe DDL
深入解读MySQL8.0 新特性 :Crash Safe DDL 深入解读MySQL8.0 新特性 :Crash Safe DDL 前言 在MySQL8.0之前的版本中,由于架构的原因,mysql在server层使用统一的frm文件来存储表元数据信息,这个信息能够被不同的存储引擎识别。
2735 0
MySQL · 源码分析 · binlog crash recovery
前言 本文主要介绍binlog crash recovery 的过程 假设用户使用 InnoDB 引擎,sync_binlog=1 使用 MySQL 5.7.20 版本进行分析 crash recovery 过程中,binlog 需要保证: 所有已提交事务的binlog已存在 所有未提交...
2603 0
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等