容量规划与成本优化

系统讲解 ClickHouse 容量规划与成本优化:数据量/增长率/保留期/副本数/压缩比构成的容量模型、压缩比实测方法、存储与计算资源配比公式、冷热分层与 TTL 降采样、列裁剪与编码调优、基于 system.parts 的成本监控,以及扩容阈值与告警实践。

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~4xUUID、哈希,难压
Float(Gorilla)3~8x时序数据友好
高精度 Decimal3~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 年
层介质保留期单位成本访问频率
热NVMe7 天高高
温SSD90 天中中
冷S31~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 的持续监控与明确的扩容阈值,就能把容量与成本都控制在可预期的范围内。先优化、后扩容,永远是性价比最高的顺序。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「数据库」更多文章

  1. 从 MySQL/PostgreSQL 迁移的 SQL 差异
  2. 查询并发控制与资源隔离
  3. 轻量删除更新与变更语义