时序分析最佳实践:时间序列建模、降采样与异常检测 SQL

系统讲解 ClickHouse 处理时间序列数据的最佳实践:时序表设计(时间列/排序键/分区)、时间分组聚合(dateBin/toStartOfInterval)、降采样与稀疏化、滑动窗口与同比环比、常见时序指标(最大最小值/变异系数)、SQL 异常检测(Z-score/移动平均)、与 TSDB(InfluxDB/Prometheus)的对比,以及大规模时序写入与保留策略。

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 分钟5m30 天日常监控
1 小时1h365 天趋势分析
日聚合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

维度ClickHouseInfluxDB / Prometheus
写入吞吐极高(列式批量)高
查询灵活性SQL 全能力时序专用语法
压缩率高(列式+Codec)中
生态与数仓打通监控生态(PromQL/Grafana)
适用统一分析平台纯监控

7.2 何时选 ClickHouse 做时序

需要 SQL 灵活分析(跨表、复杂聚合)
需要与业务数仓同源分析
数据量大、压缩敏感
同时承载非时序分析(日志、业务)

监控面板接入:Grafana 原生支持 ClickHouse

8. 生产实践清单

主题核心结论
表设计toDate(ts) 分区 + (维度,时间) 排序 + TTL
宽/窄表指标固定用宽表,动态指标用窄表
窗口聚合toStartOfInterval + 同比环比 + 滑动窗口
降采样物化视图分梯,TTL 各层清理
异常检测移动平均 + Z-score + 同比对比
与 TSDB要 SQL 灵活性选 ClickHouse,纯监控选专用 TSDB

时序分析的最佳实践,是把"表设计、窗口聚合、降采样、异常检测"四个环节串成一条流水线——表设计决定能扫多快,聚合决定怎么算,降采样决定存多久,异常检测决定何时告警。

继续阅读

探索更多技术文章

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

全部文章 返回首页

「数据库」更多文章

  1. 查询缓存与预热:缓存策略、热点治理与查询加速
  2. 字典与维度表 JOIN:Dictionaries、dictGet 与星型模型优化
  3. 复制表与跨机房容灾:ReplicatedMergeTree、双活架构与脑裂防护