C#/.NET 上位机项目实战:温室监控系统 10万条数据并发与数据库 3 层优化方案
1. 项目背景与挑战
在工业自动化领域,温室监控系统是典型的物联网应用场景。这类系统通常需要处理来自大量传感器的实时数据,同时还要保证控制指令的及时响应。我们最近完成的一个项目就面临这样的挑战:系统需要管理10个温室,每个温室包含21个采集节点和9个控制节点,每5分钟采集一次数据,一个月内产生了近10万条记录。
这类系统的核心难点在于:
- 高并发数据采集:多个温室节点同时上传数据
- 实时性要求:控制指令需要在毫秒级响应
- 数据可靠性:采集数据不能丢失,控制指令必须准确执行
- 系统稳定性:需要7×24小时不间断运行
2. 系统架构设计
2.1 整体架构
我们采用了典型的三层架构设计:
[硬件节点层] ←LoRa→ [通信网关层] ←TCP/IP→ [上位机应用层]硬件节点层:
- 温度采集节点(空气/土壤)
- 湿度采集节点
- 光照强度采集节点
- 通风/灌溉/遮阳控制节点
通信网关层:
- 每个温室部署一个中央网关
- 采用星型拓扑结构管理本温室所有节点
- 负责协议转换和数据预处理
上位机应用层:
- 数据接收与处理模块
- 数据库存储模块
- 用户界面模块
- 报警与控制模块
2.2 通信协议选择
在通信协议选择上,我们做了以下技术决策:
| 通信场景 | 协议选择 | 理由 |
|---|---|---|
| 节点↔网关 | LoRa | 低功耗、室内覆盖足够、免频段费用 |
| 网关↔上位机 | RS-485 | 支持多站、全双工、10Mbps速率 |
| 客户端↔服务端 | TCP | 可靠传输、指令互斥需求 |
// TCP服务端示例代码 TcpListener server = new TcpListener(IPAddress.Any, 8080); server.Start(); while (true) { TcpClient client = server.AcceptTcpClient(); ThreadPool.QueueUserWorkItem(HandleClient, client); } void HandleClient(object obj) { using (TcpClient client = (TcpClient)obj) using (NetworkStream stream = client.GetStream()) { // 处理数据接收与响应 } }3. 数据库优化方案
3.1 数据特点分析
首先我们需要了解系统的数据特征:
- 数据量:每月约10万条
- 单条数据大小:约102字节
- 写入频率:每5分钟批量写入
- 读取需求:实时监控+历史查询
基于SQL Server的存储计算:
-- 表结构设计 CREATE TABLE GreenhouseData ( Id BIGINT PRIMARY KEY IDENTITY, GreenhouseId TINYINT NOT NULL, -- 1字节 Temperature1 FLOAT, -- 4字节 ×8 Temperature2 FLOAT, -- ...其他温度字段 Humidity1 FLOAT, -- 4字节 ×8 -- ...其他湿度字段 LightIntensity1 FLOAT, -- 4字节 ×5 -- ...其他光照字段 ControlStatus1 TINYINT, -- 1字节 ×9 -- ...其他控制状态字段 RecordTime DATETIME NOT NULL -- 8字节 );3.2 三层优化策略
第一层:架构优化
读写分离
- 主库负责写入
- 从库负责查询
- 通过SQL Server的Always On实现
分库分表
- 按温室ID水平分片
- 冷热数据分离(近期数据与历史数据)
缓存层
- Redis缓存实时数据
- 内存缓存常用查询结果
第二层:数据库配置优化
-- 优化配置示例 ALTER DATABASE GreenhouseDB SET AUTO_UPDATE_STATISTICS ON; ALTER DATABASE GreenhouseDB SET ALLOW_SNAPSHOT_ISOLATION ON; -- 索引优化 CREATE NONCLUSTERED INDEX IX_GreenhouseData_Time ON GreenhouseData(GreenhouseId, RecordTime);第三层:代码级优化
- 批量插入优化
// 使用StringBuilder构建批量插入SQL StringBuilder sb = new StringBuilder(); sb.Append("INSERT INTO GreenhouseData VALUES "); for (int i = 0; i < dataList.Count; i++) { if (i > 0) sb.Append(","); sb.Append($"({dataList[i].GreenhouseId}, ...)"); } using (SqlCommand cmd = new SqlCommand(sb.ToString(), connection)) { cmd.ExecuteNonQuery(); }- 事务优化
// 手动控制事务提交 using (SqlTransaction transaction = connection.BeginTransaction()) { try { // 执行多个操作 transaction.Commit(); } catch { transaction.Rollback(); throw; } }- 连接池配置
<!-- 在配置文件中优化连接池 --> <connectionStrings> <add name="GreenhouseDB" connectionString="Data Source=...;Max Pool Size=100;Min Pool Size=10;..." providerName="System.Data.SqlClient" /> </connectionStrings>4. 性能测试与结果
我们设计了以下测试场景:
| 测试场景 | 并发量 | 平均响应时间 | 成功率 |
|---|---|---|---|
| 单温室数据写入 | 100次/秒 | 23ms | 100% |
| 多温室数据写入 | 500次/秒 | 89ms | 99.8% |
| 控制指令下发 | 50次/秒 | 15ms | 100% |
| 历史数据查询 | 复杂查询 | 1.2s | 100% |
优化前后对比:
| 指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 写入吞吐量 | 200条/秒 | 1200条/秒 | 6倍 |
| CPU使用率 | 85% | 45% | 降低47% |
| 查询响应时间 | 3.5s | 0.8s | 77%提升 |
5. 经验总结与实用技巧
在实际开发中,我们积累了一些有价值的经验:
- 连接管理:确保及时关闭数据库连接,使用
using语句块 - 参数化查询:始终使用参数化查询防止SQL注入
- 异常处理:对数据库操作进行完善的异常处理和重试机制
- 监控指标:关键指标包括连接池使用率、查询耗时、锁等待时间
// 实用的监控代码示例 PerformanceCounter pc = new PerformanceCounter(); pc.CategoryName = "SQLServer:General Statistics"; pc.CounterName = "User Connections"; float connections = pc.NextValue(); // 记录到日志或监控系统 Logger.Info($"当前数据库连接数: {connections}");对于上位机开发,还有一些通用建议:
- 使用
async/await避免UI线程阻塞 - 采用MVVM模式提高代码可维护性
- 实现合理的日志记录策略
- 考虑使用Dapper等轻量级ORM
// 使用Dapper的示例 public IEnumerable<GreenhouseData> GetRecentData(int greenhouseId, int hours) { using (var conn = new SqlConnection(ConnectionString)) { return conn.Query<GreenhouseData>( "SELECT * FROM GreenhouseData WHERE GreenhouseId = @id AND RecordTime > @time", new { id = greenhouseId, time = DateTime.Now.AddHours(-hours) }); } }在项目后期,我们还实现了以下高级功能:
- 数据压缩:对历史数据采用压缩存储
- 智能预警:基于机器学习算法实现异常检测
- 远程升级:OTA方式更新网关固件
- 数据可视化:使用WPF实现丰富的图表展示
这个项目的成功实施证明了C#/.NET在工业自动化领域的强大能力。通过合理的架构设计和持续的优化,我们构建了一个稳定可靠、高性能的温室监控系统,为类似项目提供了可复用的技术方案。