一般情况下,不同数据库间的数据同步采用事务日志的方式。如果需要将数据库 A 的数据同步到数据库 B,则需要对数据库 A 中的所有执行的语句以及相应的事务信息进行记录,再通过 redo,undo log 等进行数据恢复,这样的方式也可用于不同集群之间的数据同步。
这里,我们先明确下主/备集群的定义,方便对照阅读后文内容:将需要提供原始数据的集群定义为主集群,需要接收数据的集群定义为备集群。
上述描述的方式虽然对主集群影响较小。然而对集群而言,仍然需要等待主集群的日志落盘,这也将不可避免地增加了磁盘的多次工作,浪费性能资源。同时,该方式无法自主决定数据同步复制的粒度,更较难实现从集群选择性复制或者同步主集群的部分表。
面对上述的困境,KaiwuDB 采取在边缘端使用单机数据库或低性能分布式数据库,云端采用统一的高性能分布式数据库,云端与边缘端采取一对多的形式;通过建立连接,在数据库底层直接进行数据的复制和同步,减少性能损耗并维护同步数据的一致性。
主集群服务启动在较高配置的服务器上,称为云端(C 端);备集群服务启动在较低配置的服务器或功能开发板上,称为边缘端(E 端)。
主集群的所有 C 端均为同一个集群,E 端的所有节点可能为单机模式或集群模式(以单机模式为主)。E 端的所有数据库由于环境资源的限制,只能管理相关领域的部分数据,而 C 端需管理所有节点的数据。
用户执行语句:
REPLICATE targets FROM partitioned_backup opt_with_options
建立起 A 集群到 B 集群的连接后,由 A 集群发送 Batch 数据到 B 集群。
图 1 A-B 集群数据复制流程
二、流程
1. 建立链接
集群 B 需要建立在集群 A 的链接来传输数据。该链接不是将两个集群连接为同一个集群,而只是在两个集群间建立一个数据传输的通道。
在连接建立后,两个集群会分别启动一个服务(Job),用于维护该连接。该连接可以选择为表级的,或是数据库级的,用于同步单个数据表或者整个数据库。
图 2 数据同步任务执行流程
3. 数据回放
当数据表的初始数据同步后,后续集群同步的数据流便不再是较大的资源,而是随着 A 集群用户的不同执行语句陆续发送给 B 集群。
受限于 B 集群的硬件配置情况,语句应尽量不使用原始语句或者相关的日志文件,而是采用更底层的形式语句。
以实现了 Google Spanner 架构的分布式数据库 CockroachDB 为例,该数据库使用 Range 的概念划分数据,每个 Range 都是一段连续的 Key-value 集合,所有的 SQL 语句最后都会生成批处理请求(Batch Request),然后经过副本层通过 RangeFeed 将 BatchRequests 发送到对应的 Range 上。
在 CockraochDB 中,每个表可能会有多个 Range 存储,由于每个 Range 内的 Key 是顺序的,故每个 Range 只能存储一个表上的数据。每当写入一个 Range 时,通过 RangeFeed 来执行写入操作。
当数据回放时,RangeFeed 每接到一个写入操作,就需去查看该表是否需要同步。如果需要同步,则 RangeFeed 就将该写入操作发送给需要同步的集群。
对于 B 集群来说,当接收到 A 集群同步的 BatchRequest 时,B 是否能够回放该条写入操作取决于该事务是否完成以及该事务依赖的事务是否均完成,以防止可能发生的事务碰撞。
三、技术亮点