先说一个结论:在智慧工厂里堆一套庞大的中心数仓,真正让人头疼的往往不是“存不下”,而是“想查一个数要等半天”。设备点位从几千涨到几十万之后,一条告警链路、一张实时看板、一次分钟级的边缘统计,全被一个慢查询拖垮。我们后来把数仓拆成两层——边缘数仓负责“即时响应”,中心数仓负责“长期洞察”,用一套时序采集与分析协同机制把两边的数据串起来,才算真正跑顺。这篇文章就把这段实践完整拆开讲:为什么拆、边缘层怎么建、中心层怎么建、两层怎么协同,以及我们踩过的那些真坑。
1. 为什么我从“一套大数仓”改成了“边缘+中心”两级架构
1.1 从一次产线事故说起
去年年中我们工厂做了一次设备改造,产线上新增了一批带高速采集的传感器,点位规模从原来的几千个一下冲到接近五十万,采集频率也从 5 秒一档提到了 1 秒一档。当时我还没意识到问题的严重性,觉得“数仓嘛,无非就是扩容”,结果上线的第三天就出事了。
产线操作员反馈,设备状态看板上的数据滞后超过两分钟。后台一查,是边缘网关把数据打到中心 Kafka,中心数仓在凌晨批量计算时积压了当天的明细,结果实时统计跑不出来。更要命的是,现场一名工艺工程师想查某台设备过去一个小时的温度曲线,用来判断当天的异常根因,前端页面直接超时。说白了,不是数据丢了,是“一套中心数仓包打天下”的模式,在时序数据膨胀之后彻底扛不住了。
那次事故之后我做了个简单的容量测算。50 万个点位、1 秒 1 条,一天就是 4320 亿条记录,哪怕每条只占 50 字节,单日原始数据就有 2TB 左右,一年就是七八百 TB。把这种量级的明细数据全部实时灌进中心数仓做查询,存储成本和查询延迟都不可接受。更不合理的是,产线上一半以上的监控逻辑其实只关心最近几分钟到几小时的数据,压根不需要跨天分析。
1.2 时延、成本与数据特征的拉扯
想明白之后,我意识到问题出在“用一套数据系统同时满足两种完全不同的需求”。我把智慧工厂里的数据需求粗分成两类。
一类是“即时响应型”,比如设备突发停机、温度超限、振动特征突变。这类需求对时延极其敏感,理想状态是几百毫秒到秒级出结果,决策逻辑简单直接,主要看单个设备或单个点位的当前状态。另一类是“长期洞察型”,比如月度 OEE 趋势、某批次产品的工艺参数和质量指标关联、跨产线横向对比。这类需求允许分钟级甚至小时级的延迟,但数据维度多、关联复杂,需要把设备数据、工单、质量、能耗等揉在一起分析。
两类需求对数据系统的要求是矛盾的。即时响应型需要数据靠近现场、查询路径短、存储粒度可以粗一点;长期洞察型需要数据集中、schema 宽、历史留存久。硬塞在同一套中心数仓里,结果就是实时任务和批量任务互相抢资源,谁都跑不快。
成本曲线也在逼我们做拆分。中心侧如果要保留全量秒级明细,就得维持高配置的计算和存储集群,但绝大部分历史明细数据实际访问频率极低。比较合理的做法是:边缘侧保留短周期的秒级原始数据,中心侧保留降采样后的聚合数据和较长周期的特征数据,把原始明细压缩到几天或几周的窗口。
1.3 两层数仓的定位切分
最终我们确定的分工是这样的。
边缘数仓定位在厂区或车间内部,承接各类工控协议(Modbus TCP、OPC UA、S7 等)上报的时序数据,负责秒级和分钟级的计算、告警、短时查询。它服务的对象是产线班组长、设备维修人员和现场工艺工程师。中心数仓定位在企业级,负责跨车间、跨班次、跨产线的长周期分析,对接 MES、ERP、QMS 等业务系统,服务对象是生产计划、质量分析、工艺优化和工厂管理层。
数据流向是单向闭环:边缘层采集时序数据后,先落地到本地时序库,一部分直接用于边缘计算,另一部分按策略同步到中心数仓,中心数仓做更重的清洗、关联和建模,再通过数据服务把分析结果反哺到报表平台和 BI。边缘和中心不是主从复制的关系,而是各有独立计算能力又互相配合的两级体系。
2. 边缘数仓怎么实现“即时响应”
2.1 时序数据库选型:它才是边缘的主角
想实现即时响应,核心是把时序数据落到对的存储引擎上。关系型数据库和普通 Hadoop 数仓在这个场景下基本不适用,因为时序数据的写入模式是“以当前时间为轴持续追加”,查询模式则高度集中在“某个设备某个时间段内的数值序列”。
我们当时在 TDengine、InfluxDB 和 IoTDB 之间做对比。InfluxDB 生态成熟、文档多,但单机写入吞吐受磁盘和内存配置影响明显,在几十万点位场景下要精心设计分片;IoTDB 对工业场景支持不错,支持对齐时间序列和复杂路径查询,适合需要精细建模的场景;TDengine 的优势是“超级表”模型和内置的降采样、连续查询功能,对点位这类“设备 + 多测点”的结构非常友好,集群运维也简单。
我们的选择是 TDengine,核心原因是它的存储模型和我们的数据形态匹配。在 TDengine 里,每台设备建一张子表,测点作为子表的列,所有同类型设备归到一张超级表下。写入时按设备维度落盘,查询时只要指明时间范围和设备标签,就能迅速定位到对应分片,不用做全局扫描。
选型时我不建议只盯基准测试的数字,还得结合实际写入模型。我们在现场环境测了五千点位的持续写入和并发查询,重点看两个指标:一是在磁盘 I/O 波动时写入是否积压,二是多条件聚合查询的 P99 时延。实测下来,TDengine 在百路并发写和二十路聚合查询混跑时,P99 能稳定在 300 毫秒以内,符合即时响应的要求。
2.2 点位建模:别把时序点存成关系表
这是很容易翻车的一步。很多团队第一次搭时序存储,下意识地模仿关系库建一张宽表,每行一条记录,字段是“时间戳、设备 ID、测点 A、测点 B、测点 C”,结果数据量大之后存储膨胀得厉害,查询也越来越慢。
我们在边缘侧采用“设备根 + 测点列”的模式:每台物理设备对应一张子表,字段固定为时间戳和需要关注的测点值,设备自身的属性(区域、产线、型号)放在标签里。这样做的直接好处是,同一设备的测点天然存储在一起,按设备查询时不需要大量 join;另一个好处是写入时按设备批量提交,减少网络往返。
点位还要做分级。有些点位的值高频变化,比如振动加速度、电机电流,必须 1 秒级采集;有些点位变化缓慢,比如环境温度、液压油位,5 秒甚至 30 秒采集就够了。我们把点位分成三类,分别配置采集频率和保留周期:
| 点位类型 | 示例 | 采集频率 | 边缘保留周期 | 中心保留策略 |
|---|---|---|---|---|
| 高频波动点 | 振动、电流、压力 | 1 秒 | 7 天原始 | 10 秒降采样,留 12 个月 |
| 中频状态点 | 温度、转速、流量 | 3-5 秒 | 30 天原始 | 1 分钟降采样,留 24 个月 |
| 低频累积点 | 能耗、产量计数 | 30 秒 | 90 天原始 | 5 分钟聚合,长期保留 |
点位分级表在建库之前就要定好,因为后续的聚合任务、上传策略、数据生命周期管理全都依赖这个分级。别想着“先全采下来再说”,点位膨胀带来的成本是成倍增长的。
2.3 聚合、告警与短窗口留存
边缘层不负责长期分析,但必须处理三类任务。
第一类是降采样聚合。TDengine 的连续查询可以直接在边缘库上定义滚动窗口聚合,把 1 秒原始数据压成 10 秒或 1 分钟的均值、最大值、最小值。聚合结果单独建表存储,供看板查询。这样即使原始数据因老化被清理,短期报表能力也不会受影响。
第二类是阈值告警和异常判定。我们最初用简单的规则引擎,每个点位配置上限、下限和变化率,超过阈值就推 Kafka 告警。后来发现单纯的阈值在工况切换时误报太多,于是叠加了滑动窗口统计:取最近 30 个点的均值和标准差,如果当前值偏离均值超过 4 倍标准差,才判定为异常。规则虽然简单,但在边缘侧非常好使,而且计算成本几乎可以忽略。
第三类是短窗口留存,也就是给维修人员留一个“回溯窗口”。设备出故障后,维修人员第一件事就是翻故障前几分钟的原始数据。边缘库只要保留最近 7-30 天的秒级明细,就能覆盖绝大多数故障排查场景,完全不需要去中心库查。
边缘层还有一个容易被忽略的设计:写入协议要精简。我们统一使用 MQTT 上报 JSON 报文,边缘网关做解析后批量写入时序库,每条报文包含网关 ID、设备 ID、点位数据组、采集时间。批量大小控制在 500-1000 条一次,能显著降低写入开销。
3. 中心数仓怎么支撑“长期洞察”
3.1 中心侧的数据分层与存储
中心数仓侧的数据架构,我们采用了“湖仓一体分层”的思路。对象存储保存全量降落数据,数仓引擎负责结构化建模,按 ODS-DWD-DWS-ADS 四层组织。
ODS 层是“原始落地层”,接收边缘侧上传的时序聚合数据和业务系统同步的数据。这里我特别建议把时序聚合文件和业务系统数据分开放在不同的目录前缀下,避免混在一起导致后续处理任务互相阻塞。比如时序数据按“产线 / 设备 / 日期”分区,业务数据按“业务域 / 日期”分区。
DWD 层做清洗和标准化。时序数据的标准化主要是统一时间精度、处理时区偏移、给缺失值打标记。工业现场经常出现设备停机不采集导致的“无数据窗口”,这一层要把这些窗口显式识别出来,不能直接当成平均值参与计算。
DWS 层是核心。这里把时序数据进一步转成业务口径的宽表,比如“设备小时统计表”“产线班次日报表”,字段包括运行时长、平均负载、温度均值、有效产出等。这张宽表会与 MES 里的产量数据做关联,供上层直接查询。
ADS 层面向具体应用,可以是 BI 数据集,也可以是算法特征宽表。这一层不强调实时,而是强调口径统一和查询性能。
3.2 从“时序明细”到“特征化表”
中心数仓和边缘数仓最本质的区别,是中心要把时序数据变成“可用于分析和建模的特征”,而不是继续保留“某年某月某秒某个点位是多少”的原始形态。我们做了一个“特征化加工层”,把上传的聚合时序数据进一步提炼成业务特征。
比如某台注塑机的温度曲线,边缘上传的是每 10 秒的均值。中心建模时,我们会在这个基础上算出:
- 一个班次内的温度均值、方差、最大波动幅度;
- 温度曲线与注塑周期内设定曲线的均方根误差;
- 升温阶段的斜率、降温阶段的斜率;
- 温度在设定范围外的累计时长和占比。
这些特征才会真正被后续的质量分析使用。早期我们尝试直接在 BI 上拖拽原始时序数据做分析,效果很差,因为同一台设备的原始曲线在不同班次之间交错在一起,根本无法直接对比。特征化之后,每个班次变成一行记录,质量人员可以做“班次之间的横向对比”和“参数与不良率的相关分析”。
特征化表的设计建议先跟工艺工程师和数据分析师对齐,不要拍脑袋定义。我们第一版的特征表字段太多,很多特征从来没人用过,后来砍到只保留与质量、能耗、设备健康度直接相关的指标,表结构瘦身了三分之一,查询效率明显提升。
3.3 跨产线、跨班次的横向对比才是中心的优势
边缘层盯的是“这条线这台设备现在有没有异常”,中心层盯的是“这台设备跟同型号的其他设备比,是不是有隐性劣化”。
这里举一个实际案例。我们在某条产线的四台注塑机上装了同型传感器,边缘侧每台都做了独立告警,阈值相同,四台设备看起来都很正常。但中心数仓把四台设备的温度均值按班次做对比,发现 3 号机的平均温度比其他三台高 3 摄氏度,并且差异随时间缓慢扩大。单看边缘告警,3 号机没有任何一次触发阈值;但放到产线整体视角,它就非常显眼。后来停机检查发现是冷却水回路堵塞,如果不做跨设备对比,这个问题可能要被掩盖一两周。
这就是“长期洞察”的价值。中心数仓要做的事,是把单条时序曲线放到设备群、时间段、工艺条件的多维坐标系里去做比较,找到边缘层看不到的“相对异常”。所以中心层的建模不要只盯着设备维度,还要把批次号、工单号、班次、工艺配方版本作为关联键,跟 MES 和 QMS 的数据打通。
4. 两级协同的关键机制:同步、任务分配与统一查询
4.1 数据上行与同步策略
边缘数仓和中心数仓之间是单向的数据上行,但这个“上行”不能做成无脑全量复制。我们设计了分级上传策略。
按点位分级,高频波动点上传播放的是 10 秒降采样结果,中频状态点上传播放的是 1 分钟结果,低频累积点则上传 5 分钟聚合。原始秒级数据只在特殊场景下按需调取,例如质量追溯需要用到完整的信号片段时。
上传时机也要错开。边缘网关会把待上传的数据先落到本地队列文件,按“每小时整点批量上传”的方式发送,避开交接班前后的业务高峰。如果中心侧的接收服务过载,会返回退避指令,边缘侧自动降速,而不是重试风暴。我们的队列采用本地磁盘缓存加断点续传,网络抖动不会丢数据。
还有一个重点是数据版本管理。边缘层的聚合计算偶尔会因为补数据、修正点位漂移而产生更新,如果直接覆盖中心数仓的数据,会导致下游报表产生历史跳变。我们给每次上传的数据附一个“批次版本号”,中心侧按“增量追加 + 最新版本覆盖”的方式处理,分析任务只消费“最终一致”的快照视图。
4.2 计算任务的分工边界
两级协同最容易犯的错,是把边缘当成了“薄采集器”,什么计算都往中心塞。我们花了几周时间把既有的计算任务全部梳理了一遍,按“时间敏感度”和“数据范围”重新分配。
留在边缘层的任务有三个特征:只依赖本设备或本网关的数据;需要秒级到分钟级出结果;需要高频访问原始/近原始数据。典型的包括设备状态实时看板、基于滑动窗口的异常检测、短时故障回放。
放到中心层的任务也有三个特征:依赖多个设备、多个系统的数据;允许分钟级以上的延迟;需要长周期的历史数据。典型的包括 OEE 趋势分析、质量与工艺参数相关性分析、设备群劣化对比、能耗月报。
边界不是黑白的,有些任务在两个方向都可以做。我们的原则是:如果任务的数据源主要落在边缘且结果需要回传现场操作端,就留在边缘;如果任务的数据源跨越多个边缘节点或需要跟业务系统 join,就放中心。宁可让中心多算,也不要在边缘做跨节点聚合,边缘节点之间的网络联调复杂度太高。
4.3 统一查询层屏蔽物理分布
对上层应用来说,它不应该关心“这个查询是打到边缘还是打到中心”。我们开发了一个统一的数据服务 API,把两级数仓封装到同一个查询接口后面。
接口的设计逻辑很简单:请求先带一个“时效性”参数,实时类查询默认路由到边缘层,分析类查询默认路由到中心层;如果边缘层没有该点位的数据(比如已经超过保留期),自动 fallback 到中心层。
为了控制查询成本,我们在接口层强制做了“时间范围限制”和“降采样级别限制”。比如查询范围超过 7 天的时序曲线,服务端会自动把粒度提升到分钟级,避免返回数百万行数据压垮前端图表。这个限制一开始被业务部门吐槽,后来大家发现看月趋势本来也不需要秒级粒度。
统一查询层还承担数据权限的职责。边缘层数据权限按工区隔离,中心层按业务域隔离。运维人员只能查自己负责工区的数据,质检人员只能查跟质量相关的表。这一步如果不在 API 层做死,后期合规审计会很麻烦。
5. 协同落地中踩过的坑与排查复盘
5.1 聚合口径不一致:边缘与中心对不上账
上线后第一周,工艺工程师就发现一个诡异的事:边缘层看板显示某台设备当天温度均值是 81.2 度,中心数仓报表里却是 79.6 度。两边差了 1.6 度,虽然不多,但没人能解释。
排查下来,根因是两层用了不同的聚合算法。边缘层连续查询里用的是“按窗口内非空值个数做平均”,剔除了一些由于网关瞬断产生的空值;中心数仓在清洗时,则把空值窗口先填充成了上一个有效值,再参与平均。两边的“空值处理策略”不同,聚合结果自然对不上。
修复方案是统一空值语义:凡是边缘上传的聚合序列,空值窗口只允许用固定标记位表示,不允许在源头填充;中心侧只有在进入特征化层时才按业务规则决定是否填充,且填充逻辑写入数据血缘文档。从那之后,两边口径终于能对上。
5.2 时钟漂移造成的时序乱序
边缘网关分布在车间不同位置,部分网关的本地时钟会漂移。设备上报的采集时间戳来自传感器或 PLC,而网关的上报时间戳来自系统时钟,两者一旦不一致,时序库里就会出现“时间往回跳”的数据点。
这个问题在单机时几乎不可见,但在多网关并发查询时非常致命。我们有一次做设备劣化分析,发现某台设备的振动均值曲线出现了周期性的“尖刺回退”,数据点的时间戳比前一条还早几百毫秒,导致降采样窗口错位。
解决办法分两层。第一层是边缘网关统一用 NTP 同步,并且在采集链路里约定“传感器时间优先、网关接收时间为辅”,每条记录同时携带采集时刻和接收时刻,入库时按采集时刻排序。第二层是中心侧在 ODS 层增加乱序检测任务,把同设备同点位中时间戳差值超过阈值的记录标记出来,不参与后续聚合,待修正后再重新进入。
5.3 上传任务占满带宽引发产线卡顿
这个坑相当隐蔽。我们把上传任务统一安排在整点批量执行后,某段时间频繁出现产线 HMI 画面卡顿,监控网络发现,整点前后网关上传流量飙升,把车间交换机的上行端口带宽快打满了。
原因是几十个边缘网关在同一时刻并发上传,且上传内容包含了一些未经降采样的明细点。细节很蠢,因为某些点位虽然被标记为“低频”,但我们的上传脚本默认取的是“该点位最近一小时所有原始值”,而不是聚合值。
修复很简单:上传脚本严格按点位分级取数;上传时段改为随机化的“整点后 5-15 分钟”窗口;带宽占用超过阈值时自动降级,先传核心点位聚合数据,非核心点位顺延。后来再也没有出现过类似卡顿。
5.4 中心回溯重算覆盖了边缘修正结果
这一版是最痛的。边缘层有一次因为传感器漂移,运维人员手工修正了一批点位值,修正后的上传数据带了新的版本号。但中心数仓夜里跑了一个周度回溯任务,这个任务读的是旧版本快照,重算后又把修正前的数据写回了 DWD 层,等于把运维白天的修正全部覆盖了。
问题出在“回溯任务没有感知数据版本变更”。我们当时的处理是在数据服务层增加“基于目标点位 + 时间区间 + 版本号”的锁机制,回溯任务启动前必须先检测目标分区的最新版本号,如果与任务计划版本不一致,直接中止并提示人工介入。
这个机制之后,中心侧的回溯重算全部要显式声明“基于哪一版数据”,不允许多个任务对同一分区随意覆盖。数据血缘也因此清晰了很多。
5.5 一些设计与运维层面的经验沉淀
走完这一轮踩坑,我的体会是,边缘中心和中心数仓的协同,本质上是“数据命名的统一”“计算口径的统一”“版本语义的统一”这三个统一的工程化。技术选型反而是相对容易的部分,难的是把这些规则贯彻到每条链路里。
举个例子,点位命名。早期不同产线对同一个测点的命名不一样,A 线叫 “Temp1”,B 线叫 “zone_temp”,导致中心侧关联分析时不得不用手工映射表维护,维护成本居高不下。我们后来统一了全厂的点位编码规则,格式为“产线编码-设备编码-测点类型-序号”,从源头避免命名混乱。这个动作对后续所有分析和运维都有长远好处。
运维层面还有一个容易被低估的项:边缘节点的磁盘水位监控。边缘时序库一旦写满,会导致写入失败甚至进程崩溃,而且这种故障通常发生在半夜。我们加了每 5 分钟扫描一次磁盘水位、超过 70% 自动触发历史数据压缩的策略,同时给核心点位单独划保留空间,避免被低频点位的长期数据挤占。
如果你也要搭这套两级架构,我的建议是:先把点位分级和口径语义定下来,再选时序引擎,最后再谈数据同步和查询接口。顺序反了,后面返工的工程量会非常可观。这套方案我们已经跑了三个多月,边缘层的即时查询 P99 在 500 毫秒以内,中心层常规报表的日活查询没再出现过超时告警,总算从当初那场事故里彻底走出来了。