随着企业业务的发展,系统架构趋于复杂、数据规模不断增大,数据分布存储在不同的地域、数据中心或云平台上的现象越发普遍,如何保证数据的可靠性和在线服务的连续性成为人们关注的重点。在此基础上,跨集群复制(Cross-Cluster Replication,CCR)应运而生,并逐渐成为数据和服务高可用性的重要保障。
CCR 通常被用于容灾备份、读写分离、集团与公司间数据传输和隔离升级等场景。
-
容灾备份:通常是将企业的数据备份到另一个集群与机房中,当突发事件导致业务中断或丢失时,可以从备份中恢复数据或快速进行主备切换。一般在对 SLA 要求比较高的场景中,都需要进行容灾备份,比如在金融、医疗、电子商务等领域中比较常见。
-
读写分离:读写分离是将数据的查询操作和写入操作进行分离,目的是降低读写操作的相互影响并提升资源的利用率。比如在数据库写入压力过大或在高并发场景中,采用读写分离可以将读/写操作分散到多个地域的只读/只写的数据库案例上,减少读写间的互相影响,有效保证数据库的性能及稳定性。
-
集团与分公司间数据传输:集团总部为了对集团内数据进行统一管控和分析,通常需要分布在各地域的分公司及时将数据传输同步到集团总部,避免因为数据不一致而引起的管理混乱和决策错误,有利于提高集团的管理效率和决策质量。
-
隔离升级:当在对系统集群升级时,有可能因为某些原因需要进行版本回滚,传统的升级模式往往会因为元数据不兼容的原因无法回滚。而使用 CCR 可以解决该问题,先构建一个备用的集群进行升级并双跑验证,用户可以依次升级各个集群,同时 CCR 也不依赖特定版本,使版本的回滚变得可行。
为满足上述各场景的需求,市面上也有不少数据产品推出 CCR 功能,其中比较有代表性的是 Elasticsearch 和 ClickHouse。
-
CCR 是 Elasticsearch 推出的一项付费功能,其本质上是 Leader/Follower 的同步,它在数据导入时是按照分区进行数据同步,而不是按照写入的数据同步,这就会导致数据不一致的问题出现。
-
ClickHouse 一般通过 Remote Function 或者 ClickHouse-Copier 来实现 CCR。Remote Function 仅适合全量同步,同步时需要遍历表、分区进行同步。ClickHouse-Copie 也不支持增量迁移,由于 ClickHouse 本身没有事务的设计,在使用 Copier 同步数据相当于跨级群之间的副本同步,无法保证同步的一致性,也无法配置关于 DB 级别的同步,需要按照表逐个进行配置,使用流程比较复杂,易用性一般。
由于 CCR 是企业在系统服务可用性方面的强需求,因此许多厂商将其纳入产品的付费增值功能中,需要购买企业版才能使用。 秉持开源开放的原则,在 Apache Doris 2.0 版本中我们正式推出 CCR 来服务广大开源用户。
相较于 Elasticsearch 和 Clickhouse,Apache Doris CCR 可以在库/表级别将源集群的数据变更同步到目标集群,可根据场景精细控制同步范围;用户也可以根据需求灵活选择全量或者增量同步,有效提升了数据同步的灵活性和效率;此外 Doris CCR 还支持 DDL 同步,源集群执行的 DDL 语句可以自动同步到目标集群,从而保证了数据的一致性。Doris CCR 配置和使用也非常简单,简单操作即可快速完成跨集群数据复制。基于 Doris CCR 优异的能力,可以更好实现读写负载分离以及多机房备份,并可以更好支持不同场景的跨集群复制需求。
Doris CCR 的设计
在 Apache Doris 2.0 版本中,我们引入了 Binlog 机制来追踪数据的修改记录,包括 Meta Binlog 和 Data Binlog。为了实现集群间的数据同步,我们引入了外部组件 Syncer ,通过 Syncer 获取到最新的 Binlog 并回放到下游集群来实现数据的同步,同时增加了一系列机制来清理多余 Binlog。具体实现包括:
新增Binlog
在 Apache Doris 2.0 之前的版本中,我们无法追踪到 Apache Doris 数据修改记录,而数据变更记录正是实现 CCR 的前置依赖。为解决该问题,在 Apache Doris 2.0 版本中,我们引入了 Binlog 机制,通过 Binlog 机制自动记录数据修改记录和操作,以实现数据的可追溯性,同时我们还可以基于 Binlog 回放机制来实现数据的重放和恢复。由于我们支持库表级别同步,因此在使用时需要为 DB/Table 添加 Binlog 相关的属性,目前 Binlog 支持enable和ttl_seconds这两种属性。
注意:如果想要使用 CCR 功能,开启 Binlog 是的必要前提条件。
-- Table
alter table binlog set ("binlog.enable" = "true"); //开启 binlog
alter table binlog set ("binlog.ttl_seconds" = "864000"); // 配置 binlog 过期时间-- DB
alter database ccr set properties ("binlog.enable" = "true");
alter database ccr set properties ("binlog.ttls" = "864000");
持久化机制
为了确保在系统崩溃或者各种突发事件后可以得到及时恢复,我们引入了持久化机制,将数据持久化至磁盘来确保数据的可靠性和一致性。数据的持久化主要涉及 FE 所存储的元数据信息以及 BE 所存储的实际数据本身,在开启 Binlog 属性后,FE 和 BE 会将 DDL/DML 操作的修改记录持久化成 Meta Binlog 和 Data Binlog。在进行数据操作时,FE 会触发相应的日志记录。我们对 EditLog 的实现进行了增强,以确保日志的有序性。我们通过构建一个递增序列的 LogID,对每个操作进行准确记录,并按顺序持久化。这种有序的持久化机制有助于保证数据的一致性。
在 FE 发起 Publish Transaction 的时候,BE 会执行对应的 Publish 操作,BE 会将这次 Transaction 涉及 Rowset 的元数据信息写入以 rowet_meta 为前缀的 KV 中,并持久化到 Meta 存储中,提交后会把导入的 Segment Files 链接到 Binlog 文件夹下。通过该方式,FE 的元数据和 BE 的数据可以构建一个逻辑上的 Binlog 系列。而该机制可以通过物理文件回放或逻辑回放来实现数据的恢复,在性能和可靠性方面均能提供有效的解决方案。
数据回放
为了更好的连接源集群及目标集群,我们引入了中间同步与控制组件——Syncer。通过 Syncer 可以将源集群的数据抽取到目标集群上,并且可以将 Binlog 系列抽取到另一个集群上进行数据回放。
具体实现:
-
支持了物理文件回放,使用物理文件回放方式,可以有效地重现数据操作流程。
-
Syncer 以 FE CommitSeq 作为游标,可以获取到下一条提交 Commit 的 Meta Binlog。Syncer 会根据 Meta Binlog的信息协调下游的 BE 去上游 BE 抽取真实的 Binlog 文件。该机制不仅保证了回放数据的一致性,还保证了高效的数据同步性能。
-
在处理同步时,Syncer 在创建任务时优先使用 Snapshot 级别的 Backup/Restore 对 Doris 进行全量的数据恢复,接下来根据恢复的 Snapshot 的 CommitSeq 进行增量数据恢复。
Binlog 数据清理
随着数据导入越来越多,Binlog 记录的数据操作也会越来越多,占用的存储资源也会逐步增大。因此,我们需要一种数据回收机制来清理多余的 Binlog。
当我们进行 Binlog 数据清理时,在配置 Binlog GC 时我们需要注意 DB 和 Table Binlog GC 的同步状态。比如当用户在 Enable DB Binlog 之前 Enable Table Binlog,之后又需要 Disable DB Binlog 时,我们需要保持之前的 Table Binlog Enable 状态的相关配置,原因是 DB 的清理条件优先于 Table 的清理条件,如果 DB Binlog 是 Enable 状态,那么就需要按照 DB 的 GC 时间进行 Binlog 清理,否则就会出现 DB 和 Table 的清理状况是不一致,进而导致 Binlog 不一致的情况。
面对这种情况,FE 端会根据 Binlog 的过期时间定期扫描已经过期的 Binlog,将对应的清理过期请求下发给 BE,而 BE 会根据最后一条的 Commit Seq 对对应的 Tablet 进行元数据和 Rowset Binlog 的清理。在这个过程之中需要关注 DB 和 Table Binlog 的重叠情况。
如何使用 CCR
使用须知
目前在使用 CCR 时暂时需要开启 Doris 的 Root 权限,其他须知如下:
-
对于源集群的 Binlog 流程需要 Master Token,Master Token 在 FE 上获取需要 Root 权限
-
对于其他对元集群的 Binlog 获取只需要 Show 权限
-
Binlog 本身的同步只需要对表或者 DB 的目标集群开启 Load 权限
安装部署
-
部署源集群和目标 Doris 集群
-
部署数据同步组件 Syncer
下载和编译源码
git clone https://github.com/selectdb/ccr-syncer
cd ccr-syncer# -j 开启多线程编译
# --output指定输出的路径名称,默认名称为output
bash build.sh <-j NUM_OF_THREAD> <--output SYNCER_OUTPUT_DIR>
编译完成的源码在 Output 文件夹中,与 Doris 类似启停脚本在 Bin 中,可执行文件在 Lib 中
# SYNCER_OUTPUT_DIR是编译的输出路径
# SYNCER_DEPLOY_DIR是实际部署的路径
cp -r SYNCER_OUTPUT_DIR SYNCER_DEPLOY_DIR
cd SYNCER_DEPLOY_DIR# 启动syncer,加上--daemon使syncer在后台运行
bash bin/start_syncer.sh --daemon# 停止syncer
bash bin/stop_syncer.sh
配置任务
- 在 FE/BE 的 conf 文件中添加如下配置以开启 Binlog
enable_feature_binlog=true
- 打开目标集群中同步库/表的 Binlog
-- enable database binlog
ALTER DATABASE ccr SET properties ("binlog.enable" = "true");-- enable table binlog
ALTER TABLE enable_binlog SET ("binlog.enable" = "true");
- 向 Syncer 发起同步任务
curl -X POST -H "Content-Type: application/json" -d '{"name": "ccr_test","src": {"host": "localhost","port": "9030","thrift_port": "9020","user": "root","password": "","database": "demo","table": "example_tbl"},"dest": {"host": "localhost","port": "9030","thrift_port": "9020","user": "root","password": "","database": "ccrt","table": "copy"}
}' http://127.0.0.1:9190/create_ccr
补充参数说明:
-
name:CCR 同步任务的名称,唯一即可
-
host、port:对应集群 Master 的 Host、MySQL(JDBC) 的端口
-
thrift_port:对应 FE 的 rpc_port
-
user、password:说明 Syncer 以何种身份去开启事务、拉取数据等
-
database、table:
-
如果是库级别的同步,dbName、tableName 为空
-
如果是表级别同步,则需要填入 dbName、tableName,不为空
-
查看和取消状态
- 查看同步进度
curl -X POST -H "Content-Type: application/json" -d '{"name": "ccr_test"
}' http://127.0.0.1:9190/get_lag
- 停止任务
curl -X POST -H "Content-Type: application/json" -d '{"name": "ccr_test"
}' http://127.0.0.1:9190/stop_ccr
数据同步性能实测
为了测试 CCR 的数据同步效率,我们也进行了基于全量数据的导入测试。在全量数据的导入过程中,2TB 数据仅需不到 4 小时即可同步完成,单节点写入速度超过 170MB 每秒,具体结果见下方表格。随着集群规模的扩展,数据写入的效率呈线性提升的趋势。
需要说明的是,性能测试在特定的环境和配置上进行测试,结果与不同的环境、版本及配置有关,在此仅作为参考。
全量同步
源集群和目标集群均为 1FE 1BE 的集群,系统信息与硬件信息如下:
源集群数据量:2097152MB
目标集群数据量:0
全量同步性能测试结果
后续规划
目前 Doris CCR 已支持表和库级别的数据同步。具体来说,在表级别支持各种数据导入方式,支持轻量级和重量级 Schema Change,包括增加单表物化视图等,以支持更灵活的数据同步需求;同时 CCR 还支持动态分区,手动分区等。在库级别支持整库同步,可以将源集群中的所有表数据同步到目标集群中。此外,CCR 还支持创建和删除表的同步操作,可以在源集群中创建或删除表时,自动同步到目标集群中,以实现数据的同步和一致性。
未来我们还将在持续发力,不断提升 Doris CCR 的同步能力与性能,主要包括:
-
进一步增强表库级别的 DDL 操作,提供更灵活、更可靠的数据同步和管理功能;
-
支持用户自定义 Binlog 消费,直接使用 Select 语句消费 Binlog,按照对应的 Driver 返回数据,比如 MySQL 协议的 Rowset;
-
支持逻辑数据格式同步,允许用户让目标集群 BE 去源集群 BE 获取 CSV 或者 Parquet 等标准格式的增量数据(Binlog),方便用户在多个底层 BE 数据格式(Rowset)不兼容版本之间进行同步;
-
支持冷热分离,完善对 Doris 本身分层存储的支持;
-
库级别同步支持黑名单,目的是过滤掉某些表 ,用户可以方便在使用CCR的时候不需要因为库内有些表无需同步而为每张表都设置单独的同步任务(方便保证在几张表之上的事务)
-
目标集群支持主备切换,开启 Binlog 可以将增量数据同步给源集群;
-
增强 Syncer 的运维与可观测性相关的功能,提升 Syncer 的运维部署能力,监控 Syncer 的开销和同步任务的进度。使得 Syncer 支持更多相关的运维操作,支持分布式部署和同步进度多种 DB 的支持。
在此也欢迎有相关需求的同学积极在评论区反馈需求或问题。
# 作者介绍:
许瑞亮,SelectDB 资深研发工程师
李仕杨,SelectDB 生态研发工程师