时序数据库选型被写入峰值带偏了?金仓超表让我少加了不少班

时序数据库选型被写入峰值带偏了?金仓超表让我少加了不少班

去年做过一个工业物联网项目,设备点位不少,数据基本按秒、十几秒这个频率往上报。

刚接手时数据库还是 MySQL。

其实项目刚上线那几个月没出什么大问题,数据量还没起来,查设备曲线、翻历史记录都正常。后来跑了大半年,问题才一点点冒出来。表越来越大,写入偶尔开始抖,查几个月前的数据明显变慢,磁盘也隔一阵子就报警。

这种项目很容易形成一个惯性思路:

设备数据、高频写入,那就上时序数据库。

我们当时差点也这么干了,甚至已经开始对比几个产品的写入性能。结果方案开会的时候,客户提了一个需求,把前面的思路基本推翻了。

他们不希望把数据拆开。

原因倒不是技术上的,而是业务真的要这么查。

设备上传的温度、电流、压力只是其中一部分。系统里还有设备档案、设备所在区域、告警记录、维修工单。有时候客户要看的不是"某台设备最近一天的温度",而是"某个区域最近出现过异常,而且半年内维修过的设备"。

这就麻烦了。

时序数据扔到 A 库,设备和工单留在 B 库,单独看两个系统都挺漂亮。等业务真要查的时候,Java 代码开始两边取数据、拼 ID、做关联。

这种代码我以前写过,开始几十行,后来基本都会变成一坨。

所以后面试金仓时序组件的时候,我比较在意的反而不是它每秒能写多少,而是它能不能把这部分数据继续放在一套数据库体系里。

超表我刚开始也理解复杂了

第一次看到 Hypertable 这个词,我还专门翻了一阵资料。

后来真正用下来发现,可以先把它理解得简单一点。

应用看到的是一张表,底下不是一整块。

比如还是正常的设备数据:

sql 复制代码
CREATE TABLE sensor_data (
    time TIMESTAMPTZ NOT NULL,
    device_id INTEGER NOT NULL,
    temperature DECIMAL(5,2),
    humidity DECIMAL(5,2),
    status INTEGER
);

如果按照时间对数据进行分块,假设一个块放 7 天,那么 1 月 1 日到 7 日是一块,8 日到 14 日又是下一块。

这些事情主要由数据库处理。

业务代码这边其实没那么多感知。

查询还是类似以前:

sql 复制代码
SELECT time, temperature
FROM sensor_data
WHERE device_id = 10001
  AND time >= '2026-07-01 00:00:00'
  AND time <  '2026-07-02 00:00:00'
ORDER BY time;

以前我们用普通关系型数据库处理这类数据,也不是完全没办法。

按月分表呗。

我之前就干过。

第一年十几张表还好,写几个脚本也能维护。时间一长就开始难受了。历史表什么时候清、分区什么时候建、跨月查询怎么办,后面又碰上客户临时把数据保存时间从一年改成三年......

然后继续改脚本。

这些东西单独拿一个出来都不难,就是特别碎。

所以超表这个功能,我后来觉得省事的地方就在这里。以前需要我们自己操心的一部分分块工作,可以让数据库接过去。

后来我反而不太迷信"每秒写入多少条"

做这个项目之前,我看时序数据库资料,第一眼基本都找写入性能。

100 万条/秒。

200 万条/秒。

数字越大感觉越厉害。

但项目真做下来以后,我发现这个数字只能说明一部分问题。

比如客户后来提过这么一个需求:

找出某园区过去 24 小时出现过温度异常的设备,同时把设备型号、所在位置以及最近一次维修记录带出来。

温度异常这一段属于时序数据。

设备型号不是。

维修记录也不是。

还有一种查询也经常出现:把所有设备在某个时间段的最后一次上报记录找出来。

数据少的时候这种 SQL 没什么感觉,设备数和时间跨度上去以后,就不是简单扫一下时间索引了。

所以选型时只盯写入峰值,很容易把自己带偏。

写进去只是第一步,后面怎么把数据拿出来才是真正天天遇到的事。

我当时专门记了一组测试数字

金仓的相关资料里有 TSBS 测试结果,环境是 96 核 CPU、512GB 内存,其中做了 KES 和 InfluxDB 的一些场景对比。

写入部分我这里就不展开了。

我当时记下来的是两个查询。

一个是取每台设备在指定时间范围里的最后一条读数。

资料中的结果:

复制代码
KES        7.55 ms
InfluxDB   367.45 ms

另一个是阈值筛选:

yaml 复制代码
KES        304.9 ms
InfluxDB   2651.17 ms

第一次看到 7.55 和 367.45 这个差距,我还特意回去确认了一下测试场景。

不过这种数据我一般不会直接拿来下"谁比谁快几十倍"的结论。

Benchmark 这东西太吃环境了。

CPU、内存、数据规模、参数怎么配,甚至具体 SQL 怎么写,都可能把结果拉开。

但它至少让我注意到一个问题:时序场景一旦不只是简单地按时间拉一段数据,而是开始做分组、过滤、取最新值这些操作,查询能力的重要性马上就上来了。

实际业务偏偏很喜欢这些查询。

还有个开始没太在意的问题:磁盘

系统跑起来以后才发现,这个问题甚至比写入更现实。

数据不敢删。

客户的要求大概是这样:

最近几天的数据经常查,肯定留着。

几个月前的数据偶尔也有人翻。

一年前的数据?

问业务,业务通常会说:"先别删,以后可能有用。"

这个"以后可能有用"基本就意味着一直存。

而传感器数据又特别能长,一天看不出来,一个月也还能接受,时间拉到一年、两年,磁盘空间就很明显了。

好在时序数据本身比较适合做压缩。

同一台设备的 device_id 会重复很多次,时间连续递增,一些指标的变化幅度也比较规律。

金仓时序组件可以针对较老的数据配置压缩策略。

项目里完全可以根据业务自己划,比如最近的数据正常保留,超过一定时间以后再压缩,更老的数据另外制定保留规则。

资料里有测试场景做到压缩后空间约为原来的十分之一。

这个数字挺好看,不过真做容量规划时我不会直接按 10:1 算。

还是得拿自己的数据测。

温度、电压这种连续数值是一种情况,大量随机字符串又是另一种情况,最终压缩率不可能完全一样。

这也是做数据库项目几年以后养成的习惯:官方性能数字可以参考,预算表里最好还是写自己的实测数字。

连续聚合倒是解决了一个老问题

我们以前自己写过统计任务。

设备 10 秒钟报一次数据,但大屏要的是小时平均值,报表要的是每天最大值。

最开始很简单。

写个定时任务,每小时:

scss 复制代码
AVG(...)
MAX(...)
MIN(...)

算完扔到统计表。

后来业务说还要 5 分钟粒度。

加一张表。

过了一阵子又要按天。

继续加。

最烦的还不是表多,是有一天任务挂了几个小时。第二天发现报表中间缺了一截数据,还得自己写补偿逻辑把那几个小时重新算回来。

连续聚合处理的其实就是这类事情。

原始数据可以是 10 秒一级,上面做 5 分钟结果,再往上做小时、天级的数据。

看实时曲线的时候查细数据。

看月报就没必要再去碰最底层那几十亿条记录了。

这个功能没有"每秒百万写入"听起来那么唬人,但对开发来说,我觉得反而更有用。

因为少写几个定时任务,后面就少维护几个坑。

最后还是没有拆库

那个项目最后没有把时序数据单独扔出去。

主要原因也很简单:业务关系太多。

设备档案是一块。

实时指标是一块。

位置是一块。

告警和维修工单又是一块。

这些东西最后总会碰到一起。

如果为了追求某一个指标,把它们硬拆成几个系统,前面数据库确实轻松了,后面麻烦基本都会落到应用代码身上。

项目跑了一段时间以后,我最大的感受倒不是"性能提升了多少倍"。

而是我没怎么再碰以前那些按月建表、归档、跨表查询的脚本了。

磁盘策略配好以后也不用隔几天盯一次。

这才是我觉得省时间的地方。

现在再让我选时序数据库,我肯定还是会看写入吞吐,但不会上来就拿 Benchmark 排名。

我一般先翻业务 SQL。

看看他们平时到底查什么。

如果只是设备 ID + 时间范围,事情比较简单。

如果翻着翻着发现一堆设备表、区域表、工单表都要参与进来,那就得重新考虑了。

数据库跑得快当然重要。

但对开发来说,还有一个指标基本没人拿出来跑分:

这套东西上线以后,到底要不要天天有人伺候。

这个项目做完以后,我反而越来越在意这个了。

相关推荐
大黄评测2 小时前
jQuery 遍历方法 each 实战:循环处理列表数据
后端
大勇前进2 小时前
jQuery 事件委托原理:彻底解决动态生成元素绑定失效问题
后端
苍何2 小时前
做了个微信聊天爆款视频 Skill, Codex / WorkBuddy一键复刻使用
后端·aigc
卷无止境2 小时前
用 FastAPI 撑起大文件的上传下载:从流式处理到断点续传的完整实践
后端·python·fastapi
铁皮饭盒2 小时前
网页端, 6.5MB人脸识别模型, 谷歌框架, 又快又准
前端·javascript·后端
长大19882 小时前
jQuery AJAX 完整封装教程:告别重复写请求代码
后端
worxfr3 小时前
Go 并发控制:从 Channel 方向约束到实战模式
开发语言·后端·golang
拾陆楼4 小时前
PT: DMSA辅助调tree报告前后级余量脚本
后端·学习
今天的砖头有点烫手啊4 小时前
接口太慢?Spring Boot 缓存体系 @Cacheable 全链路拆解
spring boot·后端·缓存