ClickHouseNotes

第 18 章:分片键设计

zjc 于 2026-01-18 发布

这是《ClickHouse 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 分片键决定数据分布在哪个物理分片上。好的分片键能让数据均衡、查询并行、业务键稳定;差的分片键会带来热点、倾斜、跨分片查询和数据迁移难题。

18.1 为什么分片键重要

events_all
  shard 1: user_id hash % 3 = 0
  shard 2: user_id hash % 3 = 1
  shard 3: user_id hash % 3 = 2

影响:

维度 影响
数据均衡 各分片磁盘和写入量
查询并行 单分片或全分片扫描
业务局部性 同键数据是否同分片
扩容成本 路由变化是否需要迁移
聚合语义 去重和状态合并

18.2 常见分片键

随机:

ENGINE = Distributed(..., rand())

按用户:

ENGINE = Distributed(..., cityHash64(user_id))

按订单:

ENGINE = Distributed(..., sipHash64(order_id))

按用户和日期:

ENGINE = Distributed(..., cityHash64(user_id, event_date))

按业务城市:

ENGINE = Distributed(..., city_id)

业务字段直连路由容易倾斜,通常更推荐 hash 后由分片权重处理。

18.3 选择流程

1. 明确查询是否必须按某键聚焦
2. 检查键的基数和均匀性
3. 检查热点键占比
4. 确认同键数据是否需要同分片
5. 评估未来扩容
6. 测试写入和查询分布
7. 记录路由规则

18.4 数据倾斜检查

SELECT
    shardNum() AS shard,
    count() AS rows,
    sum(amount) AS gmv
FROM analytics.events_all
WHERE event_date = today()
GROUP BY shard
ORDER BY shard;

如果只查本地表,需要在各节点分别执行。更实际的监控方式是采集:

指标 说明
每分片写入行数 写入热点
每分片写入字节 网络和磁盘热点
每分片磁盘用量 容量倾斜
每分片 CPU 计算热点
每分片 part 数 合并压力

18.5 查询局部性

如果查询常按 user_id

SELECT *
FROM analytics.events_all
WHERE user_id = 1001;

使用 cityHash64(user_id) 可以定位一个分片。

如果查询按 order_id,而分片键是 user_id,则订单可能分布多分片。对 ReplacingMergeTree 或 CollapsingMergeTree,同一业务键跨分片还会破坏合并语义。

18.6 可聚合性

优先选择可以让聚合下发的分片方式:

指标 跨分片合并
sum 可合并
count 可合并
min / max 可合并
avg 需 sum + count
uniqExact 需合并精确集合,成本高
uniq 可合并近似状态
quantile 可合并状态

全局精确去重是最容易出问题的查询。可用方案:

  1. 保证去重键与分片键一致;
  2. 使用近似 uniq;
  3. 离线全局计算;
  4. 维护 bitmap 或 HLL 状态;
  5. 专用去重服务或引擎。

18.7 扩容与路由稳定性

传统 hash 取模在分片数变化时会导致大量数据路由改变:

hash(key) % 3 -> hash(key) % 5

ClickHouse 的加权分片和路由实现与版本、集群配置有关。规划扩容时应评估:

  1. 新旧路由规则;
  2. 历史数据是否迁移;
  3. 查询是否跨新旧分片;
  4. 业务键合并语义;
  5. 切换窗口;
  6. 回滚方案。

18.8 复合分片键

cityHash64(user_id, event_date)

优点:

  1. 分布更均匀;
  2. 降低单用户热点;

缺点:

  1. 按 user_id 查询无法定位单分片;
  2. 同一用户数据分散;
  3. 状态合并更复杂。

如果业务需要用户状态表,不要为了均衡拆散业务键。可使用用户分桶,让一个桶内用户仍然聚集。

18.9 特殊场景

时间序列

按时间分片会把写入集中到最新分片,通常不建议。时间主要交给分区,分片键选择实体 ID。

多租户

小租户可以 hash 后均匀分布;大租户需要独立处理,例如独立集群、独立分片或逻辑表。

状态表

订单、用户状态等 Replacing/Collapsing 表,应保证业务键稳定路由到同一分片。

18.10 分片键评估表

候选键 评估
rand 均衡,但无法聚焦查询
user_id 适合用户行为和状态
order_id 适合订单明细
device_id 适合设备分析
event_date 容易热点,不适合
city_id 低基数且倾斜
tenant_id 大租户易热点

本章小结

分片键设计要同时考虑数据均衡、查询局部性和业务语义。时间交给分区,实体 ID 常用于 hash 分片;状态表必须保证业务键稳定路由。上线前用写入、磁盘、查询和去重场景验证,不要只看理论均匀。

思考题

  1. 随机分片有什么优缺点?
  2. 为什么 event_date 不适合做分片键?
  3. 业务状态表对分片键有什么要求?
  4. 如何发现数据倾斜?
  5. 扩容时为什么要评估路由稳定性?