腾讯云数据仓库 TCHouse-C 自研实时数据更新方案,性能提升超 10 倍!
随着大数据数仓技术的发展,业务或商业决策对快速、准确数据支持的依赖日益加深,对数据仓库的数据实时更新技术提出了更高要求。当前,社区版 ClickHouse 不支持唯一索引,通常使用 ReplacingMergeTree 或者 CollapsingMergeTree 等表引擎进行数据的去重和更新操作,针对新写入数据通过后台异步任务合并 Part 或者查询时实时合并 Part 来实现精确去重。但是,这种数据聚合方式的实时性和查询性能不佳,难以满足业务场景。
若使用 ALTER UPDATE/DELETE 等实时更新删除语句,则需要重写存量 Part 数据,造成计算资源的大量消耗和耗时增长,无法保证数据更新删除的实时性。
腾讯云数据仓库 TCHouse-C 是腾讯云提供的全托管 ClickHouse 服务。针对这一问题,腾讯云数据仓库 TCHouse-C 全新推出了一种高性能实时数据更新方案,旨在满足高频数据实时去重及轻量级部分列更新场景的需求。
一、主要挑战
在数据更新方案的实现中,面临的主要挑战包括:高性能、大规模数据、副本一致性和高可用性。
高性能
在高性能数据实时更新方案研发中,需要考虑存储模型、支持唯一键约束、更新即可见等因素:如果不支持唯一键约束,就没法实现 UPSERT 语义;如果支持唯一键约束,写入性能就会打折扣;列存储支持写入即可见的代价相比行存更大,若单次修改涉及多个列,则会增加写入链路延迟。
大规模数据
传统数仓通常在超大规模数据写入时多采用追加写入方式,如果要实现实时更新,需要快速定位存量数据及其位置信息。当数据规模增大时,实现数据更新的代价也会同步增加,进而影响写入性能。
副本一致性
社区版 ClickHouse 的副本表机制下,两副本仅满足最终一致性,对实时更新一致性没有强约束。由于多次实时更新操作可能分散在不同副本上执行,在进行实时去重的判断时,可能会因为副本同步的滞后性导致数据更新覆盖顺序错乱,最终使更新出现错误。
高可用性
数仓在实际使用过程中可能遭遇进程故障、服务器故障、硬盘故障等多种运维故障,这对发生故障后数据的正确性保障提出了挑战。
二、业界现有技术方案
在数仓领域,支持数据实时更新的系统包括 Apache Hudi、阿里云 ADB 和 Hologres 等,其中所采用的技术方案主要有以下几种:
Copy-On-Write 策略
Copy-On-Write 是一种常用的数据写入策略。该模式下,新写入的数据将与存量数据进行冲突检测,对有冲突的内容重新写入更新后的数据。在查询时,由于可以直接读取最新数据,可确保数据的完整一致。然而,该策略会造成写入代价增大,适合读多写少的场景。
图一
Merge-On-Read 策略
当更新数据到达时,不检查数据冲突,直接生成新的文件。查询时,读取合并多个版本的数据后返回最新数据。该策略的优点是写入友好,但查询代价大。
Delta Store 策略
Delta Store 策略是一种改进型的 Merge-On-Read 策略。在写入数据时,根据主键索引找到数据所在文件和位置并关联新修改的数据。查询时,则将原始数据和关联的更新合并以返回最新数据。相比于 Merge-On-Read 策略,由于在写入流程中引入了主键索引,会牺牲部分性能。而在查询过程中,由于只需合并修改数据,查询性能得到了提升。
图二
Delete + Insert 策略
Delete + Insert 策略是 Delta Store 策略的改进。在更新数据到达时,将通过主键索引定位旧数据并标记为删除,同时写入新数据。查询时,则读取所有数据并根据删除标记过滤有效数据。该策略在数仓批量处理场景下优势明显,可充分利用谓词下推、向量化执行等技术优化性。
图三
表一
三、数据实时更新核心技术
社区版 ClickHouse 采用 Merge-On-Read 策略,会导致读取性能变差,难以支持实时删除和实时更新。腾讯云 TCHouse-C 自研的实时数据更新采用 Delete-And-Insert 策略,系统为具备 Upsert 功能的表引擎提供唯一索引(Unique Index)支持。数据更新时,根据定义的 Unique Key 和唯一索引确定更新数据所在行,并将该行标记为删除后重新写入新的行。查询时,如果某行标记为删除,则查询引擎会自动过滤掉这些行。
腾讯云 TCHouse-C 提出的方案能够支持高频数据实时更新,补充了社区方案所不具备的轻量级部分列更新能力,其主要功能点及实现如下:
索引和标记删除
在实时更新的场景中,表级索引类似于 TP 数据库中的 Unique Index。用于在写入或更新数据时快速确定新写入行是否已经存在,以及其存在的位置信息。腾讯云 TCHouse-C 的 Upsert 方案为每个表设计了一个全局索引,用于存放 KEY -> 行号的关键信息,便于每次进行 Upsert 操作时能迅速定位到需要更新的行。
图四
社区版 ClickHouse 内核在实现轻量级删除功能中引入了虚拟列_row_exists。当使用 Lightweight-Delete 功能时,会立即为相应的 PART 生成_row_exists 列的数据文件,用于标记数据被删除。腾讯云 TCHouse-C 的数据标记删除实现中也采用了该思路,, 在查询时候自动补全查询条件 WHERE && _row_exists = 1。 与社区本地文件方案不同是,TCHouse-C 标记删除信息持久化到本地 KV 存储后,也会随 PART 的元数据一起加载到内存 BUFFER 中,并按 PART 维度存储。基于此,单次查询可直接从内存中构建虚拟列 _row_exists,从而大幅提升查询效率。
轻量级数据更新/删除
腾讯云 TCHouse-C 方案支持 UPDATE ... SET ... WHERE ...语法,执行数据更新包括整行更新和部分列更新。通过 WHERE 条件确定更新数据位置后,利用 Delete+Insert 思路将存量数据标记为删除再写入新数据。对于部分列更新,未更新的列数据会写入新 Part 中覆盖旧数据。
腾讯云 TCHouse-C 也支持 DELETE FROM ... WHERE ...语法,根据 WHERE 条件确定需要标记删除的数据位置,然后写入一个特殊的 Part 来覆盖删除标记数据。
后台数据合并优化
在 ClickHouse 内核中,后台数据合并(Merge)具有优化查询效率、优化存储结构、减少数据碎片化、数据去重等功能,是 ClickHouse 存储引擎的重要特性。标准的数据合并流程如下:
选取参与数据合并的 Part(Part 在合并期间仍可被查询)。
将多个待合并的 Part,采用多路归并算法,按需将去重或聚合后的数据写入新的 Part。
将新的 Part 提交到系统。
将参与合并的 Part 标记为过期,并通过引用计数管理,最终从文件系统中删除。
通常情况下,ClickHouse 假定写入的数据不可变更,但引入 Upsert 功能,即引入标记删除后,就打破了数据不可变更的约束。每个 Part 的数据行数可能随着写入、更新、删除实时变化,进而也会影响 Merge 流程。因此在引入数据实时更新功能后,需要优化数据合并流程:
1
需要考虑正在合并的数据会被写入流程标记删除,以及合并 Part 中包含由 DELETE FROM 语句产生的无数据特殊 Part
2
标记删除的数据需要在 Merge 流程中删除,否则随着写入进行,冗余数据会越来越多,导致数据膨胀。
腾讯云 TCHouse-C 的具体解决方案如下
生成快照
选取参与数据合并的 Part 时, 对参与数据合并的 Part 的数据、元数据以及标记删除信息生成快照。生成快照后,在进行合并操作时保证不受其他正在进行的 Upsert/Delete 操作的影响。并且在合并生成新 Part 期间直接过滤掉标记删除的行,以此避免数据膨胀。
图五
2. 再度提交
生成合并的新 Part 后,将新 Part 再次进行 Upsert 提交更新全局索引和标记删除信息,对合并期间写入的新数据进行去重判断。
图六
如上图所示,Part1 和 Part2 合并为 Future Part, 然后 Future Part 提交时会重做 Upsert,并根据先后原则更新 Unique Index 以及标记删除信息。
多副本高可用
ClickHouse 支持多副本机制,由 ZooKeeper 确保数据在各副本上的一致性。为了支持数据实时更新,腾讯云 TCHouse-C 打破了数据 Part 不可变约束,采用更多优化措施在确保高可用前提下满足数据更新正确性。 主要包含以下几个方面:
(1)行级版本机制
ClickHouse 中每个副本都可读可写,需要通过 Zookeeper 实现副本之间数据同步,可能会出现先写入的 Part 被后同步导致 Upsert 出现乱序问题。
腾讯云 TCHouse-C 引入行级版本机制,每行数据会包含一个 Version 信息,在数据写入时生成。为了最大化使用灵活性,Version 既支持客户自定义,也支持自动从 Zookeeper 获取。在实际进行 Delete+Insert 或者 Upsert 时,只有 Version 更大的行能够去标记删除 Version 更小的行。当多个副本同步到一致的日志点时,对应的每一行数据 Version 也会完全一致。
图七
服务重启时,会根据每个 Part 的标记删除信息以及行级 Version 信息对表级的 Unique Index 进行重建,保证重启前后 Unique Index 的信息完全一致。
(2)墓碑机制
由于新数据的写入和副本数据同步的顺序没有强一致保证,Delete 请求删除数据和数据同步也可能存在乱序,进而导致被删除的数据重复写入。针对这一问题,腾讯云 TCHouse-C 在执行 Delete 语句删除数据时通过在 Unique Index 索引维护墓碑,而不是立即清除,从而避免删除和副本同步异步执行导致的时序问题。
(3)副本间同步删除请求
为了在副本间同步删除请求,腾讯云 TCHouse-C 在执行 Delete 语句时会生成一个特殊的 Part,将该 Part 通过正常的数据同步流程同步到副本节点并走 Upsert 提交流程, 实现 Delete 的多副本间同步。
故障恢复优化
一次完整的 ClickHouse 写入操作包含数据落盘、元数据提交等步骤。当出现副本磁盘损坏时,往往需要重建副本,腾讯云 TCHouse-C 为副本表 CloneReplica 流程定制了 Fetch 逻辑,允许将正常副本的标记删除信息也复制过来,以保证副本能够在故障恢复的同时恢复 Unique Index 信息。
四、性能对比测试
采用标准的 SSB 数据集 lineorder_flat 表,构造 6 亿行规模,在 64C256G 服务器上进行测试以对比腾讯云 TCHouse-C 与社区版 ClickHosue 在实时数据更新上的性能。经测试,腾讯云 TCHouse-C 的实时更新方案支持轻量级更新和删除,在查询、更新、删除等场景效率明显优于社区版主流使用的表引擎 ReplacingMergeTree。
导入性能对比
针对 Upsert Table、ReplacingMergeTree 和 MergeTree 表,第一次全量导入 6 亿 SSB 数据,然后再导入包含相同 key 的 6 亿数据进行覆盖 Upsert,分别记录数据导入耗时。
图八
结果表明,Upsert Table 相比 MergeTree 导入速度劣化在 50%以内,但导入性能较 ReplacingMergeTree 提升一倍。
单次查询性能对比
使用 SSB 的 Q1-Q4 标准查询,比较 Upsert Table 和 ReplacingMergeTree 在精确去重查询时的性能。在有大量 Upsert 覆盖的场景下,Upsert Table 查询耗时明显低于 ReplacingMergeTree,性能提升数倍。
图九
并发小规模查询性能对比
采用和单次查询一致的数据集,模拟 32 个并发客户端,每个客户端随机查询 100 行数据。结果表明,Upsert Table 在高并发小规模查询效率上优于 ReplacingMergeTree 至少 1 倍以上。
图十
更新/删除性能对比
在 ClickHouse 内核中,默认更新删除操作需要重写存量 Part 数据,适合批量大规模数据处理,而在小规模更新删除时效率低且消耗大量资源。Upsert Table 通过 Delete+Insert 策略,在单次更新删除部分数据的场景大幅提升效率。在单次操作涉及数据量越小时,Upsert Table 优化效果越明显,性能提升想较于社区版方案也有明显优势。
图十一
高并发更新/删除性能对比
模拟 32 个客户端随机并发更新删除,每次更新删除 100 行数据。测试结果显示,高并发更新删除下 Upsert Table 性能提升巨大,测试场景提升可达数十倍。
表二
测试总结
经过上述多维度对比测试,可以看出相比社区版 ClickHouse, 腾讯云 TCHouse-C 提出的基于支持 Upsert 的 MergeTree 表的实时数据更新方案性能更优:
1.大批量数据导入性能相比 ReplacingMergeTree 提升 100%,相比普通 MergeTree 写入性能劣化在 50%以内
2.并发更新和删除数据,性能差距显著,32 并发场景下性能提升达到 10 倍以上
3.在精确去重时查询性能优于 ReplacingMergeTree
4.高并发更新删除场景下,大幅优化了社区版 Mutation 任务繁重的问题,实时性显著提升
五、后续工作
腾讯云数据仓库 TCHouse-C 全新推出的高性能实时数据更新方案,能够满足高频数据实时去重及轻量级部分列更新场景的需求。当前方案仍存在一些不足,包括单节点单表支持的数据规模有限、节点重启时数据加载时间较长、单次大规模写入数据时更新索引阶段表锁影响查询性能等。后续,我们将持续改进和优化数据实时更新功能,也欢迎您联系我们交流体验新一代产品能力!
评论