1. 时序场景为什么适合 ClickHouse
监控、IoT、日志、指标——这些都是"以时间为主轴的追加型数据":写入连续、量大、查询按时间窗口聚合。
TSDB 需求:
高频写入(百万级/秒)
时间窗口聚合(1m/5m/1h)
长期保留 + 降采样
快速查询"最近 N 小时趋势"
ClickHouse 的优势:
列式 + 压缩 → 海量时序低存储成本
向量化聚合 → 秒级扫描数十亿点
MergeTree 有序 → 时间范围剪枝高效
TTL → 自动生命周期管理
一句话总结:时序数据的本质是"有序追加 + 窗口聚合",而 MergeTree 的有序性、压缩与 TTL 恰好精准命中这些需求——这是 ClickHouse 在时序赛道的底气。
2. 时序表设计
2.1 表结构设计要点
CREATE TABLE metrics (
ts DateTime64(3),
host String,
metric String,
value Float64
) ENGINE = MergeTree()
PARTITION BY toDate(ts) -- 按天分区,利于 TTL/裁剪
ORDER BY (metric, host, ts) -- 排序键:先维度后时间
TTL toDate(ts) + INTERVAL 30 DAY; -- 自动清理 30 天前数据
2.2 设计原则
| 要素 | 推荐 | 原因 |
|---|---|---|
| 时间列 | DateTime64(3) 或带毫秒 | 精度可控 |
| 排序键 | (维度, 时间) | 维度过滤 + 时间范围双剪枝 |
| 分区键 | toDate(ts) | 按天 TTL、按天删除 |
| 唯一标识 | 无(OLAP 不强制唯一) | 追加语义 |
2.3 多指标 vs 单列
方案 A:宽表一列一指标
ts, cpu_usage, mem_usage, disk_usage
查询简单,但指标扩展需改表
方案 B:窄表 (metric, value)
ts, metric, value
新指标无需改表,查询需过滤 metric
常用:监控、IoT(指标动态)
一句话总结:时序表设计的黄金三角是"时间分区 + 维度优先排序键 + TTL",而"宽表 vs 窄表"取决于指标集合是否固定。
3. 时间窗口聚合
3.1 间隔分桶函数
-- toStartOfInterval(推荐,支持任意间隔)
SELECT
toStartOfInterval(ts, INTERVAL 5 MINUTE) AS bucket,
count() AS cnt,
avg(value) AS avg_val
FROM metrics
WHERE metric = 'cpu'
GROUP BY bucket
ORDER BY bucket;
-- dateBin(对 Date 类型)
SELECT dateBin(INTERVAL 1 DAY, ts, toDateTime('2020-01-01')) AS day, ...
3.2 同比 / 环比
-- 环比:今天的值 vs 昨天的值
WITH today AS (
SELECT toStartOfInterval(ts, INTERVAL 1 HOUR) AS h, avg(value) AS v
FROM metrics WHERE metric = 'cpu' GROUP BY h
)
SELECT
t.h,
t.v AS today,
y.v AS yesterday,
(t.v - y.v) / y.v * 100 AS pct_change
FROM today t
LEFT JOIN today y ON y.h = t.h - INTERVAL 1 HOUR;
3.3 滑动窗口
-- 每行取"过去 1 小时"的累计值(窗口函数)
SELECT
ts,
sum(value) OVER (
PARTITION BY host
ORDER BY ts
ROWS BETWEEN 60 PRECEDING AND CURRENT ROW
) AS rolling_sum
FROM metrics
WHERE metric = 'requests';
一句话总结:窗口聚合的三件套是"间隔分桶 + 同比环比 + 滑动窗口"——分别回答"趋势、变化率、滚动累计"三类问题。
4. 降采样与稀疏化
数据量随时间线性膨胀,长期保留原始秒级数据代价高。降采样在保留趋势的前提下压缩数据。
4.1 物化视图降采样
-- 原始数据 → 5 分钟聚合表(长期保留)
CREATE MATERIALIZED VIEW metrics_5m
ENGINE = AggregatingMergeTree()
ORDER BY (metric, host, bucket)
AS SELECT
toStartOfInterval(ts, INTERVAL 5 MINUTE) AS bucket,
metric, host,
avgState(value) AS avg_value,
maxState(value) AS max_value,
minState(value) AS min_value
FROM metrics
GROUP BY bucket, metric, host;
4.2 查询降采样结果
SELECT bucket, avgMerge(avg_value) AS avg_val
FROM metrics_5m
WHERE metric = 'cpu'
ORDER BY bucket;
4.3 降采样策略
| 层级 | 粒度 | 保留期 | 用途 |
|---|---|---|---|
| 原始 | 秒级 | 7 天 | 精确排障 |
| 5 分钟 | 5m | 30 天 | 日常监控 |
| 1 小时 | 1h | 365 天 | 趋势分析 |
| 日聚合 | 1d | 永久 | 容量规划 |
4.4 TTL 配合降采样
-- 原始表 TTL:7 天过期(明细)
TTL toDate(ts) + INTERVAL 7 DAY;
-- 降采样表单独保留 30 天
一句话总结:降采样是"用物化视图把原始数据按粒度分梯"——秒级进明细、分钟级进短期聚合、小时级进长期归档,TTL 各层自动清理。
5. 时序指标 SQL 速查
5.1 常用指标
| 指标 | SQL |
|---|---|
| 均值 | avg(value) |
| 最大/最小 | max(value) / min(value) |
| 极差 | max(value) - min(value) |
| 变异系数 | stddevSamp(value) / avg(value) |
| 分位数 | quantile(0.95)(value) |
| 计数 | count() / uniq(host) |
5.2 区间 / 变化
-- 值的变化量(相邻采样差)
SELECT
ts,
value,
value - lagInFrame(value) OVER (ORDER BY ts) AS delta
FROM metrics
WHERE metric = 'cpu'
ORDER BY ts;
-- 变化率
SELECT
ts,
value,
(value - lagInFrame(value) OVER (ORDER BY ts))
/ lagInFrame(value) OVER (ORDER BY ts) * 100 AS pct_delta
FROM metrics
ORDER BY ts;
一句话总结:时序指标的核心是聚合 + 变化——avg/quantile 描述"水平",lagInFrame 描述"变化",组合起来覆盖大部分监控分析。
6. SQL 异常检测
6.1 移动平均基线
-- 用过去 1 小时均值作为基线,当前值偏离多少倍标准差
WITH stats AS (
SELECT
avg(value) AS mu,
stddevSamp(value) AS sigma,
now() - INTERVAL 1 HOUR AS win_start
FROM metrics
WHERE metric = 'cpu' AND ts >= now() - INTERVAL 1 HOUR
)
SELECT
m.ts, m.value,
(m.value - s.mu) / s.sigma AS zscore
FROM metrics m, stats s
WHERE m.metric = 'cpu'
AND m.ts >= now()
ORDER BY m.ts;
6.2 Z-score 阈值检测
-- 找出超过 3σ 的异常点
WITH base AS (
SELECT ts, value,
avg(value) OVER (ORDER BY ts ROWS BETWEEN 60 PRECEDING AND CURRENT ROW) AS mu,
stddevSamp(value) OVER (ORDER BY ts ROWS BETWEEN 60 PRECEDING AND CURRENT ROW) AS sigma
FROM metrics WHERE metric = 'cpu'
)
SELECT ts, value
FROM base
WHERE sigma > 0 AND abs(value - mu) / sigma > 3;
6.3 同比异常(季节性)
-- 当前值与 7 天前同时刻对比
SELECT
today.ts, today.value AS now_val, past.value AS past_val,
(today.value - past.value) / past.value * 100 AS change_pct
FROM (SELECT toStartOfInterval(ts, INTERVAL 1 HOUR) AS ts, avg(value) AS value
FROM metrics WHERE metric = 'requests' AND ts >= today() GROUP BY ts) today
LEFT JOIN (SELECT toStartOfInterval(ts, INTERVAL 1 HOUR) AS ts, avg(value) AS value
FROM metrics WHERE metric = 'requests'
AND ts >= today() - INTERVAL 7 DAY AND ts < today() - INTERVAL 6 DAY
GROUP BY ts) past
ON today.ts = past.ts
ORDER BY change_pct DESC;
一句话总结:SQL 异常检测三板斧——移动平均基线、Z-score 偏离、同比季节性对比;不需要外部 ML,纯 SQL 就能覆盖多数阈值类告警。
7. 与 TSDB 的对比
7.1 ClickHouse vs 专用 TSDB
| 维度 | ClickHouse | InfluxDB / Prometheus |
|---|---|---|
| 写入吞吐 | 极高(列式批量) | 高 |
| 查询灵活性 | SQL 全能力 | 时序专用语法 |
| 压缩率 | 高(列式+Codec) | 中 |
| 生态 | 与数仓打通 | 监控生态(PromQL/Grafana) |
| 适用 | 统一分析平台 | 纯监控 |
7.2 何时选 ClickHouse 做时序
需要 SQL 灵活分析(跨表、复杂聚合)
需要与业务数仓同源分析
数据量大、压缩敏感
同时承载非时序分析(日志、业务)
监控面板接入:Grafana 原生支持 ClickHouse
8. 生产实践清单
| 主题 | 核心结论 |
|---|---|
| 表设计 | toDate(ts) 分区 + (维度,时间) 排序 + TTL |
| 宽/窄表 | 指标固定用宽表,动态指标用窄表 |
| 窗口聚合 | toStartOfInterval + 同比环比 + 滑动窗口 |
| 降采样 | 物化视图分梯,TTL 各层清理 |
| 异常检测 | 移动平均 + Z-score + 同比对比 |
| 与 TSDB | 要 SQL 灵活性选 ClickHouse,纯监控选专用 TSDB |
时序分析的最佳实践,是把"表设计、窗口聚合、降采样、异常检测"四个环节串成一条流水线——表设计决定能扫多快,聚合决定怎么算,降采样决定存多久,异常检测决定何时告警。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。