工业物联网的数据有一个很现实的特点:设备型号有限,设备数量却很多;每台设备的属性结构相近,采集频率又高。比如同一类风机,可能有成百上千台,每台持续上报温度、振动、电流、转速和运行状态。若把这些数据直接按普通业务表存储,表数量、索引和查询成本很快会失控。
在 AllLinks 平台中,工业物模型与 TDengine 的映射,可以理解为把“产品定义的数据结构”落成“超级表”,再把“每一台真实设备”落成“子表”。
产品物模型 → TDengine 超级表 设备实例 → TDengine 子表 物模型属性 → 表字段 设备标识 → 标签或子表标识 属性上报时间 → 时间戳 一次属性上报 → 一条时序记录从工业物模型开始
工业物模型通常定义在产品层。以一类水泵为例,产品物模型可能包含:
| 物模型字段 | 含义 | 数据类型 |
|---|---|---|
| temperature | 电机温度 | Double |
| pressure | 管路压力 | Double |
| current | 工作电流 | Double |
| running | 运行状态 | Boolean |
| faultCode | 故障码 | String |
这些字段并不属于某一台具体水泵,而是“水泵产品”共有的能力描述。因此,最自然的映射方式是:每个产品对应一张 TDengine 超级表。
例如,产品water_pump_v1可以映射为超级表ts_water_pump_v1。超级表中保存时间戳和物模型属性字段;设备 ID 作为标签或子表的区分依据。
CREATE STABLE ts_water_pump_v1 ( ts TIMESTAMP, temperature DOUBLE, pressure DOUBLE, current DOUBLE, running BOOL, fault_code NCHAR(64) ) TAGS ( device_id NCHAR(64) );这里的字段来自物模型,字段类型也应由物模型的数据类型决定。温度、压力等数值属性不应统一转成字符串,否则后续的范围筛选、聚合计算和趋势图查询都会变得麻烦。
设备实例映射为子表
假设平台中存在两台水泵:
pump_001pump_002
它们使用同一个产品物模型,因此共享同一张超级表,但分别对应各自的子表。
CREATE TABLE ts_water_pump_v1_pump_001 USING ts_water_pump_v1 TAGS ('pump_001'); CREATE TABLE ts_water_pump_v1_pump_002 USING ts_water_pump_v1 TAGS ('pump_002');这样设计的好处很直接:产品维度可以查询同类设备整体运行情况,设备维度又可以快速查询单台设备的历史数据。
例如,查询一台设备过去一天的温度变化:
SELECT ts, temperature FROM ts_water_pump_v1_pump_001 WHERE ts >= NOW - 1d ORDER BY ts DESC;查询同类全部设备在某个时间段内的压力数据,则可以直接查询超级表:
SELECT ts, device_id, pressure FROM ts_water_pump_v1 WHERE ts >= NOW - 1h;一次上报,对应一条记录
平台当前的 TDengine 列式存储策略采用“设备一次完整属性上报写入一行”的方式。也就是说,设备在某一时刻上报多个属性时,不会把温度、电流、压力拆成多条记录,而是组织为一条带时间戳的数据。
{ "deviceId": "pump_001", "timestamp": 1757059200000, "properties": { "temperature": 68.5, "pressure": 1.2, "current": 8.6, "running": true } }写入 TDengine 后,大致对应:
ts temperature pressure current running 2026-09-05 10:00 68.5 1.2 8.6 true这种结构特别适合设备按固定周期上报完整状态的工业场景。查询一段时间内的运行趋势时,一次读取就能得到多个相关属性,前端也更容易绘制趋势图。
不过,这种设计有一个前提:设备最好能够一次上报主要属性。若设备只零散上报单个属性,或者属性变化非常频繁、模型变化很快,宽表会出现较多空值,建模方式就需要重新评估。
消息、物模型与时序库之间的转换
在平台内部,设备数据不会直接以原始 MQTT 报文写入 TDengine,而是先经过统一设备消息处理。
MQTT 原始报文 ↓ 协议解析与设备认证 ↓ 统一 DeviceMessage ↓ 匹配产品物模型、校验属性类型 ↓ 生成时序 Point ↓ 写入“产品超级表 + 设备子表”这个过程的关键是物模型承担了“翻译器”的角色:
- 它告诉平台属性名称是什么;
- 它定义属性的数据类型;
- 它帮助平台将原始值转换为适合存储和计算的数据;
- 它决定 TDengine 中哪些字段需要创建或更新。
因此,TDengine 表结构不应由设备报文临时决定,而应以产品物模型为准。否则,同一个属性可能被不同设备上报为不同名称或不同类型,后续的统计与查询会失去统一标准。
属性、事件和日志应分开建模
工业设备除了周期性属性,还会产生告警事件、故障事件和操作日志。这几类数据的结构和查询方式不同,不建议全部塞进同一张属性超级表。
较合理的方式是:
| 数据类型 | 推荐映射 |
|---|---|
| 高频设备属性 | 产品属性超级表 |
| 告警、故障、状态变化 | 事件超级表或事件时序指标 |
| 平台操作、设备指令记录 | 日志存储或独立日志指标 |
| 当前状态 | 缓存或设备最新消息记录 |
| 设备、产品、物模型配置 | 关系型数据库 |
这也是工业 IoT 平台常见的“冷热分层”思路:最新状态用于实时监控,历史属性进入 TDengine,设备档案和配置留在关系型数据库。
映射设计中最容易忽略的问题
我更倾向于把“产品”作为超级表边界,而不是为每台设备单独设计一张普通表。前者让物模型和存储结构保持一致,也便于横向比较同类设备。
但有三个约束必须提前确认:
- 物模型属性变更会影响 TDengine 表结构,新增、删除或改类型都需要有明确的升级策略。
- 设备采集时间和平台接收时间要区分。当前数据通常以设备消息时间戳作为时序记录时间,设备时钟不准时需要额外校时。
- 不要把所有字段都设计成标签。标签适合设备编号、区域、型号等筛选条件;温度、电流等持续变化的数据应当是普通列。
结语
TDengine 超级表与工业物模型的映射,本质上是把产品标准转化为时序数据标准:产品定义超级表,设备实例定义子表,物模型属性定义字段,设备上报时间定义时序索引。
这种映射让平台既能按单台设备查看历史趋势,也能按产品、区域或设备群进行横向分析。对工业 IoT 来说,真正重要的不是“把数据写进数据库”,而是让设备模型、消息模型和存储模型保持同一套语义。模型统一了,后面的监控、告警、报表和运维分析才有可靠基础。
已按你提供的 Sepia 技能的技术文章路径完成写作;该技能也已安装,可在后续继续用于润色或重写。
纵横工业互联网团队是河南863一支专注于工业数字化的团队,是深耕工业数字化转型领域的专业技术与解决方案服务商,聚焦工业企业智能化升级核心需求,打造了全栈式、可落地的工业互联网产品与服务体系。我们构建了自主可控的五大核心产品体系,涵盖面向产业集聚区 / 工业园区的产业集聚区工业互联网管理平台,以及面向工业企业全生产流程的物联网平台、能耗能碳管理平台、设备管理系统、MES 生产制造执行系统。
我们团队累计接入工业设备 10 万余台,覆盖 100 余类设备类型,适配 840 余种工业协议,深耕烟草、高端装备、汽车零部件、新能源电力、煤炭能源、家电制造等数十个核心工业领域,服务 50 余家行业头部企业与产业园区主体,沉淀了海量的项目落地经验与行业 Know-How,具备全链路数据采集、计算、应用与数字化运营能力,可为产业园区、工业企业提供一站式工业互联网解决方案,助力园区产业升级、治理提效,助力企业降本增效、精益管理、绿色转型。