
干过几年工业信息化项目的人应该都懂这种场景现场设备的数据明明都躺在PLC里但是要让这些数据准时、干净地落进数据库中间弯弯绕绕比想象中多得多。OPC UA解决的是设备互联和数据互认的问题但真正想把点位数据搬进MySQL总得有个人干这跑腿活。我最近做一套设备数据采集方案时就直接用Node-RED把链路打通了OPC UA模拟服务器替代现场PLC→ Node-RED订阅点位变化 → 数据格式化 → 写入MySQL。整个过程全部可视化完成从环境准备到数据落库两个晚上搞定。这篇教程就从这个实战项目里来面向正在做数据采集又不想写一堆结构化代码的工程师。下面不光有每个节点怎么配、参数怎么填还会把我在实际配置中踩过的坑一起记录下来给你一份能直接照抄的作业。1. 整体方案设计为什么是Node-RED、OPC UA和MySQL1.1 三种技术各自解决了什么问题先把三个角色摆清楚OPC UA是统一数据接口解决的是“怎么从设备那边把数据拿得出来”Node-RED是数据搬运与加工中枢解决的是“拿到数据之后怎么处理、发到哪里去”MySQL是最终数据仓库解决的是“历史数据存哪里、报表和看板去哪里查”。在工业现场OPC UA已经基本成了设备通信的事实标准。PLC、传感器、扫码枪、智能仪表主流厂商出的新设备都支持OPC UA协议而且它在防火墙穿透、信息安全、数据建模这些方面比老一代的OPC DA要强不少。机房做数据采集面子上首选就是OPC UA。但OPC UA只是协议数据要落库中间总得有个软件。以前大家习惯用Kepware 工控机 自研上位机的方式配置复杂改个点位就要重新折腾一遍。用Node-RED的好处在于它是可视化流编辑采集、转换、写库全用节点拼出来维护门槛低现场电气工程师也能看懂。MySQL就更不用多说了传统工厂DBA熟运维成体系报表工具连接方便。相比时序数据库项目交付时客户往往点名要求“给我搞个MySQL”因为它好交接、好备份、好迁移问啥都有人会。1.2 典型应用场景这套组合最典型的场景就三个设备运行数据采集把产线PLC里的电流、电压、温度、转速、产量计数等点位周期性采集后存入MySQL供MES系统查询设备状态。能耗监测项目从智能电表、水表、气表的OPC UA服务器读取实时读数经Node-RED定时写入数据库日报表、月报表直接SQL统计。设备OEE分析把设备启停状态、故障信号、运行时长采集落库后续在前端搭建OEE看板。我这次具体做的场景是模拟一个注塑车间用Prosys OPC UA Simulation Server模拟了三个温度点、两个开关量点、一个运行状态点Node-RED按1秒周期订阅数据解析后写入MySQL的machine_readings表。整个链路跑起来后数据库里就能持续看到模拟设备的数据在增长。1.3 为什么不用Python或者C#直接写不是不能写而是不划算。用Python或者C#去接OPC UA先要装SDK比如python-opcua、OPCFoundation .NET Standard然后写一个常驻服务处理连接重连、订阅管理、数据缓存、日志输出还要考虑崩溃恢复。这套代码写出来少说三五百行后期设备点位增加时还要改代码、测试、重启服务。Node-RED这套链路里的节点OPC UA客户端、MySQL写入这些都有成熟方案功能上完全满足需求。真遇上大批量设备接入还能通过MQTT网关做级联架构可以平滑扩。从项目交付和维护两个角度看Node-RED确实是在“快速搞定”和“稳定运行”之间最平衡的选择。当然也要说清楚如果项目点位超过一两万个、采集频率是毫秒级那得换高性能服务组件Node-RED做小规模采集和边缘处理更合适别硬撑着上生产超大规模场景。2. 动手前的环境准备2.1 一套完整的环境清单这次实测用的环境信息如下便于你对照组件版本/规格说明操作系统Windows 10 专业版也可以用Ubuntu 20.04/22.04差别不大Node.js16.20.0 LTSNode-RED要求Node.js 14以上尽量上16或18Node-RED3.0.2当前稳定版OPC UA模拟服务器Prosys OPC UA Simulation Server 5.4.2模拟PLC数据源OPC UA客户端测试工具UaExpert 1.6.2验证模拟服务器端点和点位MySQL8.0.36也可以用5.7注意认证插件差异Node-RED节点node-red-contrib-opcua 0.3.32 / node-red-node-mysql 1.0.3核心功能节点我这边实际跑通用的就是这套组合。Node-RED有个好处就是它对版本的敏感度不高你手上的版本大体相当按本文步骤来就行。2.2 安装Node-RED的两种方式Node-RED安装方式有多种我只说我正式项目里用过的两种npm全装和Docker。方式一npm全装适合Windows/Linux桌面环境npm install -g node-red node-red浏览器打开 http://127.0.0.1:1880 就能进Flow界面。这种方式最直接节点调试也方便。方式二Docker方式适合服务器环境docker run -it -p 1880:1880 -v node_red_data:/data --name nodered nodered/node-redDocker部署的好处是环境隔离、迁移方便但问题是一旦容器删除不挂数据卷节点就全没了。用Docker跑的话记得一定要把/data目录挂出来防止重装容器时Flow全部丢失。提示无论哪种安装方式装好后第一步建议先改admin密码和开启用户认证。Node-RED默认无鉴权公网环境会被人刷爆。2.3 模拟数据源用Prosys Simulation Server造数据手头没有真实PLC就用模拟服务器它比想象中好用得多。Prosys提供的这个模拟服务器从官网免费下载即可装完启动后默认监听端口53530。启动后在Address Space里能看到一批自带节点通常位于Objects → Simulation → Simulation Demo下面有几个温度、开关量类型的模拟变量。在配置中你可以设定数字点变化间隔和变化范围比如让Temperature每秒在20到50之间变化这样数据库里能明显看到数据的起伏。验证模拟服务器是否正常用UaExpert这个客户端工具连一下这个工具也是免费的是OPC爱好者圈最常用的调试神器Server URL填写opc.tcp://localhost:53530/opcua/simulation-server安全策略选择None登录方式选择Anonymous连上后能看到节点树而且能手动订阅几个点位验证协议交互是否正常。这一步很重要它能帮你在排查问题时判断“到底是我Node-RED配错了还是模拟数据源本身有问题”。2.4 MySQL建库建表MySQL我用的8.0官方安装包安装过程不做展开网上有大量详尽的教程。需要注意的是装完MySQL 8以后默认的认证插件是caching_sha2_passwordNode-RED的mysql节点连的时候可能会出现认证不兼容的情况这个我在后面问题排查部分专门讲。建议本次实验创建独立用户和独立库别直接用root。进入MySQL命令行逐条执行CREATE DATABASE iot_data DEFAULT CHARACTER SET utf8mb4; CREATE USER noderedlocalhost IDENTIFIED WITH mysql_native_password BY NodeRed123456; GRANT ALL PRIVILEGES ON iot_data.* TO noderedlocalhost; FLUSH PRIVILEGES;我用mysql_native_password来创建用户就是提前避开认证失败坑的一个举动后面会有详细说明。然后是建表一张简单的数据记录表USE iot_data; CREATE TABLE machine_readings ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, device_tag VARCHAR(100) NOT NULL COMMENT 设备点位标识, reading_value VARCHAR(50) NOT NULL COMMENT 读取到的数值, reading_time DATETIME(3) NOT NULL COMMENT 读取时间(毫秒精度), KEY idx_read_time (reading_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT设备点位数据表;这张表的设计思路是通用的device_tag存点位名称reading_value用varchar统一存数值因为工业点位有整数、浮点数、布尔值统一转字符串最简单查询时再转类型reading_time用DATETIME(3)保留毫秒精度这样在生产现场高频采集写入时同一秒内的数据也不会互相覆盖。3. 核心实操三个关键节点的配置细节3.1 安装两个核心节点OPC UA和MySQL进入Node-RED界面点右上角菜单 → 管理面板Manage palette → 安装搜索以下两个节点分别安装node-red-contrib-opcuanode-red-node-mysql如果在线安装慢或者失败就用命令行方式在Node-RED的安装目录下执行npm install node-red-contrib-opcua node-red-node-mysql装完后重启Node-RED服务左侧节点列表里应该能看到OPC UA分类和MySQL分类。如果在左侧找不到刷新浏览器页面就能解决千万别在没刷新页面的情况下以为装失败了这个我自己就被坑过好几次。提示安装node-red-contrib-opcua依赖的包很多安装过程可能需要几分钟别中途断网。如果卡住不动先清npm缓存再重试。3.2 OPC UA客户端节点连接、订阅一次性配好拖一个OPC UA节点到画布上双击打开配置面板。第一步添加Endpoint。在Endpoint一栏点击编辑按钮弹出来填写Transportopc.tcpHost127.0.0.1换成模拟服务器所在IP远程就是那台机器的IPPort53530Path/opcua/simulation-server拼起来就是opc.tcp://127.0.0.1:53530/opcua/simulation-server和你用UaExpert验证的地址保持一致。第二步选Security Mode。测试阶段选None生产环境可以根据需要选Basic128Rsa15、Basic256等。注意模拟服务器的安全策略如果是None而你在Node-RED中选了Basic256就会握手失败。两边要完全匹配。第三步设置认证。选择Anonymous。如果OPC UA服务器配置了用户名密码那就在这里填对应的账号和密钥。以上配置好之后进入订阅设置。点击Config to Monitor添加要监控的点位NodeId格式像ns2;i1001或ns2;sMachine_TemperatureType选择NodeId方式我们可以先用UaExpert把模拟服务器节点树里的一个温度点找出来记录它的NodeId。然后在Node-RED的OPC UA节点里把该点位加入订阅。订阅还有两个关键参数Publishing Interval发布间隔数据的推送周期我实际用的1000ms即1秒推送一次。Sampling Interval采样间隔客户端从服务端去取值的周期建议和发布间隔一样或更小例如500ms。订阅配置完成后节点部署好debug输出就能看到payload。node-red-contrib-opcua节点的输出格式通常是{ payload: { value: 36.72, dataType: Double, sourceTimestamp: 2025-06-01T12:00:00.123Z }, topic: ns2;sMachine_Temperature }如果debug输出的格式和这个不完全一样很大概率是你安装的节点版本不一样。批量取value时兜底方法很简单payload里先判断value字段在不在如果数据嵌套了一层再加一层dataValue处理就行。后面我给Function节点时会做兼容写法。3.3 数据处理Function节点把OPC UA数据转成MySQL插入格式OPC UA节点出来的是对象形式的数据直接扔给MySQL节点是写不进去的。MySQL节点要求传入的msg.payload是一个包含所有字段的JavaScript对象字段名必须和表里的列对得上。我的做法是拖一个Function节点放到OPC UA节点后面双击编辑填入如下代码var data msg.payload; var readingVal ; if (data data.value ! undefined) { readingVal data.value; } else if (data data.dataValue data.dataValue.value) { readingVal data.dataValue.value.value; } else if (typeof data number || typeof data boolean) { readingVal data; } else if (typeof data string) { readingVal data; } else { readingVal JSON.stringify(data); } var tagName msg.topic || unknown; var now new Date(); var timeStr now.getFullYear() - String(now.getMonth() 1).padStart(2, 0) - String(now.getDate()).padStart(2, 0) String(now.getHours()).padStart(2, 0) : String(now.getMinutes()).padStart(2, 0) : String(now.getSeconds()).padStart(2, 0) . String(now.getMilliseconds()).padStart(3, 0); return { payload: { device_tag: tagName, reading_value: String(readingVal), reading_time: timeStr } };说明一下这段代码的用意兼容不同版本节点返回的数据结构变化保证能拿到实际数值。使用String统一转换避免数值类型被MySQL拒绝。时间字段用JS本地时间生成保证和数据库时间一致避免时区偏差。topic字段就当点位名来用但需要注意如果OPC UA订阅多个点最好在OPC UA节点的配置里给每个订阅设置一个友好名称方便后续报表识别。也可以不强行依赖topic而是在OPC UA节点后续挂一个Switch节点按topic分流再给每一路加上更有意义的别名但这种做法适合点位少的场景点位多时会画得很乱。实际项目中我推荐用代码里直接维护一个NodeId映射表的方式var nodeMap { ns2;sMachine_Temperature: 注塑机1#_模温, ns2;sMachine_Pressure: 注塑机1#_射胶压力 }; var rawTag msg.topic || unknown; var tagName nodeMap[rawTag] || rawTag;前端报表拿到的已经是中文含义清楚的点位名省得后期再做一层维表关联。3.4 MySQL输出节点一条INSERT语句搞定写入从左侧数据库分类里拖一个mysql节点放到Function节点后面。双击打开配置点击server旁边的编辑按钮配置连接信息Host127.0.0.1Port3306UsernoderedPasswordNodeRed123456Databaseiot_data这个节点底层驱动是mysql2支持连接池复用所以每个连接配置可以直接多个mysql节点共享不需要每个节点各自连一遍资源消耗会小不少。我一般一个Flow里就用同一个server配置实例避免大量连接把数据库连接数吃满。设置好server之后在Query栏填INSERT INTO machine_readings (device_tag, reading_value, reading_time) VALUES (${payload.device_tag}, ${payload.reading_value}, ${payload.reading_time})注意这里的写法在MySQL节点里${...}表示引用msg对象里的字段值。这个特性很方便但用的时侯有几个坑字段值如果包含单引号SQL会报错。工业场景里点位名一般不会带单引号但如果是批量转发的数据建议在Function节点里做个replace。字段值的类型。reading_value字段是varchar所以拼的时候要包单引号若字段是int或double不需要加引号否则MySQL会有隐式转换数据量大时会慢。写完query之后这个节点的输出一般不需要再接别的处理。如果希望确认写入成功可以直接在mysql节点后挂一个Debug节点很多情况下看输出结果是空这是正常的因为INSERT本身没有返回结果集不影响数据写入。3.5 完整数据流串联与联调把四个节点连起来链路是OPC UA节点 → Function节点 → MySQL节点其中OPC UA节点配置了三个温度点、两个开关量点、一个运行状态点的订阅。每个点位的数据进来后都会经过Function节点处理映射成中文名字然后写入数据库。部署后打开Debug侧栏观察能看到每个点位的数据打包成了对象形式包含device_tag、reading_value、reading_time三个字段。同一时刻数据库表里会多出6条记录对应6个点位。为了验证数据是否正确落库我习惯在命令行里执行一条查询SELECT * FROM iot_data.machine_readings ORDER BY id DESC LIMIT 20;能看到数据在持续增长且时间列精度到了毫秒。这样整个链路就算跑通了。我还做了一点扩展在OPC UA节点和Function节点之间加了一个RBEReport By Exception节点这个节点在node-red-contrib-rbe包里功能是“只在数值变化时才放行下游”。加了它以后如果模拟服务器的某个温度值一直不变就不会写数据库有效降低无效写入量。不过在加RBE时要注意点位从设备离线到恢复上线如果值没变化RBE不会输出这样数据库里就缺失了“设备恢复”这个变化记录。解决方法是给RBE设置一个超时时间比如30分钟强制放行一次心跳数据保底不丢。4. 常见问题排查与避坑实录4.1 OPC UA连接失败应该从哪里查起连接失败是联调阶段最高频的问题我总结了一套排查顺序按顺序走基本能定位第一步确认OPC UA服务器端的地址是否可达。在Node-RED所在的机器上ping一下服务器IP再用UaExpert测试连接同一个地址。如果UaExpert能连上而Node-RED连不上说明是Node-RED节点配置问题反过来则是网络或服务器问题不用在Node-RED里反复改。第二步确认安全策略匹配。模拟服务器里如果启用了Basic256等安全策略Node-RED的Security Mode要选择一致同时还需要在OPC UA节点的Endpoint配置里把证书相关的配置做一次重置。常见报错是把Node-RED生成的客户端证书扔到服务器不信任列表。第三步检查服务端连接数量限制。有些免费的OPC UA模拟服务器会限制同时连接数如果UaExpert已经占了一个连接Node-RED再连可能失败。先把UaExpert断开连接试试。Prosys仿真服务器的日志里能看到接入客户端的时间、IP、安全策略等信息通过它判断服务端是否收到了连接请求排查效率最高。这也是我一直强调“先把UaExpert验证通了再让Node-RED上”的原因。4.2 NodeId格式填错数据就是出不来OPC UA的NodeId格式很像文件路径节点标识对应数字格式是ns2;i1001字符串格式是ns2;sTemperature。最容易犯的错是把i和数字之间多加空格、或者把;和s的顺序搞反。错误的写法如ns2; s1001空格会导致kernel不认。如果实在不确定NodeId怎么写在UaExpert的Address Space里右键点位选择Copy NodeId粘贴过来的就是标准格式。复制出来以后在Node-RED的订阅里直接粘贴基本不会出错。4.3 MySQL写入报错认证、字符集与字段类型我从实际错误里整理了三个最常见的场景附排查方案症状可能原因解决方式Error: ER_NOT_SUPPORTED_AUTH_MODEMySQL 8默认caching_sha2_password认证插件不被旧驱动支持创建用户时用mysql_native_password前面示例已提前规避Incorrect string value: \xE6\xB5\x8B...中文字段值写入时字符集不对建库建表统一用utf8mb4并且确保连接URL设置了characterEncodingData too long for column点位值超字段长度reading_value的varchar长度调大建议100以上如果你用的是Docker版MySQL容器还要检查端口映射是否正常。在宿主机上用mysql客户端连不上但在容器内能连上通常是端口没有映射出来或者MySQL容器没有重启生效。4.4 写入频率太高数据库压力大1秒一条数据一个点位一天就是86400条如果采集几十个点位一个月下来数据库数据量会非常可观。所以在生产实践中我不会把Node-RED的采集数据直接逐条入库而是分情况处理数据变化量不大、但对实时性要求高的点位比如设备报警状态逐条写入没问题。温度、压力、流量这类缓变数据适合每5秒甚至每10秒采集一次或者只在变化量超过阈值时写入比如变化超过0.5度才更新。统计类数据可以按分钟做聚合用Node-RED的function节点在内存里做累加再每分钟输出一次平均值或累计值。也可以直接改SQL用批量插入INSERT INTO machine_readings (device_tag, reading_value, reading_time) VALUES (tag1, 10, 2025-06-01 12:00:00), (tag2, 20, 2025-06-01 12:00:01), (tag3, 30, 2025-06-01 12:00:02);在Function节点里缓冲100条再发给MySQL节点实测下来写入性能提升非常明显也从根上避免了高频写入时数据库连接被占满的问题。4.5 Node-RED进程与MySQL连接的心跳机制部署完数据流后一两天内可能发现Node-RED侧MySQL节点偶尔报ECONNRESET错误。这通常是MySQL服务端主动断开了空闲连接Node-RED连接池没有及时感知。解决办法是让MySQL节点周期性发一个简单的SELECT 1保活连接。我一般用Inject节点每5分钟触发一次SELECT 1把Inject节点连着MySQL节点的Server配置这样连接池里至少有一个活跃连接空闲断开的问题基本不再出现。这个偏门问题在长时间跑生产数据流时特别重要一开始可能察觉不到等到报警记录缺失再查就迟了。5. 实战经验把Demo升级到可用交付跑通节点只是第一步真正要把这套东西交付到客户现场还得考虑数据断线补传、设备掉线感知、看板查询这几个问题。5.1 数据断线补传机制OPC UA客户端与服务器之间的网络如果抖动订阅会断掉重连之后数据缺失的那段时间就再也补不回来了。如果采集的是产量累计值补传的意义尤其大。我的处理方法是OPC UA节点重连成功后在Function节点里加一个判断当msg.payload里带了历史时间戳且时间偏移大于一定阈值时把数据直接写入MySQL同时打一个debug日志方便运维确认补传是否执行。如果OPC UA订阅本身不提供历史数据备选方案是每隔30分钟用OPC UA Read节点主动读一次关键点位的当前值与MySQL里最新值比对偏差过大就补一条形成保底机制。5.2 设备掉线怎么反馈工业生产场景里数据没有变化并不等于设备离线但设备故障往往先从数据源消失开始。所以我加了一个“心跳监控”设计在Node-RED里用Inject节点每30秒向OPC UA服务器发送一次读请求读一个固定站位。如果连续3次读取超时就触发一条报警级别的记录写入MySQL并同时输出到LED面板。这样即使单个点位没有值变化设备离线也能第一时间在数据里暴露出来。5.3 查询报表的SQL示例写完数据库不是为了攒数据最终是要拉出来看的。这段时间配合这套采集方案我写了一个按小时统计温度的SQL供你参考SELECT DATE_FORMAT(reading_time, %Y-%m-%d %H:00:00) AS hour_slot, device_tag, ROUND(AVG(CAST(reading_value AS DECIMAL(10,2))), 2) AS avg_value, MAX(CAST(reading_value AS DECIMAL(10,2))) AS max_value, MIN(CAST(reading_value AS DECIMAL(10,2))) AS min_value FROM machine_readings WHERE device_tag 注塑机1#_模温 AND reading_time NOW() - INTERVAL 1 DAY GROUP BY hour_slot, device_tag ORDER BY hour_slot;这种SQL是给生产报表做数据源的常用格式。直接扔进MySQL客户端或报表系统都能跑起来。5.4 Node-RED进程守护最后提醒一点在生产机上Node-RED不能裸奔必须加守护。Linux下用systemd编写服务文件Windows下可以用NSSM把node-red注册成Windows服务。不然机房一次断电重启Node-RED没自动拉起来现场数据采集就断了没人知道。我自己实际部署时用的systemd配置很简单核心就是写一个/etc/systemd/system/nodered.service[Unit] DescriptionNode-RED Afternetwork.target [Service] Typesimple Usernode-red Groupnode-red ExecStart/usr/bin/node-red Restarton-failure RestartSec10 [Install] WantedBymulti-user.target每次改完Flow部署后这个服务不用重启但升级Node-RED或新增节点后必须重启这个区别要注意正好对应了大家常问的“加了节点要不要重启服务”这个问题。5.5 从单机到多设备的扩展路径有朋友看到这里可能会问如果我现场不止一台PLC而是十几台、几十台这套方案还适用吗适用性其实挺强。Node-RED节点天然支持并行添加多个OPC UA源但是要注意单个Node-RED实例连接数越多内存和CPU占用越高。十几台以内可以硬扛再多建议分机做边缘采集然后用MQTT数据总线把各边缘节点的数据汇总到中心节点再由中心节点统一写MySQL。这个架构在好几个项目中验证过稳定性、可扩展性都不错。我也顺手整理过一个节点资源和点位的参考量级普通VM服务器4核8G上Node-RED稳定承载的OPC UA订阅点位大概在五千到一万之间再往上就得认真调优或者上多个Node-RED实例分担。实际工作里折腾这套流程最大的体会是做工业数据采集不一定非得上重型平台把Node-RED、OPC UA和MySQL这三个基础组件用扎实了一张流图就能把整个链路拉通。你后面再遇到类似项目的需求不用每次从零调研把这套方案当作模板套进去同时把缓存批处理、断线补传、服务守护这些都提前加好就能少走不少弯路。最后分享一个操作习惯改动任何节点配置部署前先截图保存旧版本流如果出了问题能一键恢复这招在跑生产数据流的场景里能救命。