
海量带时间戳的数据(传感器、监控指标、行情快照)用关系型数据库存储,迟早会撞上三堵墙:写入顶不住、存储膨胀、时间范围查询越来越慢。 这篇文章用一个最小可运行的示例,带你走完"理解时序数据 → 数据建模 → 建表 → 写入 → 查询 → 聚合 → 连续聚合 → 压缩 → 保留策略"的完整链路,看完就能照着上手。
一条时序数据由四个要素构成,这是理解一切机制的地基:
概念 | 说明 | 示例 |
|---|---|---|
timestamp | 时间戳,数据发生的时间 |
|
metric | 指标名,量的是什么 |
|
tag | 标签/维度,用来"筛"数据 |
|
field | 数值,用来"算"数据 |
|
记住一条建模原则:tag 用来筛选,field 用来计算。 所有"按什么分组、按什么过滤"的维度都放进 tag,所有"求和、取均值、取最大"的量都放进 field。这一条搞反了,后面的查询和压缩都会很难受。
关系型数据库不是"不行",是它没为时序数据做任何优化,三个硬伤:
时序库围绕"时间"做了三件事来对症下药:按时间自动分片、列式存储、时序专用压缩。下面先讲原理,再上代码。
1. 超表自动分片(Hypertable / Chunk)
一张逻辑时序表,按时间窗口自动拆成多个物理子表(chunk)。写入只碰当前活跃 chunk,避免全局锁竞争;查询按时间条件精准定位 chunk,扫描量小几个数量级。这是时序库"写得进、查得快"的核心来源。
2. 列式存储 + 时序专用压缩
时序数据是数值型、按时间有序、相邻值高度相似,非常适合列式存储和专用压缩算法——Delta-of-Delta 差分编码(只存相邻时间戳的差值)和 Gorilla 浮点压缩(利用浮点数的位级规律)。典型数字型时序数据压缩比可达 10:1,存储降到原来的十分之一。
3. 连续聚合
监控场景里你要看的是"每小时 CPU 均值",而不是一亿条原始点。连续聚合就是提前按分钟/小时/天把聚合结果算好存进物化视图,查询直接读结果,用存储换查询速度。这一节后面有完整 SQL。
以最易上手的 TimescaleDB(PostgreSQL 扩展)为例,思路对所有时序库通用。
-- 建时序表
CREATE TABLE conditions (
time TIMESTAMPTZ NOT NULL,
device_id TEXT,
temperature DOUBLE PRECISION,
humidity DOUBLE PRECISION
);
-- 转成超表,按时间维度自动分区
SELECT create_hypertable('conditions', 'time');INSERT INTO conditions (time, device_id, temperature, humidity) VALUES
(now(), 'device_01', 75.5, 61.0),
(now(), 'device_02', 74.8, 62.0);大批量写入时用批插(batch insert)或专用写入协议,能把吞吐提一个数量级。
-- 最近 24 小时的数据
SELECT * FROM conditions
WHERE time > now() - interval '24 hours'
ORDER BY time DESC;-- 过去 7 天,按 5 分钟一桶做聚合(降采样)
SELECT time_bucket('5 minutes', time) AS bucket,
device_id,
avg(temperature) AS avg_temp,
max(temperature) AS max_temp,
min(temperature) AS min_temp
FROM conditions
WHERE time > now() - interval '7 days'
GROUP BY bucket, device_id
ORDER BY bucket;time_bucket 就是"时间分桶"的落地:把一秒一条的高频数据,抽稀成五分钟一个均值/最大值/最小值。趋势还在,数据量降了 300 倍。
-- 按小时预计算聚合结果,查询直接读它
CREATE MATERIALIZED VIEW conditions_hourly
WITH (timescaledb.continuous) AS
SELECT time_bucket('1 hour', time) AS hour,
device_id,
avg(temperature) AS avg_temp,
count(*) AS cnt
FROM conditions
GROUP BY hour, device_id;之后查"每小时温度均值"直接读 conditions_hourly,不用现场扫原始数据,响应从秒级降到毫秒级。
-- 开启列式压缩,按 device_id 分段、按 time 排序
ALTER TABLE conditions SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'device_id',
timescaledb.compress_orderby = 'time'
);
-- 7 天前的数据自动压缩
SELECT add_compression_policy('conditions', interval '7 days');-- 原始数据只保留 30 天,到期自动删除
SELECT add_retention_policy('conditions', interval '30 days');
-- 也可以手动清理指定时间之前的分片
SELECT drop_chunks('conditions', older_than => interval '30 days');不设保留策略是新手最常见的坑:时序数据"越旧越不值钱",不给它设保质期,存储会被历史数据慢慢吃光。标准做法是"原始数据存 30 天 → 降采样/连续聚合结果存一年 → 到期自动删"。
不同产品来自不同测试口径,横向直接比没有意义,重点看量级,最终以你自己的数据量做 POC 为准:
产品 | 类型 | 写入量级 | 压缩比(量级) |
|---|---|---|---|
金仓 KES TimeSeries | 融合多模(国产) | TSBS 基准 124 万+点/秒 | 约 10 倍 |
TDengine | 专用时序引擎(国产) | 单线程约 50 万点/秒 | 8~15 倍 |
InfluxDB | 专用时序引擎 | 约 20 万点/秒 | 5~8 倍 |
TimescaleDB | PostgreSQL 扩展 | 约 15 万点/秒 | 3~5 倍 |
场景 | 建议 |
|---|---|
时序数据与业务数据频繁关联、走信创 | 金仓 KES TimeSeries(融合多模,时序+关系同库 JOIN,兼容 InfluxDB 协议) |
纯 IoT / 监控采集,追性能与压缩 | TDengine / InfluxDB |
已有 PostgreSQL 技术栈、要复杂 SQL 分析 | TimescaleDB |
只搭监控看板 | Prometheus |
云上使用时序数据库,除了托管运维、弹性伸缩、跨可用区高可用,最能省钱的是冷热分层存储:
这个"原始明细下沉 + 聚合结果常驻"的组合,就是前面连续聚合和保留策略在云上的完整闭环。
时序数据库解决的就一件事:让海量带时间戳的数据存得下、写得进、查得快、还省钱。
如果你的数据"只写不改、每条带时间戳、要按时间查趋势",就别再用关系库硬扛了——换对数据库类型,往往比加机器更管用。本文的完整 SQL 链路(建表 → 写入 → 查询 → 聚合 → 连续聚合 → 压缩 → 保留策略)可以当作模板直接套用。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。