从关系库硬扛到专业时序引擎:SagooIoT时序数据存储的架构取舍
引言
先算一笔账。
假设现场有 1 万台设备,每台设备每 5 秒上报一次数据,一次上报 20 个测点。那么每秒要落库的数据点就是 4 万个,一天是 34.5 亿条,一个月轻轻松松破百亿。这还只是一个中型的工业现场,没有算上告警事件、操作日志、边缘网关的汇聚数据。
这个量级下,MySQL 这类关系型数据库不是"慢不慢"的问题,而是"能不能扛住"的问题。写入的时候,索引要维护,事务要提交,行级锁要竞争;查询的时候,一张百亿行的表,随便一个时间范围过滤都能让 DBA 冒冷汗。更要命的是,时序数据天生"只增不改、只删过期",关系库那一套事务、回滚、外键约束在这里几乎全是负担。
我们接手过一个能源监测项目,前期图省事,把设备上报直接写进了 MySQL。设备量一过 3 万,历史数据页就开始转圈,单次查询动辄几十秒,数据库服务器 CPU 常年打满,夜里还得靠定时任务手动搬历史表,运维被折磨得够呛。后来切到专业时序库,同样一台服务器,查询从几十秒降到亚秒级,存储空间还省了大半。
这件事让我真正意识到:在物联网平台里,时序数据的存储不是一个"用哪个数据库"的小选择,而是整个数据底座的地基。 地基打错了,上面的规则引擎、大屏、告警、数据分析全都跟着晃。
这篇文章,我就把 SagooIoT 在时序数据存储上的架构取舍讲清楚:为什么分层、怎么选型、写入和查询分别做了哪些工程化处理,以及踩过的坑。
一、时序数据到底特殊在哪
要讲清楚存储方案,得先看明白时序数据本身的脾气。它和普通业务数据有三点本质区别。
第一,写入密集、追加为主。 设备数据是持续、高频地往里灌的,几乎不存在"改一条旧记录"的操作。写入模式是典型的 append-only,而不是 update。
第二,查询有明显的模式特征。 大多数查询是"某几台设备在某一段时间内、某几个测点的值",也就是按 设备 + 时间范围 + 测点 三个维度切片。这种查询最怕全表扫描,最需要的是时间维度的分区和测点维度的索引。
第三,数据有生命周期。 半年前的一秒一条的原始数据,业务上基本没人再看;但"按月聚合的日均值"可能要保留十年。这就要求存储层能自动做降采样和过期清理,而不是无脑堆积。
把这三点摊开来看,关系型数据库的强项------事务一致性、复杂 join、任意条件的灵活查询------在时序场景里基本用不上;而它的短板------写入吞吐、时间范围查询、海量数据下的存储成本------恰好全踩在痛点上。这就是为什么时序数据要用专门的引擎。
二、分层存储:各干各的,别都塞进一个库
SagooIoT 没有走"一个数据库吃天下"的路子,而是按数据用途做了明确分层:
| 存储组件 | 承担角色 | 存什么 |
|---|---|---|
| MySQL / PostgreSQL | 关系型业务库 | 产品、设备、物模型、用户、角色、规则、告警配置等元数据 |
| TDengine / InfluxDB | 专业时序库 | 设备上报的遥测数据、历史数据、聚合指标 |
| Redis | 缓存与队列 | 设备在线状态、实时快照、会话、任务队列 |
| MinIO | 对象存储 | 固件包、文件、图片等非结构化数据 |
| MQTT Broker | 消息中间件 | 设备上下行报文的收发 |
这五层各司其职,边界清晰。业务元数据走关系库,因为它是典型的增删改查,需要事务和约束;遥测数据走时序库,因为它是高频追加、按时间切片查询;在线状态走 Redis,因为要的是极低延迟的读写。
这里有个很容易踩的坑:把设备实时状态和时序历史混在一个地方。 设备"当前在线、当前值"这类数据,业务上要求毫秒级返回,而且每台设备只要"最新一条",适合放 Redis;而"过去 24 小时每分钟的平均值"这类数据,才该进时序库。二者如果都往时序库里塞,不但查询慢,还会白白撑大存储。
分层之后,系统对存储的依赖就松散了。时序库挂了,不影响设备管理和权限模块;关系库升级,不打断数据采集。这种解耦,是平台能长期稳定演进的前提。
三、时序库的选型:TDengine 与 InfluxDB 两条路
SagooIoT 深度集成了两款主流时序库------TDengine 3.0+ 和 InfluxDB 2.x,部署时可以二选一,也可以按场景搭配。选型上我们主要看四个维度。
写入性能。 TDengine 走的是"超级表 + 子表"的建模方式,一张超级表对应一类设备,每台设备是一个子表,写入时批量落盘,官方宣称单机可支撑百万级数据点秒级写入。InfluxDB 2.x 走的是 measurement + tag + field 的模型,写入路径也很成熟。两者都远超关系库在同类负载下的表现。
存储成本。 TDengine 的列式存储和压缩能力比较突出,同样的原始数据,占用的磁盘通常比关系库低一个数量级;InfluxDB 也有压缩,但相对温和。
查询能力。 两者都原生支持时间窗口、降采样、聚合函数(avg、max、min、last、first、percentile 等)。TDengine 额外提供了类似 SQL 的查询语法和窗口查询,对习惯 SQL 的团队更友好。
运维复杂度。 InfluxDB 生态成熟、社区大,但分布式集群能力在开源版本里有所收窄;TDengine 分布式集群能力完整,但学习曲线略陡。
我们的经验是:中小规模、团队熟悉 SQL 的,优先 TDengine;已经有一套 InfluxDB 运维经验、或者看重其生态的,用 InfluxDB。 关键不是哪家更好,而是别把时序数据留在关系库里。
四、写入链路:怎么把每秒 4 万条稳稳接住
设备报文从网线进来,到变成一条可查询的时序数据,中间要走一段链路。SagooIoT 在这段链路上做了几处关键处理。
第一步,协议接入与解析。 设备通过 MQTT、TCP、UDP、HTTP 等协议接入,报文在协议接入层完成解包,还原成结构化的数据点。这一层是可插拔的,工业协议(Modbus、OPC UA、IEC104 等)通过插件接入。
第二步,数据校验与标准化。 解析出来的原始值,要经过物模型映射,把"寄存器地址 40001 的值"翻译成"电机 A 相电流",单位、精度、量程在这里统一。不合格的数据在这一步就被拦下,不往下游污染存储。
第三步,批量写入。 这是最关键的一步。逐条 insert 在高频场景下会触发大量的网络往返和落盘开销,所以 SagooIoT 把短时间窗内的数据点攒成一批,按批次批量写入时序库。批处理能把写入吞吐提升一到两个数量级。
第四步,异步解耦。 上报接收和落库之间用队列做缓冲,削峰填谷。设备量瞬时暴涨、或者时序库短暂抖动时,数据先堆在队列里,等下游恢复后再慢慢消化,避免把数据库打挂。
这套链路的价值在于:它把"设备什么时候发"和"数据库什么时候写"解耦开了。 设备端不用等落库确认,平台也不用因为一次写入抖动就丢数据。
五、查询优化:让历史数据别再转圈
存储只是前半场,查询才是用户天天能感受到的那一半。大屏、历史曲线、报表,每一个都在考验查询链路。
分区与预聚合是两把最趁手的刀。 时序库按时间自动分区,查询"最近一小时"时,只会命中最近的分区,而不是全表扫描。在此基础上,SagooIoT 利用时序库的降采样能力做预聚合:原始数据按分钟、小时、天逐级聚合,查询"过去一年每天的峰值"时直接读预聚合表,而不是对百亿条原始数据现场算一遍。
缓存兜底热数据。 最近一段时间的实时值放在 Redis,前端大屏刷新时优先读缓存,命中不上的才回源时序库。这样最常见的"看实时"场景几乎不碰时序库,把宝贵的查询资源留给真正的历史分析。
窗口查询与流式计算。 对于"过去 5 分钟的平均温度超过阈值就告警"这类需求,用窗口函数在写入侧做增量计算,而不是等查询时现算。规则引擎和告警判断因此可以在数据落库的同时完成,延迟压到秒级以内。
这些手段叠加起来,效果是立竿见影的。前面那个能源项目,切换之后历史曲线页从几十秒缩到亚秒,报表导出从"等一杯咖啡"变成"点一下就好"。
六、一个真实的改造案例
拿上面那个能源监测项目来说,改造前后的对比很能说明问题。
改造前,所有数据写 MySQL,设备量 3 万台时:历史数据单次查询 40 秒起步,日报表导出要跑十几分钟,存储 3 个月膨胀到几百 GB,运维每天手动搬历史表、清过期数据。
改造后,遥测数据迁到 TDengine,元数据留在 MySQL,实时值放 Redis。同一台服务器:历史查询压到 1 秒以内,日报表秒出,存储占用降到原来的十分之一左右,降采样和保留策略自动跑,运维从"天天熬夜搬表"变成"月底瞄一眼磁盘水位"。
这个案例没有换什么昂贵硬件,改的就是存储架构。数据没变,设备没变,只是让每类数据住进了它该住的房子。
七、踩过的坑
时序存储这条路,我们也交过学费。三个坑值得记下来。
坑一:把时序库当关系库用。 有人习惯性地给测点建一堆索引、写一堆关联查询,结果时序库性能不升反降。时序库有自己的脾气,得顺着它的数据模型来,而不是套关系库的思维。
坑二:忽略乱序数据。 设备断网重连后,会补传一段历史数据,时间戳是乱的。如果写入侧不做乱序处理,直接落库,查询结果可能对不上。要在写入前做时间戳排序或交给时序库的乱序处理机制。
坑三:降采样和保留策略拍脑袋。 降采样粒度定得太细,预聚合表本身就很大;定得太粗,查历史趋势又不够细。保留策略定得太短,合规审计要数据时没了;定得太长,存储成本白白浪费。这两者必须结合业务真实需求来定,而且要能随业务调整。
八、对比一下竞品
把 SagooIoT 和几款主流开源物联网平台的存储层摆在一起看,差异很清楚。
| 平台 | 时序存储方案 | 特点 |
|---|---|---|
| ThingsBoard | 默认 PostgreSQL,可选 Cassandra/Timescale | 关系库起步,规模大了要换,迁移成本高 |
| EdgeX Foundry | 无内置时序库,依赖外部服务 | 框架灵活,但存储要自己拼 |
| Mainflux | PostgreSQL + 可选 InfluxDB/Timescale | 可选时序库,但集成深度一般 |
| SagooIoT | 原生深度集成 TDengine / InfluxDB,分层存储 | 开箱即用,写入与查询针对时序优化 |
SagooIoT 的差异在于,它把时序存储当成一等公民来设计,而不是一个"可选的后端插件"。分层架构加上对 TDengine、InfluxDB 的深度集成,让它在海量设备数据的写入和查询上,比那些"关系库起步"的平台更有底气。
九、下一步的方向
时序存储的演进还远没到头,几个方向我们正在跟进。
冷热分层。 热数据进内存/SSD,温数据进常规时序库,冷数据归档到对象存储。让"每一份数据都待在成本最合适的地方"。
边缘预聚合。 把聚合和降采样下沉到边缘网关,边缘只把结果往上送,进一步压缩上行的数据量。
流式一体化。 让写入、窗口计算、告警、落库在同一条流里完成,减少中间环节,把端到端延迟再往下压。
数据血缘。 原始数据经过多少次聚合、转换,最终喂给了哪个报表,全程可追溯,让数据治理有据可依。
总结
时序数据存储,是物联网平台最容易被人忽视、又最能拉开差距的一块地基。它不显眼,因为用户看不到它;但它决定了一切------大屏刷得快不快、历史查得顺不顺、存储成本压不压得住,全看它。
SagooIoT 的选择很朴素:让业务数据走关系库,让遥测数据走时序库,让实时状态走缓存,让每类数据住进自己该住的房子。 分层不炫技,但它是平台扛住海量设备、长期稳定运行的前提。
如果你也在搭物联网平台,别急着写业务逻辑,先把数据存储的架构想明白。地基对了,后面都是加法;地基错了,后面全是返工。
(本文涉及的 SagooIoT 能力,均可在官方文档与开源仓库中查证:文档 iotdoc.sagoo.cn,源码 github.com/sagoo-cloud。)