数据持久化方案_MySQLSQLite双保险
2026/8/21 7:22:59 网站建设 项目流程

PLC 监控系统数据持久化方案:MySQL + SQLite 双保险,不丢数据

工业监控中数据丢失是最致命的。本文详解 PLCMonitor 的双数据库架构:MySQL 主存储 + SQLite 本地缓存,网络故障时自动降级,恢复后自动同步。


问题:你的产线数据打算存多久?

很多工业监控项目上线后才发现一个问题:数据说没就没了。

场景后果
上个月温度曲线异常,数据已经清了找不到证据,排查延长
月底要出生产报表,历史数据丢了管理层不高兴
数据库文件损坏三个月白干了
停电了没电时采的数据,永远没了

工业监控的数据存储,不是"能存就行",而是要做到:

  • ✅ 不丢数据(容灾)
  • ✅ 存得住(容量管理)
  • ✅ 查得快(性能)
  • ✅ 随时恢复(备份)

为什么用两个数据库?

PLCMonitor 同时使用SQLiteMySQL,分工明确:

数据库角色特点
SQLite本地缓存 + 故障切换单文件、免安装、离线可用
MySQL主存储并发支持、远程访问、多客户端共享

核心设计理念:优先 MySQL,失败走 SQLite

采集数据 ↓ 优先写入 MySQL(主库) ↓ 成功? ├─ YES → 记录 synced=1 └─ NO → 写入 SQLite(备库) ↓ MySQL 恢复后 SQLite → MySQL 自动同步

效果:

  • 正常运行时:数据存在 MySQL,多客户端可查
  • 网络故障时:数据不会丢,缓存在 SQLite
  • 网络恢复后:积压数据自动补传

为什么不选其它数据库?

数据库为什么不选
PostgreSQL功能强大但重量级,部署成本高
MongoDB文档型,时序数据量巨大时查询性能不如关系型
InfluxDB专用时序数据库,门槛高,社区版不支持双写降级
Redis内存数据库,断电即丢,不适合长期持久化
SQL Server / Oracle企业级商业数据库,授权费数万起步

结论:SQLite + MySQL 组合兼顾离线可用远程共享,成本最低、部署最简单。


数据量计算:500 标签每秒采集

假设场景:500 个标签,每秒采集一次

时间维度记录数
每秒500 条
每分钟3 万条
每小时180 万条
每天4320 万条
每月13 亿条
每年157 亿条

以每条记录约 40 字节计算,每年原始数据约 620 GB

存储不是问题,问题在查询

为什么不能直接查原始数据?

查询过去一年温度趋势:

  • 一年 157 亿条数据
  • 筛选温度标签 → 至少千万级扫描
  • 全表扫描,一次查询 30 秒到数分钟

时间越长,需要的不是原始数据,而是聚合数据。


聚合策略:时间越长,粒度越粗

工业监控的本质是看趋势,而不是看瞬时值

查询目的时间跨度需要的精度
实时报警1 秒精确到每个采样点
当前状态1 分钟最近值足够
今天趋势1 小时每分钟一个点即可
本周分析1 天每小时一个点即可
年度总结1 个月每天一个点即可

聚合表设计

粒度表名数据源触发间隔保留时长聚合规则
秒级data_secondPLC 采集实时7 天原始值
分钟级data_minute秒级数据每分钟30 天平均值 + 最后值
小时级data_hour分钟数据每小时1 年平均值 + 最后值
天级data_day小时数据每天永久平均值 + 最后值

什么是"平均值 + 最后值"?

以温度为例,一分钟内 60 次采样:

时间14:00:0114:00:0214:00:59
原始值25.125.325.4

聚合后变成一条记录:

timestamp: 2024-06-01 14:00:00 avg_value: 25.2 last_value: 25.4

好处:查一年数据只需查data_hour表(438 万条),而不是data_second(157 亿条)。


EAV 模型:标签随意增减

传统方式的问题

每加一个标签就要改表结构——维护噩梦。

EAV 方案

EAV =Entity-Attribute-Value(实体-属性-值),动态标签友好:

CREATETABLEdata_second(idINTEGERPRIMARYKEYAUTOINCREMENT,data_idBIGINTNOTNULL,-- 全局唯一 IDtag_nameVARCHAR(255)NOTNULL,-- 标签名valueDOUBLENOTNULL,-- 值qualityTINYINTDEFAULT0,-- 质量(0=好,1=坏)timestampDATETIMENOTNULL,-- 时间syncedTINYINTDEFAULT0-- 同步标记);

无论加多少标签,表结构不动。

data_id使用(当前毫秒时间戳 << 20) | 随机数生成,确保跨机器、跨时间唯一。


写入优化:异步批量

问题:500 次单独 INSERT 太慢

每秒 500 个标签,每条单独插入:

  • 500 次 SQL 解析
  • 500 次磁盘 IO
  • 500 次锁竞争

太慢了。

方案:队列 + 批量写入

┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │ PLC 采集线程 │ ──→ │ 异步队列 │ ──→ │ 数据库工作线程 │ │ (每秒一次) │ │ QQueue │ │ 每 200ms 处理 │ └─────────────┘ └─────────────┘ └──────┬───────┘ │ 批量 500 条 INSERT

具体做法:

  1. PLC 采集线程把数据放入异步队列
  2. 数据库工作线程独立运行,每 200ms 取出一批
  3. 最多 500 条一批,用预处理语句批量写入
  4. 成功写入 MySQL 后标记synced=1

500 次插入变成 1 次批量插入,性能提升数百倍。


主备切换:MySQL 挂了怎么办?

步骤 1: 尝试写入 MySQL 步骤 2: 失败 → 标记 MySQL 不可用 步骤 3: 下次写入直接走 SQLite 步骤 4: 每 30 秒检测 MySQL 是否恢复 步骤 5: 恢复后,自动同步积压数据

历史查询:自动选表

根据查询跨度,系统自动选择最优表:

时间跨度查询表原因
≤ 7 天data_second精度高
≤ 30 天data_minute数据量少,查询快
≤ 1 年data_hour天级精度足够
> 1 年data_day长期趋势

查询性能

场景数据量响应时间
查最近 1 小时3 万条< 100ms
查最近 7 天8.4 万条< 200ms
查最近 1 年438 万条< 2s

关键是聚合表数据量小,且命中索引。


数据清理:不越存越多

每日凌晨自动执行:

-- 清理 7 天前的秒级数据DELETEFROMdata_secondWHEREtimestamp<datetime('now','-7 days');-- 清理 30 天前的分钟级数据DELETEFROMdata_minuteWHEREtimestamp<datetime('now','-30 days');-- SQLite 回收空间VACUUM;

SQLite 文件超过 300MB 时发出警告,支持手动或配置自动清理策略。


总结

能力实现方式
不怕丢数据双数据库 + 自动降级
不怕数据量大异步批量写入
长期数据查得快多级聚合
标签随意增减EAV 模型

工业监控的数据存储,不是技术深度的竞赛,而是可靠性的较量。


参考资料

  • PLCMonitor 开源项目:https://github.com/freddiezhang1990/plcmonitor

标签#PLC#工业监控#上位机#MySQL#SQLite#数据库设计

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询