开源分布式数据库 RadonDB 的核心技术与实现

百家 作者:QingCloud 2018-08-14 13:10:25

编者按:RadonDB 是一款将分布式一致性协议 Raft 与 MySQL 相结合的新一代分布式关系型数据库,兼具 NewSQL 和 MySQL 两类数据库的优点,2018 年 5 月 10 日,RadonDB 在第九届中国数据库技术大会上正式宣布开源。


RadonDB 的设计者张雁飞


本文,RadonDB 的设计者张雁飞将从架构、执行、高可用等角度,结合开源代码为大家深度解析 RadonDB 的核心技术与实现。


为什么叫 RadonDB



RadonDB 是一个开源的分布式数据库。为什么叫 RadonDB 呢?


RadonDB 中的 Radon 出自元素周期表,是一种惰性气体,名字叫做氡。因其化学性质比较稳定,所以我们就以此来命名了这款数据库产品。


RadonDB 包含两个部分 Radon 和 Xenon,并不是一个简单的数据库中间件。其中,Radon 类似于一个数据库中间件,而 Xenon 是一个高可用的 MySQL 管理集群工具。


RadonDB 架构



上图是 RadonDB 的整个架构图,其中最上面一层是 Radon,一个分布式的 SQL 层,负责 SQL 的解析和转发。这一层就是大家说的数据库中间件,它会根据用户请求将 SQL 语句生成分布式执行计划,并推到下面的存储层。


下面这一层是 Xenon,一个高可用的 MySQL 管理工具。在这一层的每个虚线框里都是一“主”两“从”的 MySQL,都通过 Xenon 进行管理。


Xenon 其实是一个无中心化的管理工具,当主节点挂了之后,会使用 Raft 协议进行选主,选出新的“主”之后,再根据 MySQL Binlog 这些机制进行数据同步,从而使新的主节点继续对外服务。


分布式 SQL



RadonDB 的主要功能是对用户 SQL 生成分布式计划以及执行器并行执行,当执行器下发到存储层以后,进行 ORDER BY、LIMIT、GROUP BY 等二次运算。


Radon 支持集群模式,所以基本上是无状态的。当其中一个节点挂了之后,其他节点可以很快的迁移过去,保证 Radon 这一层的高可用。



如果从代码层面来看 Radon 的具体工作流程,用户 SQL 到达 Radon 之后,query.go 文件会对 SQL 进行语法树的生成,生成之后根据 SQL 的类型进行处理。



首先,根据语法树生成分布式的执行计划。分布式的执行计划是根据路由表查找每个具体的数据分布在哪些后端,然后生成具体的子句。


分布式执行计划生成之后,就运行 Insert executor 文件,并下发到相应节点去执行。


以上,就是 RadonDB 中 Radon 这一层的基本工作机制。


RadonDB 的一些技术细节


Radon 这一层还有一个比较重要的功能——分布式事务。Radon 分布式事务是基于 MySQL 原生事务—— XA 事务来进行的。


MySQL 的 XA 事务在执行时可分为五个阶段:第一个阶段是 XA START,第二个阶段是 SQL 执行,第三个阶段是 XA END,第四个阶段是 XA PREPARE,这个阶段其实数据已经通过 Binlog 传到了备库,即使主库挂了,重建之后事务仍然处于 XA PREPARE 状态,我们可以认为数据不会丢失。最后一个阶段是 XA COMMIT。


RadonDB 对这五个阶段进行了分工,共分成 begin、execute、commit三个阶段。



RadonDB 实现了 SI 隔离级别的分布式事务。Radon 里有一个 Commit Lock,如果不加这个锁是实现不了这种隔离级别的。


那么什么是 SI 隔离级别呢?SI 是 SNAPSHOT ISOLATION 的缩写,它的作用是未提交的不可见,例如有三个分区,当它们都没有 XA commit 时,其它事务读的时候是看不到未提交事务的数据。另一个作用是部分提交不可见,还是有三个分区,第一个分区 XA commit 了,其他两个分区正准备 commit,这时候如果有其他的客户端读数据也是不可见的。



为了检测 XA 的隔离级别,我们研发了一个开源工具,它的思路比较简单,就是一个更新线程不停地去更新,16 个扫表线程不停地扫表。如果分布式事务满足不了 SI 隔离级别,那么 16 个扫表线程就有可能看到更新线程的部分数据。


我们进行了 100 多亿次操作和检测的测试,并且过程是随机的。我们会把存储层的主节点宕掉来做“主从”切换。在大量的测试中,目前还没有发现读取到部分数据的情况。


下面介绍一下RadonDB的另一个组件—— Xenon,一个高可用的 MySQL 管理工具。假设一个节点有一“主”两“从”,三个 MySQL,那么它们之间的高可用怎么来实现呢?



Xenon 的工作机制是和 MySQL 配合,通过 MySQL 链接不停地去 ping MySQL,并拿到 MySQL 的信息。一个 MySQL 对应一个 Xenon,并部署在一个 container 里,一“主”两“从”分布在不同的 container 里面。


当 Master 不可服务时,其他的 Xenon 会检测不到 Master 发来的心跳,这时由 Xenon 发起的心跳会发起选主操作,进而其他的从节点会被选为新的主节点。




接下来,我们讲一下 Xenon 如何发起选主操作、如何选择新的主节点以及选完之后如何保证数据不丢失?


对于一“主”多“从”的 MySQL 集群想要做到高可用有几个挑战:


  • 第一是如何选“主”;

  • 第二是选“主”之后,数据怎么跟原先的 Master 进行同步,保证数据不丢失;

  • 第三是如何尽快选“主”,当原来的“主”挂了之后,新的主节点如何尽快应用数据,并对外提供服务。


我们把 MySQL 的 GTID 和 Raft 的选主结合了起来。Xenon 主要实现了 Raft 选主功能,结合 MySQL GTID 实现高可用。


了解 Raft 算法的朋友可能都知道,Raft 主要做两件事,第一件就是选“主”。第二个就是 log 同步。Xenon 选“主”使用了 Raft 选主协议,选“主”之后会结合 MySQL GTID 进行数据同步。


如果是一“主”两“从”的节点,那么这两个从节点哪个被选为新的主?这里结合了 MySQL 的 GTID 和 semi-sync。


当我们把 semi-sync 的 vote_timeout 设置为无限长,基本上就可以认为是“主”。写完之后,至少有一个“从”会收到,然后返回给“主”,“主”再返回给 cluster,这样保证至少有一个“从”和“主”的数据是完全同步的。


当“主”挂了之后,和主节点数据完全同步的从节点会被选为新的主节点,之后根据 MySQL 的并行复制,快速回放,并对外进行提供服务。


介绍完 Radon 和 Xenon,我们看一下,数据在 RadonDB 里面是怎么分布的?RadonDB 的底层存储基于 MySQL,也就是说由 Xenon 管理的一主两从是一个节点,整个存储层是由多个这样的节点组成的。



用户创建一个表的时候需要指定一个分区键,RadonDB 会根据分区键把整个大表分成 32 个小表。分配规则是这样的,整个表有 4096 个槽位,其中每个小表是 128 个槽位,共有 32 个小表。


RadonDB 的最小单位就是小表,命名为 T1_0000 到 T1_0031,每个小表都是占 128 个槽位,例如第一个小表是从 0 到 127。这样当用户在做 Insert 时,就可以依据此判断数据会落在哪个小表里。



RadonDB 如何做扩容呢?RadonDB 最小单位是一个小表,但 4096 和 128 这两个数字是可以配置的。在扩容上,RadonDB 可以让小表在不同的机器间动态漂移。因为是 MySQL 表,所以把小表从一个 MySQL 实例上飘到另外一个 MySQL 实例比较简单。


首先是做一个全量迁移,记下当时迁移的位点,然后再对增量进行追加。这种以小表为迁移的方式不但不影响读写操作,而且操作方便,既可以扩容,还可以缩容。



RadonDB 还支持 Binlog,为什么?因为 RadonDB 是一个分布式数据库,如果有别的数据库或数据想订阅 RadonDB 数据,那么就可以订阅 RadonDB Binlog。


连上 RadonDB 之后,执行 SHOW BINLOG EVENTS,指定从哪个 GTID 开始,同时还可以指定订阅多少条。这样就可以把 RadonDB 数据实时的导入到异构的数据库里。



如果 RadonDB 收到了比较复杂的 AP 操作,例如 JOIN,它的机制又是怎么样的呢?RadonDB 还有一个计算节点,当用户 SQL 上来之后, RadonDB 如果发现里面有比较复杂的 JOIN 等 AP 操作,会自动的把这个请求路由到计算节点上。


计算节点是插件式的,它可以是其他比较强大的 AP 分析型数据库,计算节点把结果计算完之后,RadonDB 会自动的反馈给客户端,在这种情况下,客户端是无法感知到这些操作的。


这样做的好处是事务型和计算型是资源隔离的,但缺点是存储需要两份。如何克服缺点呢?其实目前我们也没有很好的办法,只是通过压缩暂时的解决了这个问题。


RadonDB 还实现了审计日志功能,当用户的请求到达 RadonDB 之后,RadonDB 把用户请求记录到本地磁盘上。我们可以通过上图中的多个维度进行审计,同时还可以查询慢操作。当日志请求量比较大时,RadonDB 会定期进行清理。


同时 RadonDB 还支持多种审计模式,例如只读审计、只写审计、读写审计等等。



大家可能会比较担心分布式数据库灌进了大量数据就很难导出来了。针对这种情况,RadonDB 提供了导入和导出的工具,这些工具是并行式的,导入/导出的速度比 MySQL 原生的 Mydumper 还快。



RadonDB 提供了全链路的监控,如果分布式数据库是一个黑盒,那么出了问题就很不容易排查。RadonDB 从前往后做了三层监控,第一层就是 show processlist,这层是监控用户到 RadonDB 的连接,跟 MySQL 是一样的。


其中 RadonDB 实现了一个序列表,这一层的作用就是可以看到 cluster 到 RadonDB 执行的 SQL 语句。


第二层是监控 RadonDB 内部峰值事务执行到哪个阶段,大家可以通过 show txnz 命令进行监控;


最后一层就是 show queryz,这个命令可以看到具体的子句在哪些后端执行。


通过这三层监控就可以很快的定位到具体问题,比如一个慢 MySQL,它到底是慢在哪些地方。



上图是性能对比表,上面的 RadonDB 是四个存储节点,下面是单机MySQL。大家可以看到,RadonDB 的性能基本上是单机的三倍,而延迟基本上是 1/3。


为什么会出现这种情况呢?这就是分布式的威力。假设我们有一个 1TB 的表,如果使用单机数据库,那么 Btree 会比较高,而且每次请求的 IO 深度路径也会比较长。


而 RadonDB 会把 1TB 的数据分成四个节点,假设平均每个节点是 250G,每个节点还有细分每个小表。当用户请求时,我们只需请求小表,而且 RadonDB 对所有的请求都是并行执行的,时间完全取决于最慢的小表。所以在这种设计中,RadonDB 的性能基本上会是单机的三倍,而延迟是 1/3。


RadonDB 的未来


最后说一下 RadonDB 的展望,大家都了解类似于 Google Spanner 这种 NewSQL 会是一个大趋势,而且很多公司也都在完全自主研发 NewSQL。


有人都认为传统的基于 MySQL 分库分表的方式已经过时了,而我们提出了一个新的概念——MyNewSQL,就是 MySQL 和 NewSQL 相结合。


其实 RadonDB 就是一个 MyNewSQL,它把 NewSQL 领域里常用的算法都拿到了 MySQL 里,从而实现了 MyNewSQL。RadonDB 最后实现的功能和 NewSQL 基本无异,但它是基于 MySQL 进行存储,表、数据结构都可以是异构的,性能上也有很大的提升。


一个分布式数据库的技术其实是非常繁琐的,通过这篇文章是不可能完全讲清楚的,但好在 RadonDB 已经开源,大家可以去 GitHub 上查看源码。


RadonDB 的 GitHub 地址:https://github.com/Radondb


更多详情请点击阅读原文


FIN -


关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接