StarRocks 存算分离在得物的降本增效实践
一、背景
OLAP 引擎在得物的客服、风控、供应链、投放、运营、ab 实验等大量业务场景发挥重要作用,在报表、日志、实时数仓等应用场景都有广泛的应用。
得物引入和使用 OLAP 引擎的过程中,每个业务都基于自己的需求选择当时最适合自己的引擎。现在得物内部同时使用阿里云上的 Hologres、ADB、Clickhouse 与 ECS 自建的开源 Clickhouse 与 StarRocks 5 种引擎产品,从业务使用成本和运维维护的角度长远看都是不利的。
2024 年,我们开始向只保留 1 到 2 个引擎的方向努力。最近,我们把一套 4000+核的业务,最复杂的 Clickhouse 集群迁移到了存算分离的自建 StarRocks 上,成本节约 40%,查询耗时降低一半,单节点不可用导致的集群不可用耗时从 15 分钟减少到了 30s。
二、Clickhouse 在大数据量下的困境
Clickhouse 虽然单机性能首屈一指,但分布式架构存在水平扩展性、元数据管理、数据一致性、join 查询性能等一系列问题。被迁移的这个 Clickhouse 集群服务于得物的智能运营平台,使用了超过 4000 核,存储超过 500TB。随着业务增长,集群面临一些实际问题。
物化视图缺乏透明改写能力
智能运营平台日常需要查询周/月/季/年 环比/同比,查询时间跨度长,并且为了加速查询建立了 40+物化视图。Clickhouse 没有自动的透明物化视图改写能力,需要由外部代码主动管理、创建物化视图和改写 SQL 去查询物化视图。在需要重刷历史数据时,由外部代码管理的物化视图的重刷尤为复杂。
缺乏离线导入功能
开源 Clickhouse 缺少 bulkload 能力,智能运营需要每天从离线平台导入大量数据到 Clickhouse,导入链路存在格式转换,效率低的问题,并且会占用大量 Clickhouse 集群资源,导入链路的数据产出有时会晚于基线。
扩容困难
虽然使用了很多物化视图,但集群负载仍然开始接近硬件能力上限。业务想要继续扩容,但面临了两个问题:
垂直扩容没有空间:集群使用的 Clickhouse 单机规格已经是顶配,没有升配空间。
水平扩容停机需停机一周:因为 Clickhouse 水平扩容后为了查询正确性考虑,已经按指定字段分桶的表,需要重新导入数据才能正确的 resharding,这个过程需要一周的停服扩容和导入。
业务方只能搭建了一个小一些的备份集群,存储最核心的数据,与主集群构成双活来分流部分查询,来减小主集群负载,这样增加了 50%+成本。
在得物增加了对 StarRocks 的研发投入后,智能运营下定决心基于 StarRocks 存算分离做 POC,并最终顺利迁移到自建的 StarRocks 上。
三、基于 StarRocks 降本增效
存算分离带来成本下降
StarRocks 3.0 起支持存算分离,在 3.3 版本已经比较成熟了。
StarRocks 存算分离架构:
(cn 是 compute node,是无状态计算节点+本地缓存盘)
安全可靠使用的单副本
对比存算一体的 3 副本模式,存算分离使用单副本。存算分离的全量数据数据存储在远端对象存储上(上图的 Distributed Storage,我们使用的是阿里云的 OSS),即使 CN 节点挂了,其他 CN 节点也仍然可以查询到数据(虽然需要重新拉取缓存数据,查询耗时会增加),所以是可以安全可靠使用的单副本模式。这带来 2/3 的存储成本下降(本地盘只作为 data cache 之用)。
只缓存必要数据
并且存算分离不需要把所有的数据都存储在本地盘,而只需要缓存常用数据即可,在单副本之上又节省大笔存储成本,并且查询性能在使用本地缓存后能做性能一致。
并且大量使用物化视图,减少基表实际需要存储在 data cache 中的数据量。
上图说明 506TB 的数据实际只在缓存中存储了 342TB 经过评估存算分离部署模式能带来 40%+的成本下降,存储成本下降 1 - (1/3*3/5)=4/5。
扩缩容无需搬迁数据
StarRocks 存算一体可以任意水平扩容,无需停机,扩容后自动均衡搬迁数据。StarRocks 存算分离更做到了扩缩容无需搬迁数据,扩容的新节点马上就可以被利用,这在使用容器方案部署 StarRocks 时,尤为方便。
在复杂 SQL join 能力上大幅领先 Clickhouse
https://docs.starrocks.io/zh/docs/benchmarking/SSB_Benchmarking/
StarRocks 更适合星型模型,并且 StarRocks 与 Clickhouse 单表查询性能不相伯仲(数据来源:clickbench 单机性能测试 https://benchmark.clickhouse.com/)
Clickhouse 支持的 join 类型非常有限且使用复杂,StarRocks 支持各种 Join 类型且符合标准 SQL 语法的预期,这让用户无需特别关注 Join 的特殊写法带来的性能问题(指 Clickhouse 的分布式和 local 表 join 问题),专注于业务逻辑即可。
高效的离线导入
从 Clickhouse 换成 StarRocks 后,离线导入的流程发生了变化。
首先在离线平台新建一个 OSS 外表,然后执行离线平台的 insert sql select 语句从离线平台读取数据以 parquet 格式写入到 OSS 外表中,然后 StarRocks 从对象存储(OSS)直接 broker load 导入数据。对比 Clickhouse 只能 insert 导入,虽然还是使用 StarRocks 集群资源,但通过控制 broker load 并发可以把集群资源的消耗控制在可接受范围内。压测显示离线导入耗时比 Clickhouse 方案减少 2/3。
重度使用物化视图进行提效
透明物化视图改写
查询可以自动透明改写到物化视图,用户无需改 SQL,并且保证数据的正确性。以往需要用户在外围主动管理 Clickhouse 物化视图构建,数据重刷和查询改写,这引入了额外的复杂性。现在物化视图由 StarRocks 自管理,不再需要外部代码主动改写,减少了出错的可能,也提供了不侵入用户逻辑即可提升性能的方式。
使用技巧
1、不命中物化视图时,在资源组中限制大表时间跨度超过 8 天就不允许查询。
2、approx_count_distinct 改写成 hll_union_agg(hll_hash(col))让查基表与查物化视图的结果完全一致 3、使用明细物化视图减少数据读取量 4、物化视图只基于单表,方便在各种 join sql 语句中复用 5、一个物化视图包括尽量多的指标,查询时一次性查多个指标 6、materialized_view_rewrite_mode=‘force‘让一个查询中多个 countdistinct 也能命中物化视图
物化视图
推荐手工对 SQL 分析和创建物化视图毕竟效率有限,所以我们开发了物化视图推荐功能。
1、通过在 fe 中记录 SQL 结构,在外部实现基于单表的物化视图推荐程序 2、能做到对表/物化视图字段的在过滤条件中的命中次数进行统计,用来判断哪些字段做排序键能适配更多的查询
3、能做到对单表的子语句用到的指标和维度列进行分析,找到有高收益的潜在物化视图,并且排除已经存在的物化视图。4、物化视图启用 collocate group 现在物化视图推荐功能已经初步上线,并还在持续优化中。
优化选择策略和性能
1、优化选择的性能,跳过物化视图与无关表的匹配(https://github.com/StarRocks/starrocks/pull/51010)。2、提早剔除一些不包含查询需要的列的物化视图,减少了后续匹配测试的开销(https://github.com/StarRocks/starrocks/pull/51044)。3、优先选中排序键匹配查询过滤条件的物化视图,再优先选行数最少的物化视图(https://github.com/StarRocks/starrocks/pull/51511)。
扩展物化视图可用场景
物化视图作为提升查询性能的最关键也是最有效的手段,在 3.3 刚发布时存在一些 BUG,导致很多场景下物化视图不能被命中。其中社区帮助修复一些问题,我们也修复 8 个命中问题,并且都已经提了 PR 并合并。(https://github.com/StarRocks/starrocks/pull/46472https://github.com/StarRocks/starrocks/pull/47648https://github.com/StarRocks/starrocks/pull/48001https://github.com/StarRocks/starrocks/pull/48092https://github.com/StarRocks/starrocks/pull/48142https://github.com/StarRocks/starrocks/pull/48773https://github.com/StarRocks/starrocks/pull/48984https://github.com/StarRocks/starrocks/pull/49168)
修复物化视图刷新问题
1、支持刷新顺序为从新分区到老分区,这样最近的数据能最快得到加速(https://github.com/StarRocks/starrocks/pull/46691)。2、修复了 force 刷新不生效的问题(https://github.com/StarRocks/starrocks/pull/52081)3、修复物化视图在 image 创建后被非预期 inactive,重启后被强制刷新的问题(https://github.com/StarRocks/starrocks/pull/51388)。4、反馈给社区修复了 fast schema evolution 导致的 mv 非预期刷新问题
四、具体迁移过程
Clickhouse 灰度迁移 StarRocks
智能运营并不直接查询 StarRocks,而是经过中间的 ONE DSL 平台翻译 DSL(一种类 SQL)成 Clickhouse/StarRocks SQL 后发给后端引擎。通过查询时指定 DSL 目标翻译类型,决定发到 Clickhouse 或者 StarRocks,这样在迁移过程中可以按接口灰度,然后逐个的迁移,有问题可以随时单独回滚某个接口。
语法兼容
兼容 Clickhouse 函数
我们仿造 StarRocks 中已有的 Trino 的 AstBuilder 实现了 Clickhouse 的 AstBuilder,通过新增 sql_dialect=clickhouse 选项,让兼容改动不污染 StarRocks 原来的行为。
我们兼容了业务方用到的 72 个函数,覆盖了最常用 Clickhouse 函数,业务方大部分情况下不需要修改查询 SQL,从而减小了迁移需要的工时。可以做到一个 Clickhouse 函数对应一个 StarRocks 函数或者多个函数的组合,并且将来新增一个 Clickhouse 函数映射是非常容易的。
我们还实现了 Clickhouse 的 ArrayJoin 函数,能在 StarRocks 中使用一样的方式进行列转行:-- StarRocks 原生写法 select u from tbl, unnest(array_column)-- ArrayJoin 的写法 select arrayJoin(array_column) from tbl
不过 Clickhouse 的 arrayJoin 语法仍然是不支持的,需要手工改写成 StarRocks 的 unnest 语法。
此外,我们还实现了 Clickhouse 式的别名引用,可以在 select 的后段引用前段定义的别名(https://github.com/StarRocks/starrocks/pull/43919):Select id as id_alias, sum(a) as b, concat(b, ',', '_') from xx where id_alias > 100 group by id_alias
Clickhouse 表/物化视图转换工具
在引擎之外做了转换工具,能够以程序的方式将 Clickhouse 的建表和建物化视图的 DDL 转为 StarRocks 的 DDL,节约了手工转换的时间的同时也避免手工转换时可能发生的改写错误。
优化查询性能
修复特定性能问题
前面提到的修复多个场景的物化视图命中问题和优化物化视图选择策略性能
分区字段查询带函数导致物化视图分区裁剪失败(https://github.com/StarRocks/starrocks/pull/46786)
优化数值与字符串类型隐式转换(https://github.com/StarRocks/starrocks/pull/50168):对应 sql 性能提升 20 倍。
优化 date/datetime 被当做字符串使用时的规则,从而命中稀疏索引命中(https://github.com/StarRocks/starrocks/pull/50643):对应 sql 性能提升 30 倍。
减少查询时存算分离 staros 的 rpc 调用次数(https://github.com/StarRocks/starrocks/pull/46913) + 简易 rpc 结果缓存:查询耗时普遍减少 3s+。
创建大量物化视图
通过持续对 io 高/cpu 高的查询针对性优化,发现有大量查询是可以用物化视图加速的。在原有 Clickhouse 迁移过来的物化视图之外,又增加了许多物化视图。目前为止已经有 140+物化视图。最大的 3 张表占总存储的 50%,对应了 60+物化视图。
表结构优化
StarRocks 所有的表结构都继承自 Clickhouse 的表结构,在实际线上运行过程中,发现了很多 schema 不合理的地方。通过对表和物化视图的排序键、分桶键、分桶数量的优化让其符合最常用的过滤条件中的字段的顺序,性能得到极大提升(如何设计合理排序键,以便查询利用前缀索引加速https://docs.starrocks.io/zh/docs/table_design/indexes/Prefix_index_sort_key/)。比如某张表的排序键优化后,查询性能提升了 150 倍。
找到问题 SQL 让用户修改写法
通过持续对 io 高/cpu 高的查询针对性优化,发现有很多 StarRocks rule 不能等价转换,但从业务角度看是等价的小改动,可以显著提升性能的场景。
1、换种写法改进 plan 比如下面的极端 case 的改动提升了 250 倍性能,因为它导致估算的行数变少,从而优化了 join order 的顺序,生成了更好的 plan。
2、把 limit 提前到 join 语句里
业务上会先 join 维度表取得更多字段,最后再 order by join 左分支中的字段再 limit。改成把 limit 放到 join 左分支中,然后再 join 维度表。这样减少了 join 的行数,降低 70%耗时。
3、去除 join key 上不必要的函数调用
业务方 join on upper(query)上,但所有 join 到的表的数据其实都是小写(这个信息只有业务方自己才知道),根本不需要转成大写后再 join。去除 upper 函数再 join 提升至少一倍性能。
4、去除不必要的 toString
用户喜欢 join 时把数值字段 toString 之后 join,这样去掉 toString 也可以减少计算量提升性能。除了 join key,还有很多 select 地方也喜欢用 toString,也都是非必要的。
5、子查询合并,消除 join。收益:性能提升约 50%
6、维度表过滤条件下推,收益:查询超时->0.1s
运维和可观测性增强
除了上面引擎方面的改动,还配合业务方的管理需求,支持了一些特定的 Api,比如:
资源水位 API
SQL 复杂度因子 API
根据表名获取物化视图列表和物化视图的具体列信息 API
指定 custom_query_id,可以异步 kill query 的能力(https://github.com/StarRocks/starrocks/pull/47632;https://github.com/StarRocks/starrocks/pull/48114)
五、成果展示
得到社区的给力支持,以及与智能运营团队紧密的合作,我们最终顺利从 Clickhouse 迁移到 StarRocks,并且在查询性能耗时减少为 Clickhouse 查询耗时的一半。
以下是获得的收益:
成本收益:智能运营底层 AP 引擎费用下降 40%,存储费用下降 5/6。
性能和体验收益:智能运营的大盘的 P95 耗时从最初的 8.5s 降低到当前的 4.3s;P0 页面低基维度的耗时从最初的 9.07s 降低到 4.77s,高基维的 P95 耗时从 24.38s 降低到 11.94s; 查询成功率从 98.57%提升到 99.44%。
数据时效性收益:在双跑期间 clickhouse 的基线 SLA 达成率是 99.42%,StarRocks 的基线 SLA 达成率是 100%。
稳定性收益:集群因为单节点异常导致集群不可用的时间从 15min 缩短到 30s 内。
我们与社区保持了密切的接触,会提交一些重大 issue,同时也贡献我们的修复给社区。迁移过程中共提交 PR 28 个,已 merge 24 个(https://github.com/StarRocks/starrocks/pulls?q=is%3Apr+author%3Akaijianding)。在迁移过程中,开发了 Clickhouse 函数兼容等 10+功能,修复 40+个问题(包括反馈给社区修复的)。
六、总结
此次迁移达成了预期的成本和性能的收益目标,也拓展了集群未来的成长空间,也让业务团队和引擎团队都更加的了解 StarRocks,收获大量迁移经验,为将来迁移其他业务提供了有说服力的范例。
在迁移过程中,我们与社区保持了紧密的联系,获得了社区大量帮助,也贡献了大量 patch 给社区,减少社区其他人需要踩的坑。在我们得物内部 StarRocks 的未来规划中,我们也将继续深度参与社区。
往期回顾
1.基于 Redis 内核的热 key 统计实现方案|得物技术 2.盘点这些年搭建器在用户体验优化的实践|得物技术 3.Java 性能测试利器:JMH 入门与实践|得物技术 4.彩虹桥架构演进之路-负载均衡篇|得物技术 5.解析 Go 切片:为何按值传递时会发生改变?|得物技术
文 / Kaijian 关注得物技术,每周更新技术干货,要是觉得文章对你有帮助的话,欢迎评论转发点赞~未经得物技术许可严禁转载,否则依法追究法律责任。
版权声明: 本文为 InfoQ 作者【得物技术】的原创文章。
原文链接:【http://xie.infoq.cn/article/8c3d1dd3891063b09f30ebe05】。未经作者许可,禁止转载。
评论