半夜两点,车间里的机器还在轰鸣,产线不会因为天黑而停下来。我坐在中控室里盯着那块大屏,上面滚动着每一个工位的实时产量、设备状态、温控曲线和报警记录。大屏不卡,报表秒开,外面客户来参观时看到的都是这样一派精密的景象。但真正经历过智能工厂落地的人心里都清楚,这些光鲜画面的背后,不是那个酷炫的可视化界面,而是机房角落里几台不怎么起眼的服务器,一台跑着Linux,一台扛着数据库。
你拆开任何一家制造企业的数字化转型项目,底层基本都能看到同一个组合:Linux负责“跑”,数据库负责“扛”。Linux跑的是采集服务、协议解析、消息转发、应用容器;数据库扛的是设备点位、工艺参数、产量记录、质量追溯——每天成千上万甚至上亿条的写入。这两样东西只要有一个不老实,大屏立刻卡死,MES报表马上打不开,线长一个电话就打到信息科。
这篇文章我想从一线实施的角度聊聊:智能工厂里的Linux到底在哪些地方跑,数据库又到底扛了哪些活,以及当我们把一个车间几十台设备的数据真正接进来之后,会遇到哪些避不开的坑。适合刚进入智能制造领域、正在做设备数据采集或MES/SCADA实施的朋友,也适合那些已经在做工业IT运维、想系统梳理一下整套架构的老手。
1. Linux 在智能工厂里到底在跑什么
1.1 从一台边缘网关说起
先别把Linux想得太宏大。在工厂里,它最常见的存在形式不是你办公室那台Ubuntu开发机,而是产线旁边配电柜里一个巴掌大的盒子——工业边缘网关。我做过不少项目,开局都是同一个画面:客户说“我们设备数据都在触摸屏后面,你们想办法弄出来”。你打开电气柜一看,里面要么是PLC,要么是数控系统,旁边已经装好了一个第三方网关,或者需要我们自己在工控机上部署采集程序。
这些边缘节点,十有八九跑的是Linux。原因非常现实:设备厂商不希望你在Windows上随便装东西,而一个定制的Linux系统镜像可以做得非常精简,裁剪到只有几百兆,把串口驱动、网口驱动、采集服务、看门狗全部固化进去,插上电就能跑,断电重启也不会进不了系统。
我经手过一条汽车零部件产线,用的是国产工控机加Linux系统,承担着12台机床的实时数据采集。这些机床控制器有开放的以太网接口,我们通过Modbus TCP和OPC UA两种协议去读点位。网关每500毫秒扫描一次,把数据打上时间戳,先写进本地SQLite做缓存,再批量上传到中心数据库。这套系统上线后连续运行了将近两年,只有一次因为车间断电整线重启,Linux起来之后所有服务自动恢复,基本不需要人跑到现场处理。
1.2 Linux 同时跑在服务器层
边缘层之外,Linux在智能工厂服务器层的存在感更强。MES(制造执行系统)、SCADA(数据采集与监控)、WMS(仓储管理)、EMS(能源管理),这些系统的应用服务器,尤其是数据库服务器,绝大多数都跑在Linux上,常见的是CentOS、Ubuntu Server,偶尔也有国产的Linux发行版。
为什么工业场景对Linux这么执着?我自己的体会有三点。
第一是稳定。工厂里的系统不是按“工作日”算的,是7×24小时连轴转。Windows更新重启一次就可能让产线数据断几个小时,Linux没有这种强制更新机制,只要你不主动敲reboot,它可以一直跑下去。我见过一台数据库服务器连续运行超过1000天,风扇上全是灰,但服务稳如老狗。
第二是资源占用。一个内核精简的Linux系统,512MB内存都能把采集服务跑起来,而Windows光是系统本身就能吃掉好几个GB的物理内存。在边缘算力紧张的环境下,这个差距直接决定了你能不能在一台老旧的工控机上同时跑多个采集程序。
第三是生态。工业通信协议最常用的Modbus库、OPC UA SDK、消息队列客户端、数据库驱动,基本都是优先提供Linux版本。包括像TDengine这类时序数据库,官方从底层到客户端工具都对Linux支持做得最好。做工业数据的人如果离开Linux,很多工具链根本搭不起来。
1.3 Linux 调优不能只装完系统就不管
很多人以为Linux系统装好了、服务起来了就算完事,但在工业现场这是远远不够的,必须针对长时间运行做调优。我每次部署采集服务器,都会顺手做几件固定的事。
关闭不需要的图形界面和服务,能省多少内存就省多少;修改文件描述符限制,把ulimit -n调到65535以上,否则采集进程建立的TCP连接一多,就会出现“Too many open files”报错;把内核参数vm.swappiness调低到10左右,让系统优先使用物理内存,减少不必要的交换分区读写;最后强制配置NTP时间同步,这一步我后面会专门说,因为工业现场很多数据错乱,根源就是设备间时间不一致。
这些操作看着不起眼,但每一个都能避免日后一次大故障。我在一个项目里就遇到过采集进程连接数到几千之后文件句柄耗尽,服务直接挂掉的状况,当时排查了很久,最后发现是默认的1024限制根本不够用。
2. 数据库扛的是什么活儿
2.1 工业数据其实分成三类
聊完Linux,再说说数据库。很多刚入行的朋友一听说智能工厂,就以为把所有数据都塞进一个MySQL就行,这是最常见的误解。车间里的数据五花八门,但按性质分,实际上可以归成三类:
第一类是业务数据,包括工单、物料、BOM表、工艺路线、人员权限、设备台账,这些数据量不大,一天可能就几千条,但要求强事务性,不能出错,适合用MySQL这类关系型数据库来管。
第二类是时序数据,也就是设备产生的各种点位数据:主轴温度、当前转速、气压、产量计数、能耗功率。这种数据是源源不断产生的,一个采集点一天就能产生几十万条,一条产线几十台设备,一天下来几千万条很正常。这类数据最典型的特点是有时间戳、按时间写入、几乎不做修改,用传统关系型数据库存,性能很快就会吃紧。
第三类是本地缓存数据,比如边缘网关断网时,先要把采集的数据存在本地,等网络恢复再补传。这类场景用SQLite非常合适,单文件、零配置、嵌入式,一个文件就能存下几天的数据量,断电也不容易丢。
2.2 为什么传统数据库扛不住时序数据
我做过一个最直观的对比。早期我们接一个项目,刚开始设备数量少,MySQL还能勉强扛住,每天也就几百万条数据。后来产线扩容,设备点位翻了几倍,每天写入量直接到了几千万条。问题一个接一个地出现:磁盘写入跟不上,CPU经常跑满,查询一张三天的数据图表要等十秒以上。
根本原因在于,关系型数据库的所有写入都要经过事务、索引、锁这些机制,它更在意的是数据一致性;而时序数据的特点是写入极其频繁、几乎从不修改,查询又总是绕着时间维度转。用MySQL存这种数据,就像用一个带完整账务系统的超市系统去记录每条商品的销售流水,每笔都要记账,量大了自然吃力。
后来我们把时序数据切到了TDengine,情况立刻就不一样了。它是专门为时序数据设计的数据库,底层采用列式存储,数据按时间自动分区,还内置了降采样、聚合函数、连续查询这些功能。设备原始数据存进去之后,想看五分钟均值、一小时均值,直接在SQL里写INTERVAL子句聚合,不用再写一堆GROUP BY再加上临时表的逻辑。这个效率差距,在每秒上千次写入的场景下是非常明显的。
2.3 数据库选型的完整逻辑
现在我做智能工厂项目,数据库选型基本固定了一个组合:MySQL做主数据管理,TDengine存时序数据,SQLite做边缘缓存。这个组合不是越复杂越好,而是每一类数据都找到了自己最合适的地方。
很多同事问我,能不能把关系型数据也放到时序数据库里?我的建议是最好不要。工艺参数、工单流转这种需要更新和关联查询的数据,放时序库里查询起来很别扭。反过来,把设备采集数据全写进MySQL,一旦数据量上来就会变成灾难。数据不分家,但要分库。
时序库里要长期保存多少数据,这也要提前规划。TDengine提供按时间设置数据生命周期(keep)的能力,比如普通点位数据保留90天,报警数据保留一年,历史报表数据压缩归档。这里讲究的是“够用就好”,别把内存和磁盘当成无限资源来用。我见过有项目把原始点位数设置成永久保留,结果三个月后磁盘被写满,又不得不连夜写脚本清理,非常狼狈。
3. 一套可落地的完整链路:从设备点位到数据库
3.1 先把设备“翻译”成数据
再往下就是实打实的落地工作了。也许你已经知道数据库要选什么,但当设备真正摆在面前,你面对的是一堆协议文档和点位表。我拿到一条产线之后,第一件事不是写代码,而是做点位梳理:每一个设备有哪些参数要采、点位地址是多少、数据类型是什么、采集频率多高。
这一步看着简单,其实决定后面的所有架构。举个真实例子,一台数控机床可能同时存在几百个点位,但真正业务需要关心的可能只有二三十个。如果不加筛选地全部采集,不仅是浪费存储和带宽,而且会在接口层给设备控制器增加不小的负担。有些老设备的控制器通信带负载能力很弱,采集频率太高或者点位太多,它可能连正常的工艺程序都会受影响。
点位表梳理出来之后,我一般会用一张标准Excel表格维护:设备编号、点位名称、地址、协议类型、数据类型、单位、采样周期。这张表就是整个数字化项目的地基,后续写采集程序、建数据库表结构、做看板,全都以它为准。
3.2 采集服务:批量写入是王道
点位梳理好后,开始在边缘网关写采集程序。语言选型上,Python胜在开发快,适合几百个点位的采集;C++或Go则更适合点位多、性能要求高的场景。我自己两种都写过,最终稳定运行的版本大多是C++写的采集核心加Python辅助脚本组合。
采集程序的写法,核心秘诀只有一个:批量写入。千万不要收到一条数据就往数据库插一条,那样会给数据库造成巨大的写入压力。我见过一个项目,采集程序刚上线时是逐条INSERT,结果高并发时段数据库CPU直接到100%,磁盘IO也一路飘红。
后来改成消息队列加批量入库:采集线程拿到数据写入内存队列,每隔500毫秒取出一批,一次INSERT中拼接几十到几百条记录,写入速度直接上了一个台阶。这个改动不复杂,效果却立竿见影——数据库CPU从100%降到了30%左右,采集延迟也稳定了。
3.3 数据库接入:C++绑定写入和预编译
如果用的是TDengine,官方推荐的方式是参数绑定接口,就是你看到很多文档里提到的taos_stmt_prepare这类API。一开始我也觉得直接拼SQL字符串不也一样吗?直到对比测试才发现差距:字符串拼接要反复解析SQL,而且参数转成字符串再传进去,CPU开销很大;参数绑定是先预编译SQL语句,然后直接把参数按二进制方式传给数据库,批量执行时性能可以快上好几倍。
我写过一个简单的C++采集器,核心逻辑大概是这样:先用taos_stmt_init初始化语句句柄,接着用taos_stmt_prepare准备好一条带占位符的INSERT语句,再逐个绑定数据。这样每来一批数据,只要绑定新参数再批量提交就行,不需要重新解析SQL。
INSERT INTO t_meters USING meters TAGS('saw_01', 1) VALUES (?, ?, ?)这个绑定都完成了,还要注意taos_stmt_add_batch和taos_stmt_execute配合使用,先攒一批参数再真正执行。我之前一开始调试时,每次绑定都立刻执行,效果不好,后来改成攒够几百条批量执行,性能才对了。
3.4 部署数据库时的Linux参数调整
数据库装好后,Linux系统层面还要做一轮针对数据库的调优。最典型的就是文件描述符,因为数据库每建立一条连接就占一个文件句柄,连接数一多,默认限制肯定不够。
再一个就是磁盘IO调度器,如果数据库盘是普通机械盘,在RHEL/CentOS系里我会把调度器调成“deadline”类型,和时序数据的顺序写入特性更匹配;如果是SSD,用“none”或者直接交给内核来调度,减少延迟。这些细节平时没人提,但对写入高峰期的表现影响明显。
最后,绝对不能忘的就是时间同步。NTP服务必须配好,最好让边缘网关、数据库服务器都统一从厂区的时间服务器同步。时序数据库的准确度完全依赖时间,两个设备时间差一分钟,写进去的数据排序就会错乱,查出来的曲线完全不对。这类问题排查起来很费劲,但预防成本极低。
4. 数据库同步、备份与高可用设计
4.1 业务数据的主从同步
智能工厂的数据库不是单打独斗,必须考虑高可用和数据备份。MySQL这一层,我通常会做主从复制:一个主库负责日常业务写入,一个从库负责报表查询。主库把变更写到binlog里,从库拉取binlog并重放到自己的数据库里。这样即使主库服务器出了故障,从库可以切换上位,报表和看板不至于中断太久。
配置主从不太复杂,但有个细节很容易被忽略:必须确保两张表的字符集、排序规则一致,否则复制的过程中会出现数据格式不匹配报错。还有,从库查询压力大了之后,要单独给从库加索引,别指望和主库共用同一套索引策略,因为查询负载模式完全不同。
4.2 时序数据库的多副本机制
TDengine本身有内置的多副本机制,只要集群节点数量够,数据会按照Raft协议在多个节点间自动复制。这一点在工业场景非常实用,因为设备数据一旦丢了很难补采。我在中心机房部署三节点的时候,最直接的体会是:即使有一台机器宕机,写入和查询都能秒级自动切换,完全感觉不到影响。
但多副本只是解决了高可用问题,备份仍然要自己做。不要天真地以为多副本就是备份,误删数据的时候,多副本只会把误删同步到所有节点。我每周会定时把时序数据做一次逻辑导出,再把导出文件同步到另一台存储服务器。备份的恢复演练也要真做,别等到真出事才发现备份包是坏的。
4.3 边到云的数据同步
智能工厂的数据流向通常是边到云,也就是边缘网关把数据往上送到中心数据库,而不是反过来。这种情况下,市面上很多面向互联网的数据库同步软件并不能直接套用,它们设计的初衷多半是数据库与数据库之间双向同步,而不是设备端到平台端的写入。
我现在的标准做法是:边缘采集程序只写本地SQLite缓存,后台再独立跑一个同步进程,从SQLite读取尚未上报的数据,按批次提交到中心接口或直接写时序数据库。网络断了没关系,边缘端继续本地存储,网络恢复后自动补传。这个模式关键要记录每个批次的提交状态,防止漏传和重复传。
5. 运行一年后,我踩过的那些坑
5.1 Linux侧排查实录
系统上线一年后,各种故障就会开始露头。最常见的一类是 Linux 层面的资源问题。我遇到过一台边缘网关运行半年后采集进程突然消失,查看journalctl日志,发现是内存溢出导致进程被系统杀了。后来一检查,是采集程序里有个C++的缓存队列没有做大小限制,连续跑了一个星期,内存干脆被吃光。
解决办法也简单粗暴:给采集服务加上 systemd 守护,配置成异常退出自动重启,同时把内存缓存上限写死,防止无限膨胀。从那以后,即使程序再出问题,系统也能在几秒内把服务拉起来,至少产线数据不会长时间断档。
Linux排障时,我常用这几个命令组合:top先看CPU和内存占用,iostat -x看磁盘IO是否有瓶颈,vmstat看上下文切换频率,journalctl -f实时看应用日志。大多数故障,靠这几个命令基本都能定位到方向。
5.2 数据库侧排查实录
数据库侧的坑更多。最常见的是慢查询,尤其是MES报表上线初期,用户各种花式筛选条件一跑,SQL一个比一个复杂。我打开MySQL的慢查询日志一看,有的查询居然要十几秒。逐个分析后发现,多半是联合查询的关联字段没建索引,或者日期字段上用了函数导致索引失效。
解决方法是逐个SQL优化:要么给关联字段补索引,要么改写查询条件,让索引能生效。还有一个经典情况,就是报表工具喜欢用SELECT *取出所有字段,实际只要三五个字段,查询返回的数据量大得离谱。把字段列入白名单之后,很多报表直接从十几秒降到了1秒以内。
TDengine这边最常遇到的问题反而是写入性能下降。排查出一旦数据量涨到一定程度,查询和写入都慢了下来。最后发现是节点磁盘快满,因为时序数据的生命周期设置得太长了,老数据一直堆着。把保留策略调整成按点位重要程度分级之后,马上就恢复正常。
5.3 数据库连接池爆满的经典案例
数据库中另一个非常常见的问题就是连接数爆炸。一次生产MES系统突然大面积报错,一看MySQL的show processlist,连接数已经冲满到上限。原因是应用层没有合理使用连接池,每次数据请求都新建数据库连接,请求一多全部堆在那,最终拖垮数据库。
这里的关键是应用层必须统一使用数据库连接池,而不是直接裸连数据库。连接池能复用已有连接,减少建立连接的开销,还能设置最大连接数、等待超时时间。调优的时候,先把max_connections调到一个合理值,再结合应用的并发量来设连接池大小,而不是盲目开大。很多运维一遇到连接不够就把max_connections提到几千,这是治标不治本,数据库本身的资源就那么多,连接再多也只是排队。
6. 最后想说的几句实话
做了几年智能工厂项目,最大的感受是:真正决定项目成败的,往往不是那些高深的人工智能算法,而是Linux上稳不稳、数据库扛不扛得住。你要把几百台设备的数据连续接进来,把大屏做到秒级刷新,把故障报警做到准时推送,背后没有捷径,只有一遍遍地检查采集程序、调整数据库参数、验证备份恢复。
最后分享一个我很受用的小技巧:给每一个采集服务都加上心跳上报,每30秒向数据库里写一条心跳记录。这样一来,你随时可以通过查心跳表知道哪些设备掉线、哪些采集进程不健康,不用等现场人员反映问题。这个习惯帮我提前发现过好几次隐患,也让我在排查问题的时候少走了很多弯路。
工业数据这条路,前期会有不少重复性的工作,但只要底层的Linux和数据库打扎实,后面做起算法分析、质量预测来,才能真正站在可靠的数据地基上。如果你也在做类似的事,希望这些经验能帮你少踩几个坑。