文章目录

兼容 是对前人努力的尊重 是确保业务平稳过渡的基石 然而 这仅仅是故事的起点
说实话,做时序数据平台,最容易被两件事拦住:一是"越存越贵",二是"越查越慢"。这两个问题,我在好几个项目里都见过。一开始大家觉得时序数据不就带时间戳的点嘛,能有多复杂?但真到生产环境,麻烦往往不在表怎么建,而在负载怎么来。写入像洪峰,查询像突发,留存像长跑,维度像碎片,这些特征叠在一起,很容易变成大家熟悉的"存储与分析困局"。
所以,时序项目真正要解决的往往不是"能不能存",而是四个字:稳、快、省、久。
时序数据的"困局"到底困在哪?
时序数据表面上就是"带时间戳的点"。但真到生产环境,麻烦往往不在表怎么建,而在负载怎么来。采集端一多,峰值一上来,系统很容易被顶满。看板、排障经常扎堆在某些时间窗,而且并发不低。不少关键行业要长期留存,还得随时能追溯。设备、区域、业务域、工单、班组这些维度一叠加,检索路径立刻变复杂。
我见过很多项目一开始都挺顺利的,数据量不大的时候什么都快。但过了三个月半年,问题就来了。表越来越大,索引越来越重,清理数据变成"全表删除",风险高、耗时长。维护窗口一到就抖,业务方最先感觉到的是"看板卡了"。
更糟糕的是,很多团队在看板需求面前,直接让查询扫明细表。扫描量巨大,I/O 很快顶到天花板。查询并发上来后,写入也被拖慢。结果就是大家熟悉的:白天看板卡、晚上批处理慢。
还有就是多维检索路径乱,SQL 口径不一致。同一个业务问题,换一拨人写 SQL,口径就可能完全变样。时间窗怎么算?分钟桶怎么取?迟到数据怎么算?维度怎么过滤?先过滤再聚合还是反过来?一旦口径散了,指标就会"看上去都对、但彼此对不上"。
从"存得下"到"存得好":这个问题到底是怎么演变的?
这个问题如果从时间维度来看,挺有意思的。
一开始大家关注的问题很简单,就是怎么把数据存进去。那时候数据量还没那么夸张,几万条记录撑死了,一张大表就全装下了。索引也朴素得很,就在时间字段和设备ID上建个联合索引,查询起来还挺快。但等到数据量上来,几百万几千万条记录一压,问题就全暴露出来了。
有人说用分区表能解决问题。这个想法本身没错,按时间切分确实是个思路。但我观察到的情况是,很多人用得不够精细。有的按月分区,有的按季度分区,但清理策略根本没跟上,分区越积越多,到最后谁都不敢删。还有的分区键选得不对,查询还是要扫大量分区,跟没分区没什么区别。更糟糕的是,分区的管理成本很高,运维团队经常为这个头疼。
后来预聚合的概念开始火了。大家好像突然开窍了:看板其实根本不需要明细数据,只要分钟级、小时级的聚合就够了。但问题又来了:预聚合怎么做?什么时候刷新?怎么处理迟到数据?这些问题又把很多团队难住了。我见过个项目,他们搞了个很复杂的聚合刷新机制,结果凌晨四点被业务方电话叫醒,说是聚合数据不对,排查了半天发现是迟到数据的处理逻辑有问题。
这些年来,时序数据库产品层出不穷,InfluxDB、TimescaleDB这些开源产品,还有各种国产专有产品。看起来选择很多,但实际上每个产品都有自己的一套逻辑,学习成本不低。更重要的是,很多产品功能单一,只能处理时序数据,跟企业的地理信息系统、文档图谱这些既有数据资产完全融不起来,最后又搞出新的"数据孤岛"。这让我想起一个现象:技术圈总在追求"专而精"的解决方案,但真实世界的业务场景从来不是那么干净的。
一套低成本可落地的参考建模:明细+聚合+事件
为了不把时序平台一上来就做成"越做越重"的大工程,中小企业更建议先把模型收敛到三张核心表:
明细表存原始点位,用于追溯与回放。聚合表存分钟/小时指标,用于看板与大多数报表。事件表存告警/工况事件,用于定位入口与关联分析。
这种建模的好处很直接:看板读聚合,排障走事件定位后再回放明细,系统很难被"无脑扫明细"拖垮。
明细表怎么建?
我一般这么建:
sql
CREATE TABLE telemetry_points (
ts TIMESTAMP NOT NULL,
device_id VARCHAR(64) NOT NULL,
metric VARCHAR(64) NOT NULL,
value DOUBLE PRECISION NOT NULL,
quality INTEGER DEFAULT 0,
PRIMARY KEY (ts, device_id, metric)
);
建议把常用查询路径(设备、指标、时间窗)固化为组合索引:
sql
CREATE INDEX idx_tp_device_metric_ts
ON telemetry_points (device_id, metric, ts);
分区通常按时间范围切分即可;如果你的业务维度很稳定、过滤又很高频,还可以结合"时间+业务维度"的联合切分思路,用来降低热点、缩小扫描范围。
聚合表怎么搞?
聚合表的设计要考虑查询模式,一般这样:
sql
CREATE TABLE telemetry_1m (
bucket_start TIMESTAMP NOT NULL,
device_id VARCHAR(64) NOT NULL,
metric VARCHAR(64) NOT NULL,
avg_value DOUBLE PRECISION NOT NULL,
min_value DOUBLE PRECISION NOT NULL,
max_value DOUBLE PRECISION NOT NULL,
cnt BIGINT NOT NULL,
PRIMARY KEY (bucket_start, device_id, metric)
);
聚合刷新建议给自己留"回补窗口",比如回补最近2小时,专门处理迟到/补传数据。这点很重要,很多项目就是因为没处理好迟到数据,导致报表不准确。
事件表怎么设计?
事件表主要用于告警和工况记录:
sql
CREATE TABLE telemetry_events (
event_ts TIMESTAMP NOT NULL,
device_id VARCHAR(64) NOT NULL,
event_type VARCHAR(64) NOT NULL,
severity INTEGER NOT NULL,
message VARCHAR(256),
PRIMARY KEY (event_ts, device_id, event_type)
);
把"查询口径"变成"可复用模板"
很多团队的问题不在"不会写SQL",而在"每个人都写一套"。金仓公开的时序实践强调把"区间筛选+时间窗口聚合"的写法尽量函数化、模板化,核心目的就是减少口径分裂,让压测、看板复用更顺手。
下面这三段通用SQL模板,基本能覆盖80%的业务需求。
区间回放:设备曲线
sql
SELECT ts, value
FROM telemetry_points
WHERE device_id = 'D-10086'
AND metric = 'temperature'
AND ts >= '2025-01-01 00:00:00'
AND ts < '2025-01-02 00:00:00'
ORDER BY ts;
这个查询很常见,但要注意,如果时间范围很大,还是要用聚合表。明细表适合短时间段的精确回放。
时间窗聚合:5分钟桶
sql
SELECT
(DATE_TRUNC('minute', ts) - (EXTRACT(minute FROM ts)::int % 5) * INTERVAL '1 minute') AS bucket_start,
AVG(value) AS avg_value,
MIN(value) AS min_value,
MAX(value) AS max_value
FROM telemetry_points
WHERE device_id = 'D-10086'
AND metric = 'temperature'
AND ts >= '2025-01-01 00:00:00'
AND ts < '2025-01-01 06:00:00'
GROUP BY bucket_start
ORDER BY bucket_start;
这个查询的思路是把时间戳对齐到5分钟的边界上。用DATE_TRUNC取到分钟,然后计算分钟数除以5的余数,减去对应的偏移量。这样就能把数据分成5分钟的桶。
连续告警:阈值+持续N分钟
告警处理有个经典问题:单点超阈值可能只是噪音,要持续超阈值才算真的告警。下面这个SQL能解决这个问题:
sql
WITH m1 AS (
SELECT DATE_TRUNC('minute', ts) AS minute_ts, AVG(value) AS avg_value
FROM telemetry_points
WHERE device_id = 'D-10086'
AND metric = 'temperature'
AND ts >= '2025-01-01 00:00:00'
AND ts < '2025-01-01 06:00:00'
GROUP BY DATE_TRUNC('minute', ts)
),
flag AS (
SELECT minute_ts, avg_value,
CASE WHEN avg_value >= 80 THEN 1 ELSE 0 END AS over_th
FROM m1
),
grp AS (
SELECT *,
SUM(CASE WHEN over_th = 0 THEN 1 ELSE 0 END) OVER (ORDER BY minute_ts) AS grp_id
FROM flag
)
SELECT MIN(minute_ts) AS start_ts,
MAX(minute_ts) AS end_ts,
COUNT(*) AS minutes
FROM grp
WHERE over_th = 1
GROUP BY grp_id
HAVING COUNT(*) >= 5
ORDER BY start_ts;
按分钟聚合,计算每分钟的平均值。标记哪些分钟超过了阈值。用窗口函数计算分组,遇到0的时候组号加1,这样连续的1就会在同一个组里。最后筛选出连续超过阈值至少5分钟的组。
金仓时序数据库的核心逻辑与差异化价值
企业级生态与管控,金仓时序数据库时序能力继承自其企业级数据库的全套生态,包括可视化的管理工具、细粒度的权限管控、与Oracle/MySQL高度兼容的语法,以及丰富的第三方工具连接性。相比之下,许多开源时序产品在企业级管控、安全审计和运维便利性上存在明显短板。
我在实际使用中发现,这些差异化优势在某些场景下特别关键。比如国家电网的用电信息采集系统,不仅要实时处理全省数千万智能电表产生的洪流数据,更需支撑精准的反窃电分析。得益于金仓时序数据库对复杂SQL和混合负载的强大支持,分析人员能直接在库内将实时电流波形数据与用户历史档案、地理信息进行即时关联与模型计算,将异常识别从"天级"缩短至"秒级",每年挽回的经济损失高达数亿元。
低成本落地指南:中小企业怎么选型、怎么上
中小企业做时序平台,最怕两件事:一开始就"上大而全",以及上线后"没有运维闭环"。更稳的路线是先把轻量闭环跑通,再跟着业务增长去扩展。
三种起步方案
单机起步,适合业务刚起量、团队小的情况。你要做的关键事是:分区+聚合层+备份恢复演练。
高可用起步,适合看板不可中断、停机敏感的场景。你要做:主备/切换演练+监控告警+运维流程。
分层扩展,适合写入与查询冲突明显的情况。聚合层承接看板、明细层承接回放。
成本控制的三个抓手
把"聚合层"当主力,不要让看板扫明细。看板默认读分钟/小时聚合,明细只在排障时回放。别小看这一条,很多项目的性能就是靠它稳住的。
用分区生命周期把"清理"变成常规操作。按分区归档/清理,别等表大到维护不动了才想补救。
做容量模型与压测基线,避免"拍脑袋扩容"。写入峰值、查询P95、磁盘增长、分区数量,这些指标最好从一开始就能看到、能复测。
关键行业落地时,最容易忽略的"最后一公里"
不少项目不是输在技术选型,而是输在最后一公里。
时间口径不统一,时区、采样周期、对齐方式要统一。我见过个项目,前端用UTC时间,后端用北京时间,结果报表差了8个小时,找了很久才发现。
质量位缺失,补传、估算、无效值要能标记,不然告警口径会乱。传感器数据经常有质量问题,比如传感器故障、通信中断,这些情况都要能标记和处理。
演练缺位,备份恢复、切换、清理窗口不演练,上线后会被动。很多人觉得演练浪费时间,但真出问题的时候,演练过没演练过差别大了去了。
一些实战中的坑
说到坑,我踩过不少,这里分享几个典型的。
索引不走的问题特别常见。很多开发问,为什么有索引而不走索引呢?因为优化器认为走索引方式太慢了!比如小表场景:
sql
CREATE TABLE sma(id int);
CREATE INDEX ON sma (id);
INSERT INTO sma VALUES(generate_series(1,100));
ANALYZE sma;
EXPLAIN SELECT id FROM sma WHERE id = 10;
你会发现优化器选择了顺序扫描而不是索引扫描。为什么?因为优化器认为走索引的总成本要比顺序扫描高了4倍左右。在小表场景中,优化器可能选择顺序扫描而不是索引扫描,因为服务器进程可能在1个数据块里就获得了所需数据,相比索引扫描,在索引块的叶子节点获得对应数据块的指针后还需要去表里去扫描取得行记录,这样扫描的总块数,或者说IO次数就会多余顺序扫描所需的块数。
还有关联度的问题。高效的索引扫描,只需扫描几次就能获取到所需数据,而糟糕的索引扫描往往需要返回大量数据,需要多次扫描,每次都要经历根节点→分支节点→叶子节点,索引的level越高,需要耗费的IO越大,导致大量的离散IO。
在金仓数据库中,我们可以使用cluster命令进行聚簇,对于某些时序类的数据进行范围查询,会有性能提升作用,因为索引排序和数据分布都是连续的。
返回结果集过大的情况下,索引也会失效。索引会对应大量的离散IO,我想说一下选择率这个词,如果大部分数据都需要查询到,那么索引的作用就微乎其微了,这时候再用索引扫描代价明显高于顺序扫描。
最左原则也要注意:
sql
CREATE TABLE t(id int,info text);
CREATE INDEX ON t(id,info);
INSERT INTO t SELECT n,md5(random()::text) FROM generate_series(1,100000) AS n;
ANALYZE t;
EXPLAIN SELECT id,info FROM t WHERE id = 1 AND info = 'hello';
EXPLAIN SELECT id,info FROM t WHERE info = 'hello';
你会发现第一个查询用到了索引,第二个没用到。因为我创建了一个复合索引,假设是(a,b,c)的复合索引,那么索引会先按照a列排序存储,接着按照b列排序,最后是c列,假如SQL直接查询的b或者c,意味着基可能需要访问整棵索引树,这样代价太高了。
查询条件模糊也会导致索引失效。如果查询条件使用了不等于(<>)、LIKE等运算符或者使用了函数等,那么索引可能无法被使用。因为正常情况下,等于(=)操作符可以直接利用B-tree或哈希索引进行查找。这是因为这些操作符可以在索引结构中找到对应的记录选项。而不等于(<>)操作符则需要查找所有不符合条件的记录,这会导致需要遍历整个索引树来找到匹配的记录,带来的结果是使用索引的成本比全表扫描成本更高。
LIKE操作符也可能导致无法使用索引。例如'%abc',则索引将不会被使用,如果通配符是abc%,则可以使用索引。
最后说说糟糕的统计信息。过期的,糟糕的统计信息会让优化器产生误判,可能由于某种原因导致autovacuum进程没有及时收集统计信息,这时需要做analyze手动收集统计信息,或者修改字段长度、数据类型、dml大量数据变更,也需要手工收集统计信息,否则选择率可能存在误差导致优化器估算不准。
对金仓方案的一些批判性思考:理想与现实的距离
虽然金仓的方案看起来挺完善的,但我还是想提一些批判性的思考。这不是为了黑它,而是想让大家在选择技术方案时能看得更清楚一点。
首先,融合架构确实能解决很多问题,但也有人认为这是"什么都做,什么都做不精"。专用时序数据库在某些特定场景下可能表现更好。金仓需要在性能和通用性之间找到平衡。但这个平衡点在哪里?不同的业务场景,答案可能完全不同。如果你的业务场景90%都是纯时序查询,只有偶尔需要跟其他数据源关联,那么专用时序数据库可能更合适。反过来,如果你的业务天然就是混合的,时序数据需要跟关系数据、地理信息、文档数据紧密关联,那金仓的融合架构优势就非常明显了。
其次,压缩比1:40这个数据太理想化了。我在实际项目中很少见到这么高的压缩比。真实情况可能更接近1:4到1:10,这当然也很有价值,但不要过度期待。不过话说回来,即使只有1:4,在存储成本上的节省也是相当可观的。我算过一笔账,一个中等规模的工厂,一年的传感器数据可能需要几十TB的存储,如果能压缩到十几TB,省下的硬件成本和运维成本都不是小数目。
还有,迁移成本是个现实问题。虽然金仓强调平滑升级,但从传统数据库迁移到金仓,特别是大规模的时序数据迁移,还是需要仔细规划和充分测试的。我见过几个项目,都是低估了迁移的复杂度,结果上线时间一再推迟。
数据迁移不只是把数据搬过去那么简单,应用层的SQL要适配,监控要重建,运维流程要调整,这些隐性成本往往被低估。
还有一个经常被忽视的问题是:团队的学习曲线。如果你的团队对PostgreSQL生态很熟悉,那学习金仓会很快。但如果团队背景主要是Oracle或者MySQL,还是需要一定的适应期。金仓虽然跟Oracle、MySQL语法高度兼容,但时序特性、分区策略、索引优化这些,还是需要专门的学习和实践。
最后我想说的是,没有万能的解决方案。金仓的融合架构在混合场景下确实很有优势,但如果你有一个明确的、纯时序的场景,还是应该评估一下专用时序数据库。关键是要根据你的具体业务场景、团队能力、时间预算来做决策,而不是被某个"万能方案"的噱头带跑偏。
写在最后
时序数据时代的存储与分析困局,说穿了就是:写入、查询、留存、治理四种压力同时压上来。金仓的价值在于把时序能力纳入融合数据库体系,并用工程交付的方式,把分层、切分、预聚合、治理与运维闭环组合成一套可落地、可验收的方案。
如果你希望用更低成本把时序数据管理先跑起来,建议先从"明细+聚合+事件"三表模型入手,把看板从明细查询里解耦出来;等系统稳了,再一步步把生命周期和运维闭环做扎实。
说到底,解决"困局"的关键,不是选个多牛的技术,而是把方案做成可验收的交付件。能长期稳定运行的,才是好方案。
想了解更多产品与方案,可以去金仓数据库官网看看:https://kingbase.com.cn