1. 容量规划的基本模型
ClickHouse 的容量规划可以归结为一个乘法模型:存储容量 = 日增原始数据量 × 保留天数 × 副本数 ÷ 压缩比。这个公式看着简单,但每一项都容易估错——尤其是压缩比与副本数,前者取决于列类型与编码,后者取决于副本与分片的设计。
规划前先明确四个输入:
| 输入 | 含义 | 获取方式 |
|---|---|---|
| 日增数据量 | 每天写入的原始字节数 | 上游日志/业务量估算 |
| 保留期 | 数据在热层保留多久 | 业务合规与查询需求 |
| 副本数 | 每份数据存几份 | 容灾等级 |
| 压缩比 | 原始/压缩 的比值 | 必须实测,不能拍脑袋 |
一个常见错误是用未压缩的原始大小直接乘天数,结果预留了 5~10 倍的过度容量。另一个极端是乐观地用「文本压缩比 10:1」估算结构化数据,导致磁盘提前打满。压缩比必须用真实数据实测。
2. 压缩比实测
2.1 用真实样本导入
CREATE TABLE sample_events AS events; -- 只复制结构
INSERT INTO sample_events
SELECT * FROM events
WHERE event_date >= today() - 3; -- 导入 3 天真实数据
SELECT
table,
formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed,
formatReadableSize(sum(data_compressed_bytes)) AS compressed,
round(sum(data_uncompressed_bytes) / sum(data_compressed_bytes), 2) AS ratio
FROM system.columns
WHERE table = 'sample_events';
2.2 逐列看压缩率
SELECT
name,
type,
formatReadableSize(sum(data_compressed_bytes)) AS compressed,
round(sum(data_uncompressed_bytes) / greatest(sum(data_compressed_bytes), 1), 2) AS ratio
FROM system.columns
WHERE table = 'sample_events'
GROUP BY name, type
ORDER BY sum(data_compressed_bytes) DESC;
这一步能暴露「哪列最占空间」。常见的压缩比参考:
| 列类型 | 典型压缩比 | 备注 |
|---|---|---|
| 时间戳(Delta 编码) | 20~50x | 单调递增,压缩极好 |
| 整数 ID(Delta) | 5~15x | 有序 ID 收益大 |
| 低基数字符串(LowCardinality) | 10~30x | 字典编码 |
| 高基数随机字符串 | 2~4x | UUID、哈希,难压 |
| Float(Gorilla) | 3~8x | 时序数据友好 |
| 高精度 Decimal | 3~6x | 取决于小数位 |
若某列压缩比远低于预期,说明编码选错了——压缩编码的完整选型见 /clickhouse-columnar-compression/。
2.3 用 system.parts 看现网
SELECT
table,
formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed,
formatReadableSize(sum(data_compressed_bytes)) AS compressed,
round(sum(data_uncompressed_bytes) / sum(data_compressed_bytes), 2) AS ratio,
formatReadableSize(sum(bytes_on_disk)) AS on_disk
FROM system.parts
WHERE active
GROUP BY table
ORDER BY sum(bytes_on_disk) DESC
LIMIT 20;
注意 data_compressed_bytes 是数据本身的压缩大小,bytes_on_disk 还包含索引、校验和等,通常略大。
3. 存储容量计算
3.1 分层存储模型
现代 ClickHouse 部署普遍采用冷热分层,容量规划要按层分别算:
-- 存储策略定义(storage_configuration)
-- 热层:NVMe SSD,保留 7 天
-- 温层:SATA SSD / HDD,保留 90 天
-- 冷层:对象存储 S3,保留 1~3 年
| 层 | 介质 | 保留期 | 单位成本 | 访问频率 |
|---|---|---|---|---|
| 热 | NVMe | 7 天 | 高 | 高 |
| 温 | SSD | 90 天 | 中 | 中 |
| 冷 | S3 | 1~3 年 | 低 | 低 |
分层的关键是用 TTL 规则把数据自动搬下去:
ALTER TABLE events
MODIFY TTL
event_date + INTERVAL 7 DAY TO VOLUME 'warm',
event_date + INTERVAL 90 DAY TO VOLUME 'cold',
event_date + INTERVAL 3 YEAR DELETE;
这样「热层容量」只需覆盖 7 天,而非全部数据,能省下大量 SSD 成本。S3 集成与分层搬运的细节见 /clickhouse-s3-object-storage-integration/。
3.2 计算示例
假设日增原始数据 500 GB,保留 1 年,压缩比 8:1,副本数 2:
原始年数据量 = 500 GB × 365 = 182.5 TB
压缩后 = 182.5 TB ÷ 8 = 22.8 TB
加副本 = 22.8 TB × 2 = 45.6 TB
加索引/元数据 ≈ 45.6 TB × 1.1 ≈ 50 TB
若全部放 NVMe,成本高昂;改为热 7 天 + 冷 S3,则热层只需约 500 GB × 7 ÷ 8 × 2 ≈ 875 GB,其余放 S3,成本可下降一个数量级。
3.3 预留余量
磁盘使用率不应超过 80%,因为:
- 后台合并需要临时空间(合并期间新旧 part 并存);
- mutation、
OPTIMIZE、备份都会瞬时占用空间; - ClickHouse 在磁盘接近满时会停止合并,进而阻塞写入。
按 80% 使用率反推,实际需要预留 1.25 倍的理论容量。
4. 计算资源规划
4.1 CPU
CPU 主要消耗在扫描与聚合上。经验配比:
- 热数据查询为主:每 TB 热数据配 8~16 vCPU;
- 写入为主:每 100 MB/s 写入配 8~16 vCPU(含合并开销);
- 高并发小查询:核数比单核性能更重要,优先多核。
4.2 内存
内存要同时满足「查询内存」与「OS page cache」:
总内存 ≥ 查询峰值内存 × 并发数 + OS 缓存预留 + 合并内存
经验值:内存容量 ≈ 热数据量的 10%~20%,用于 page cache 加速扫描;查询内存另算,通常按 max_memory_usage × 并发数 估算。若查询经常 spill 到磁盘,说明内存不足或 max_bytes_before_external_* 设得过低——内存管理机制见 /clickhouse-memory-management-spill/。
-- 查看当前内存压力
SELECT
metric,
formatReadableSize(value) AS val
FROM system.asynchronous_metrics
WHERE metric IN ('MemoryResident', 'MemoryVirtual', 'MemoryTracking');
4.3 IO
IO 规划看两个指标:
- 吞吐:扫描速度受限于磁盘带宽,热层建议 NVMe(>2 GB/s);
- IOPS:高并发点查依赖高 IOPS,SATA SSD 往往不够。
对象存储(S3)的延迟远高于本地盘,不适合承载热查询,只适合冷数据与低频分析。
5. 成本优化手段
5.1 编码与列裁剪
- 用
LowCardinality压缩低基数字符串; - 时间列加
CODEC(Delta, ZSTD); - 删掉从不查询的列(宽表尤其重要);
- 用
Decimal替代高精度Float反而可能更省(整数化存储)。
ALTER TABLE events
MODIFY COLUMN event_time DateTime CODEC(Delta, ZSTD(3)),
MODIFY COLUMN user_id UInt64 CODEC(Delta, ZSTD(3));
5.2 TTL 降采样
对时序数据,保留原始数据 7 天,之后只留分钟级聚合:
-- 原始表 7 天后删除
ALTER TABLE metrics_raw MODIFY TTL ts + INTERVAL 7 DAY DELETE;
-- 聚合表长期保留
CREATE TABLE metrics_1m (
ts DateTime, metric LowCardinality(String),
avg_v Float64, max_v Float64, cnt UInt64
) ENGINE = SummingMergeTree
ORDER BY (metric, ts)
TTL ts + INTERVAL 1 YEAR DELETE;
原始表 7 天 ≈ 秒级粒度;聚合表 1 年 ≈ 分钟级粒度,行数减少上千倍。这是**降采样(downsampling)**带来的最大成本收益。
5.3 减少副本与分片冗余
副本数是成本的直接倍数。需要权衡:
| 副本数 | 容灾能力 | 成本倍数 |
|---|---|---|
| 1 | 无 | 1x |
| 2 | 单节点故障 | 2x |
| 3 | 多节点故障 | 3x |
对可重建的日志类数据,2 副本足够;对不可重建的核心业务数据,3 副本更稳。分片则决定水平扩展能力,分片数一旦定下,后续调整成本高,规划时要留余量。
5.4 计算与存储分离
把冷数据放 S3、热数据留本地盘,再配合按需启动的计算节点,能大幅降低闲置成本。ClickHouse 的 S3 磁盘类型与 MergeTree 的 storage_policy 支持这种架构,让计算层可以弹性伸缩而不必搬数据。集群扩容与缩容的工程细节见 /clickhouse-cluster-scaling-upgrade/。
6. 成本监控
6.1 存储监控
-- 各表磁盘占用排行
SELECT
database,
table,
formatReadableSize(sum(bytes_on_disk)) AS size,
sum(rows) AS rows,
count() AS parts
FROM system.parts
WHERE active
GROUP BY database, table
ORDER BY sum(bytes_on_disk) DESC
LIMIT 20;
-- 各磁盘卷使用率
SELECT
name,
path,
formatReadableSize(free_space) AS free,
formatReadableSize(total_space) AS total,
round((total_space - free_space) / total_space * 100, 1) AS used_pct
FROM system.disks;
6.2 查询成本归因
按用户/查询指纹统计资源消耗,定位「谁在烧钱」:
SELECT
user,
count() AS queries,
formatReadableSize(sum(read_bytes)) AS total_read,
round(sum(query_duration_ms) / 1000) AS total_sec
FROM system.query_log
WHERE type = 'QueryFinish'
AND event_time >= now() - INTERVAL 1 DAY
GROUP BY user
ORDER BY sum(read_bytes) DESC;
把 read_bytes 与存储成本关联,就能算出「哪个业务在消耗最多 IO」。更系统的成本归因与 FinOps 实践可参考 数据 FinOps 成本优化
。
6.3 part 数量监控
part 数量过多会同时推高存储与合并成本:
SELECT
table,
count() AS parts,
formatReadableSize(sum(bytes_on_disk)) AS size
FROM system.parts
WHERE active
GROUP BY table
HAVING parts > 300
ORDER BY parts DESC;
单分区 part 数长期超过 300 说明合并跟不上,需要检查 background_pool_size 或写入批次大小。
7. 扩容阈值与告警
设定明确的阈值,让扩容成为可预期动作而非救火:
| 指标 | 告警阈值 | 动作 |
|---|---|---|
| 磁盘使用率 | 70% | 评估扩容 |
| 磁盘使用率 | 80% | 立即扩容 / 清理 |
| 单分区 part 数 | 300 | 检查合并配置 |
| 查询 P95 延迟 | 超过 SLA | 优化查询 / 加 CPU |
| mutation 积压 | > 50 未完成 | 排查写入模式 |
| 内存使用率 | 85% | 加内存 / 限并发 |
扩容前先确认瓶颈是存储、CPU 还是 IO——盲目加机器可能只解决其中一项。若瓶颈在存储,优先做 TTL 分层;若在 CPU,考虑编码优化与查询优化;若在 IO,考虑换 NVMe 或做冷热分离。
8. 常见陷阱
- 压缩比拍脑袋:必须用真实数据实测,不同业务差异可达 3 倍。
- 忽略副本倍数:3 副本意味着成本是裸数据的 3 倍。
- 不留合并余量:磁盘满会导致合并停止,进而阻塞写入,形成雪崩。
- 冷数据占着 SSD:TTL 分层没配好,老数据白白占用高成本介质。
- 不降采样:原始秒级数据保留数年,是最大的隐性成本。
- 只看总量不看分布:一张大表可能占了 80% 空间,优化它比扩容更划算。
小结
ClickHouse 容量规划的核心是那个乘法模型——日增 × 保留 × 副本 ÷ 压缩比,其中压缩比必须实测。成本优化则围绕「减少数据量」与「降低单位存储成本」两条主线:用编码、列裁剪、TTL 降采样减少数据量,用冷热分层把冷数据挪到对象存储。再配合基于 system.parts、system.disks、system.query_log 的持续监控与明确的扩容阈值,就能把容量与成本都控制在可预期的范围内。先优化、后扩容,永远是性价比最高的顺序。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。