工业物联网项目做到一定阶段,最先让人头疼的往往不是协议选型,而是数据怎么保证不丢、不乱。很多设备通过边缘网关接入平台,链路一层一层很长,车间网络抖动、运营商专线故障、网关掉电、平台发布重启,任何一个环节出问题,都会让数据流出现缺口或者重复。

我见过一个实际场景:工厂里几十台 PLC 通过网关采集数据,网络不稳定,网关内存里攒着几十万条消息准备上传,结果链路恢复后一股脑全发出去,平台处理不过来,数据库压力飙升,而且因为重试带了重复数据,统计报表直接翻倍。很多团队在排查时会发现,“数据没丢”和“数据正确”之间还有很大距离。
这篇文章想讨论的是工业物联网中的数据完整性保障,不写太虚的东西,重点看看断点续传、缓存和幂等设计这三个环节,它们分别要解决什么问题,以及实际工程里怎么落地。
先分清楚:不丢、不重、不乱是三个独立问题
数据完整性听起来是一个目标,但在工业物联网语境下,至少可以拆成三个层面:数据不丢、数据不重、数据不乱。“不丢”关注的是链路中断后数据能否补齐,“不重”关注的是重复上报不会破坏业务结果,“不乱”关注的是数据的顺序和因果关系不能被破坏。
很多方案试图用一个机制同时解决三个问题,比如在网关侧只做“重传”,以为把历史数据全部再发一遍就安全了,结果平台侧收到重复数据后,库存、能耗、产量全部算错。反过来,只做服务端去重,又发现网关重启后丢了一段数据,因为本地缓存没有持久化。
所以先把问题拆开,会清晰很多。
- 不丢:依赖断点续传和持久化缓存,确保数据从设备到平台的每个环节都留有备份。
- 不重:依赖幂等设计,平台端根据唯一业务键识别重复消息。
- 不乱:依赖时序记录和补偿机制,至少在业务侧能识别乱序并按时间窗口处理。
断点续传:关键不是“重发”,而是知道从哪里继续
断点续传字面上看是“中断后接着传”,但很多网关实现只是简单地把未发送的数据重新放回队列。这样做的问题是,如果网关在传输过程中崩了,内存里的队列也会跟着消失,重启后依然从最早的数据开始重发,或者干脆把当前一批数据丢弃。
真正的断点续传需要把“进度位点”持久化下来。每次成功发送一批数据后,记录下一个待发送的偏移量,下次恢复时从这个偏移量继续读取。这个偏移量可以是文件中的字节位置,也可以是 SQLite 表里的自增 ID。只要位点没有丢,数据就不会丢。
# 边缘网关断点续传伪代码
last_acked_offset = load_offset_from_local_db()
while True:
batch = read_batch_from_storage(last_acked_offset, batch_size=500)
if batch is empty:
sleep(10)
continue
try:
result = send_to_platform(batch)
if result.ok:
# 注意:先确认平台落库,再推进位点
last_acked_offset += len(batch)
save_offset(last_acked_offset)
else:
sleep(30)
except NetworkError:
sleep(30)
# 如果需要限速,可以在这里控制重试频率
这段伪代码的核心在于:只有平台明确返回成功,才推进本地位点;网络异常时不推进,数据始终留在本地存储中。很多团队觉得“断点续传”就是加一个重试循环,但如果没有把进度落盘,重试几十次之后网关重启,一切还是要从零开始。
在真实项目里,需要特别注意“发送成功”的语义。如果网关把数据发到平台接入层,但接入层还没来得及写入数据库就宕机了,网关收到的成功应答就不可靠。所以要么让平台真正落库后再返回成功,要么在应答里带上消息 ID,网关记录已确认的 ID,即使接入层重启,也能避免虚假成功造成的丢数。
另一个坑是积压数据过多时的“追赶风暴”。网关离线几个小时后,本地可能积压几万条采集记录,一恢复就连发,很容易把平台打挂。断点续传需要配合限速和分页,每次只发 500 条,窗口大小和数据量都可以手动控制。
缓存不是简单存起来,要设计好降级路径
刚才说的断点续传依赖本地存储,但本地存储本身也需要设计。在工业网关里,最常见的是三级缓存:内存队列、磁盘文件、外部数据库。内存队列负责实时转发,磁盘文件负责断网时暂存,外部数据库用于对账和补偿。
很多小型网关只有内存缓存,网络一抖数据就丢。有的网关加了磁盘缓存,但没有容量上限,离线时间一长,存储卡写满,后面所有数据都进不来。真正可用的缓存方案,至少要明确三个问题:缓存能放多少数据、满了以后怎么办、如何确认数据已经安全到达平台。
例如有些采集场景对实时性要求高,缓存策略会倾向于“新数据优先”,旧数据可以直接丢弃。但在能耗监测这类对完整性要求高的场景,旧数据是核心资产,必须想办法保住。这个选择没有统一答案,只能说业务越依赖数据完整性,缓存水位和淘汰策略就越要保守。
| 缓存方案 | 适用规模 | 优势 | 风险 |
|---|---|---|---|
| 内存队列 | 单网关早期接入 | 吞吐高、延迟低 | 断电即丢,无法支撑长时离线 |
| 磁盘文件 | 中小规模边缘网关 | 持久化,成本低 | 需要管理批次和位点,恢复逻辑复杂 |
| 外部数据库 | 大型平台侧补偿 | 易查询、易对账 | 依赖网络,数据库本身也可能故障 |
如果你的网关计算资源有限,推荐把消息先写入本地 SQLite 或者 LevelDB,再通过后台任务读取并上传。这样既能保证数据落盘,又不会像文件系统那样难以管理碎片。要注意的是,写入和读取不能同时争用同一个文件句柄,否则会出现锁等待,进而影响采集线程。
幂等设计:平台端最后一道防线
断点续传和缓存解决了不丢的问题,但要知道,工业链路上几乎没有“只发一次”的保证。网关重发、网络层 TCP 重传、消息队列 at least once 语义,都会让平台收到重复消息。为了防止重复数据污染业务统计,平台端必须做幂等设计。
幂等的本质是让同一个业务请求无论到达多少次,产生的效果都一样。最简单有效的方法,是在数据上报接口中增加唯一业务键,这个键不能是自增 ID,也不能是平台生成的 ID,而必须来自设备端业务上下文,例如“设备 ID + 采集时间戳 + 通道序号”。
-- 平台端基于唯一业务键去重
CREATE TABLE raw_data (
unique_key VARCHAR(64) PRIMARY KEY,
device_id VARCHAR(32) NOT NULL,
timestamp BIGINT NOT NULL,
value DOUBLE NOT NULL,
created_at BIGINT
);
-- 幂等写入:如果唯一键已存在,不再重复插入
INSERT INTO raw_data (unique_key, device_id, timestamp, value, created_at)
VALUES (?, ?, ?, ?, ?)
ON CONFLICT(unique_key) DO NOTHING;
这里用的是 PostgreSQL/SQLite 的 upsert 语义。对 MySQL 来说,可以用 INSERT IGNORE 或者先查再插,但要注意并发下可能产生重复插入,所以最优做法还是给唯一键加唯一索引,利用数据库约束兜底。
做幂等时有个容易被忽略的问题:仅仅用采集时间戳做唯一键并不安全。工业现场经常出现多台设备同时采集,或者设备较长时间校时,不同设备的时间戳可能一样。不要把设备 ID 去掉,否则会丢掉正常数据。
另一个误区是“网关已经去重了,平台不用做”。网关去重只能解决网关自身的重试,不能解决平台接入层重试、消息队列重复消费,以及多个网关同时上报同一份数据的问题。所以无论网关侧做得多完美,平台侧的幂等都不能省。
从工程角度怎么落地
到这里,三块核心机制都讲清楚了。但在真实项目中,一次性把断点续传、缓存、幂等全部做好并不现实,建议按以下顺序演进。
- 先明确业务对数据完整性的要求。关键指标是“允许丢多少”“丢多久”以及“重复数据会造成什么后果”。不丢不重不是默认需求,是需要和业务确认的。
- 在端侧实现持久化缓存和位点记录。优先保证设备离线时数据不丢,这是最接近数据源的地方。
- 在平台侧为上报告增加唯一业务键,并建立去重机制。这一步成本最低,但收益很大。
- 增加连续性监控。用平台收到的数据序号或时间戳间隔判断是否存在缺口,出现缺口及时告警,再结合补传机制处理。
以我接触过的系统为例,很多团队会把顺序反过来,先做平台可视化,再去补数据质量,最后发现端侧没有留历史数据,平台再努力也补不回来。数据完整性这件事,越靠近数据源头,决策越重要。
如果网关资源受限,可以用最简单的方案:采集数据追加到本地文件,每个文件按时间分片,起一个后台上传任务,上传完成后记录文件偏移。只要文件不被清理,数据就有恢复机会。然后随着接入数据量和场景复杂度上升,再演进到 SQLite 和唯一键幂等。
还有一点值得提醒:断点续传的位点记录和缓存清理要放在一起考虑。平台确认收到数据后,本地存储记录才能删除。如果先删除再发送,一旦发送失败数据就丢了。很多丢数问题,不是没有缓存,而是清理时机不对。
工程上可以这样理解:端侧负责按位点持久化,平台侧负责按业务键去重,两端配合才能构成完整的数据完整性保障。
最后说一点
工业物联网的数据完整性不是一个单一功能,而是一组分布在端、边、云上的协同设计。断点续传解决的是传输链路中断后的数据补齐,缓存解决的是端侧容量和突发流量之间的矛盾,幂等解决的是重复消息对业务结果的影响。三者范围不同,但目标一致:让平台最终看到的每一份数据,都尽量接近现场真实发生的情况。
实际工程项目里没有标准答案,但我倾向于先把“不丢”放在第一位,再解决“不重”和“不乱”。因为数据丢了无法凭空恢复,而重复数据只要幂等设计做得好,是可以被消除的。希望这篇文章能帮你在设计工业物联网架构时,先把这三块地基打好。
原创文章,作者:fudengji,如若转载,请注明出处:https://fudengji.cn/article/842/