ClickHouse 集群查询时,Distributed 表怎么选分片键?如何避免数据倾斜?
我会先定目标和容量,再拆核心链路:Distributed 表本身通常不存业务数据,而是把写入路由到分片的本地 MergeTree 表,并把查询下发到相关分片后汇总结果。分片键决定数据分布,副本配置决定同一分片内的冗余与读取选择。 随机分片分布简单但难以裁剪;按租户或业务键分片可让相关查询落在少数分片,却要防止大租户热点。
我不会直接画架构图,会先确认目标、规模和一致性要求。理解本地表、分布式路由、分片键、副本选择和跨分片聚合。Distributed 表本身通常不存业务数据,而是把写入路由到分片的本地 MergeTree 表,并把查询下发到相关分片后汇总结果。分片键决定数据分布,副本配置决定同一分片内的冗余与读取选择。 随机分片分布简单但难以裁剪;按租户或业务键分片可让相关查询落在少数分片,却要防止大租户热点。
容量有了以后,再把入口、核心处理和数据落点串起来。沿着「客户端访问 Distributed 表 → 按 shardingkey 选分片 → 发送到本地表副本 → 查询各分片并行执行 → 协调节点合并结果」观察输入、状态与输出,这些阶段都可以从日志、指标或源码里验证。 客户端访问 Distributed 表 Distributed 表通常不存数据,只保存集群、远端数据库表和分片规则。 system.clusters:分片副本拓扑。 system.distributionqueue:异步分布写队列。 我会先确认请求实际走到了哪条路径,再用运行数据验证,不会只看类名或配置猜测。
关键参数要从峰值流量和资源上限反推。是否区分 Distributed 逻辑表与各节点本地表。 能否设计均匀且支持查询裁剪的分片键。 是否理解分布式聚合、网络和一致性成本。
正常链路之外,还要设计失败补偿和可验证的恢复流程。简单取模分片在节点数变化后路由改变,历史数据没有自动重平衡,同一用户跨多个分片。扩容前设计稳定分片槽或显式迁移,并用查询路由兼容过渡期。 shardingkey 低基数造成倾斜:分片行数/字节偏差:稳定 shardingkey。 协调节点承担大结果集合并:远程读写延迟:协调聚合前下推。 误以为增加副本能提升分片总容量:协调节点内存:扩容用槽迁移/核对。 固定输入和基线 先在没有故障注入的环境执行上述配置,固定数据规模、并发度、运行时版本和预热时间。以「max/avg shard rows」为主基线,记录值应满足「记录稳态基线」;同时保存 分片行数/字节偏差、远程读写延迟,使后续变化能够回到同一时间轴比较。
最后再讲扩容、成本和方案边界。方案:更适合的场景:主要收益:代价与边界。 随机分片:无实体局部性要求的明细流:写入均匀:按实体查询需广播。 业务键哈希:同实体聚合和局部查询:可减少跨片:超级实体可能热点。 预定义槽/权重:需要可控扩容迁移:路由稳定、可逐槽搬迁:管理元数据与迁移复杂。 选型至少带上 日增量、分区规模、查询并发、扫描行数和压缩比,并用上面的量化基线验证;未知数据应明确为待测假设。