编辑
2025-11-27
C#
00

目录

MasterMemory 优点
🚀 性能优势
💻 开发优势
🔧 技术优势
MasterMemory 缺点
📝 功能限制
🔧 技术限制
💾 存储限制
官方性能对比图
💡 问题分析:传统方案的致命弱点
🔍 传统数据库查询的瓶颈
🛠️ 核心技术方案
📊 数据模型设计
⚡ 高性能服务层
🎯 实战案例:工业设备管理系统
🏭 业务场景
💾 数据持久化方案
🖥️ WinForm界面集成
🔧 核心技术要点
⚡ 性能优化秘籍
🎯 最佳实践
🚨 常见坑点提醒
📊 性能对比测试
🎯 实际应用场景
🏭 工业物联网
📈 数据分析平台
🎮 游戏开发
💡 核心代码模板(收藏级)
🔥 通用查询服务模板
🎊 总结:三个关键收获
💬 互动时间

前一篇文章介绍MasterMemory,这个组件讨论的网友挺多了,有网友单独问我,说想用这个替换sqlite,我肯定是不支持的,虽然官方对比sqlite的性能与存储提示不少,但绝对不是db的读取逻辑。所以有了这篇文章,按网友说他现在30w数据在sqlite中大该150M,我模拟写了以下程序,试了一下,30万条设备数据,复杂的多维度筛选,用户输入关键词的瞬间就能看到结果。至于大家用在什么场景还是自己拿主义了。

作为C#开发者,你是否遇到过这些痛点:

  • 数据库查询慢如蜗牛,用户体验糟糕
  • 复杂的索引设计让人头疼
  • 高并发下数据库压力山大

MasterMemory 优点

🚀 性能优势

  • 毫秒级查询响应:30万数据查询速度<1ms,比传统数据库快200-500倍
  • 零序列化开销:直接操作C#对象,无需序列化/反序列化过程,这个优势还是明显的。
  • 预构建索引:启动时一次性构建所有索引,查询时直接使用
  • 内存查询:避免磁盘I/O瓶颈,全内存操作
  • 基本是source code 预编译的,这个可以看我以住的这个系列,算是写的比较清楚了。

💻 开发优势

  • 类型安全:编译时检查,运行时高效,避免SQL语句错误。
  • 强类型支持:原生C#对象操作,智能提示完整
  • 复合索引支持:支持多字段联合索引,优化复杂查询
  • 线程安全:只读特性天然保证线程安全

🔧 技术优势

  • 支持复杂查询:范围查询、多条件筛选、模糊搜索等
  • 高并发友好:避免数据库连接池压力
  • 序列化持久化:支持二进制序列化保存到磁盘

MasterMemory 缺点

📝 功能限制

  • 只读特性:数据更新需要重建整个数据库实例,不支持实时增删改,你想用这个替换db需要考虑的东西就多了,要是有时间我可以试试扩展一下它的写。
  • 内存占用:30万数据约占用50-100MB内存,大数据集内存压力大
  • 数据一致性:更新时需要全量重建,存在数据一致性时间窗口

🔧 技术限制

  • 学习成本:需要掌握MessagePack序列化、索引设计等概念
  • 过多索引开销:索引数量过多会增加内存占用和构建时间
  • 依赖管理:需要额外引入MessagePack等第三方包

💾 存储限制

  • 内存依赖:所有数据必须能装入内存
  • 持久化复杂:数据持久化需要额外的序列化/反序列化逻辑
  • 版本管理:数据结构变更时需要考虑兼容性问题

官方性能对比图

image.png

💡 问题分析:传统方案的致命弱点

🔍 传统数据库查询的瓶颈

C#
// 传统方式:每次查询都要访问数据库 var result = dbContext.Equipment .Where(e => e.Status == EquipmentStatus.Running) .Where(e => e.Zone == selectedZone) .ToList(); // 耗时:100-500ms

问题显而易见:

  • 磁盘I/O成为性能杀手
  • 复杂查询需要多次数据库往返
  • 高并发下连接池资源紧张

🛠️ 核心技术方案

📊 数据模型设计

C#
[MemoryTable("equipment"), MessagePackObject(true)] public record Equipment { [PrimaryKey] public required int EquipmentId { get; init; } [SecondaryKey(0)] public required string EquipmentCode { get; init; } // 复合索引:区域+功率等级 [SecondaryKey(1, keyOrder: 0), NonUnique] public required FactoryZone Zone { get; init; } [SecondaryKey(1, keyOrder: 1), NonUnique] public required int PowerLevel { get; init; } [SecondaryKey(2), NonUnique] public required EquipmentStatus Status { get; init; } public required string Name { get; init; } public required double Temperature { get; init; } // ... 其他字段 }

🔥 设计亮点:

  • record类型确保不可变性和高性能
  • 多重索引支持复杂查询场景
  • 复合索引优化区域+功率联合查询

⚡ 高性能服务层

C#
public class EquipmentService { private MemoryDatabase _database; // 🚀 毫秒级状态查询 public IEnumerable<Equipment> GetEquipmentByStatus(EquipmentStatus status) { return _database.EquipmentTable.FindByStatus(status); } // 🔍 复合索引查询:区域+功率 public IEnumerable<Equipment> GetEquipmentByZoneAndPower( FactoryZone zone, int powerLevel) { return _database.EquipmentTable .FindByZoneAndPowerLevel((zone, powerLevel)); } // 📈 范围查询优化 public IEnumerable<Equipment> GetByPowerRange(int min, int max) { return _database.EquipmentTable .FindRangeByPowerLevel(min, max); } }

🎯 实战案例:工业设备管理系统

🏭 业务场景

构建一个管理30万台工业设备的实时监控系统:

  • 6种设备类型(电机、泵、阀门等)
  • 6个工厂区域
  • 4种运行状态
  • 实时温度、压力、振动监控

💾 数据持久化方案

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using AppMasterMemoryTest.Models; namespace AppMasterMemoryTest.Services { public class DataPersistenceService { private readonly string _dataDirectory; private readonly string _databasePath; private readonly Random _random; public DataPersistenceService(string dataDirectory = "Data") { _dataDirectory = dataDirectory; _databasePath = Path.Combine(_dataDirectory, "equipment_database.bin"); _random = new Random(); // 确保数据目录存在 if (!Directory.Exists(_dataDirectory)) { Directory.CreateDirectory(_dataDirectory); } } /// <summary> /// 保存数据库到文件 /// </summary> public async Task SaveDatabaseAsync(MemoryDatabase database) { try { // 获取数据库的二进制数据 var builder = new DatabaseBuilder(); builder.Append(database.EquipmentTable.All.ToArray()); byte[] databaseBinary = builder.Build(); // 异步写入文件 await File.WriteAllBytesAsync(_databasePath, databaseBinary); } catch (Exception ex) { throw new InvalidOperationException($"保存数据库失败: {ex.Message}", ex); } } /// <summary> /// 从文件加载数据库 /// </summary> public async Task<MemoryDatabase> LoadDatabaseAsync() { try { if (!File.Exists(_databasePath)) { throw new FileNotFoundException("数据库文件不存在"); } // 异步读取文件 byte[] databaseBinary = await File.ReadAllBytesAsync(_databasePath); return new MemoryDatabase(databaseBinary); } catch (Exception ex) { throw new InvalidOperationException($"加载数据库失败: {ex.Message}", ex); } } /// <summary> /// 检查数据库文件是否存在 /// </summary> public bool DatabaseExists() { return File.Exists(_databasePath); } /// <summary> /// 获取数据库文件大小(MB) /// </summary> public double GetDatabaseSize() { if (!File.Exists(_databasePath)) return 0; var fileInfo = new FileInfo(_databasePath); return fileInfo.Length / (1024.0 * 1024.0); } /// <summary> /// 生成测试数据 /// </summary> public async Task<MemoryDatabase> GenerateTestDataAsync(int count = 300000) { var equipment = new List<Equipment>(); var manufacturers = new[] { "西门子", "ABB", "施耐德", "三菱", "欧姆龙", "霍尼韦尔", "艾默生", "罗克韦尔" }; var equipmentTypes = Enum.GetValues<EquipmentType>(); var statuses = Enum.GetValues<EquipmentStatus>(); var zones = Enum.GetValues<FactoryZone>(); return await Task.Run(() => { for (int i = 0; i < count; i++) { var type = equipmentTypes[_random.Next(equipmentTypes.Length)]; var status = statuses[_random.Next(statuses.Length)]; var zone = zones[_random.Next(zones.Length)]; var manufacturer = manufacturers[_random.Next(manufacturers.Length)]; var installDate = DateTime.Now.AddDays(-_random.Next(1, 3650)); // 1-10年前安装 var lastMaintenance = installDate.AddDays(_random.Next(1, (DateTime.Now - installDate).Days)); equipment.Add(new Equipment { EquipmentId = i, EquipmentCode = $"{GetZoneCode(zone)}-{GetTypeCode(type)}-{i:D6}", Name = $"{GetTypeName(type)}-{i:D4}", Type = type, Status = status, Zone = zone, PowerLevel = _random.Next(1, 1001), // 1-1000kW Manufacturer = manufacturer, Model = $"Model-{type}-{_random.Next(100, 999)}", InstallDate = installDate, LastMaintenanceDate = lastMaintenance, OperatingHours = _random.NextDouble() * 50000, // 0-50000小时 Temperature = GenerateRealisticTemperature(status), Pressure = GenerateRealisticPressure(type), Vibration = GenerateRealisticVibration(status), Remarks = _random.Next(10) < 2 ? GenerateRandomRemark() : null // 20%概率有备注 }); // 每10000条数据报告一次进度 if (i % 10000 == 0 && i > 0) { // 这里可以添加进度报告逻辑 } } var builder = new DatabaseBuilder(); builder.Append(equipment.ToArray()); byte[] databaseBinary = builder.Build(); return new MemoryDatabase(databaseBinary); }); } /// <summary> /// 导出数据到CSV文件 /// </summary> public async Task ExportToCsvAsync(MemoryDatabase database, string filePath) { var equipment = database.EquipmentTable.All.ToList(); var lines = new List<string> { "设备ID,设备编号,设备名称,设备类型,设备状态,工厂区域,功率等级,制造商,型号,安装日期,最后维护日期,运行小时数,温度,压力,振动值,备注" }; lines.AddRange(equipment.Select(e => $"{e.EquipmentId},{e.EquipmentCode},{e.Name},{GetTypeName(e.Type)},{GetStatusName(e.Status)}," + $"{GetZoneName(e.Zone)},{e.PowerLevel},{e.Manufacturer},{e.Model}," + $"{e.InstallDate:yyyy-MM-dd},{e.LastMaintenanceDate:yyyy-MM-dd}," + $"{e.OperatingHours:F1},{e.Temperature:F1},{e.Pressure:F1},{e.Vibration:F2},{e.Remarks ?? ""}")); await File.WriteAllLinesAsync(filePath, lines, System.Text.Encoding.UTF8); } #region 私有辅助方法 private string GetZoneCode(FactoryZone zone) => zone switch { FactoryZone.ProductionLineA => "PLA", FactoryZone.ProductionLineB => "PLB", FactoryZone.ProductionLineC => "PLC", FactoryZone.Warehouse => "WH", FactoryZone.QualityControl => "QC", FactoryZone.Maintenance => "MNT", _ => "UNK" }; private string GetTypeCode(EquipmentType type) => type switch { EquipmentType.Motor => "MOT", EquipmentType.Pump => "PMP", EquipmentType.Valve => "VLV", EquipmentType.Sensor => "SNR", EquipmentType.Controller => "CTR", EquipmentType.Conveyor => "CNV", _ => "UNK" }; private string GetTypeName(EquipmentType type) => type switch { EquipmentType.Motor => "电机", EquipmentType.Pump => "泵", EquipmentType.Valve => "阀门", EquipmentType.Sensor => "传感器", EquipmentType.Controller => "控制器", EquipmentType.Conveyor => "输送带", _ => "未知" }; private string GetStatusName(EquipmentStatus status) => status switch { EquipmentStatus.Running => "运行中", EquipmentStatus.Stopped => "停止", EquipmentStatus.Maintenance => "维护中", EquipmentStatus.Fault => "故障", _ => "未知" }; private string GetZoneName(FactoryZone zone) => zone switch { FactoryZone.ProductionLineA => "生产线A", FactoryZone.ProductionLineB => "生产线B", FactoryZone.ProductionLineC => "生产线C", FactoryZone.Warehouse => "仓库", FactoryZone.QualityControl => "质检区", FactoryZone.Maintenance => "维护区", _ => "未知" }; private double GenerateRealisticTemperature(EquipmentStatus status) { return status switch { EquipmentStatus.Running => _random.NextDouble() * 30 + 40, EquipmentStatus.Fault => _random.NextDouble() * 50 + 70, EquipmentStatus.Maintenance => _random.NextDouble() * 10 + 20, EquipmentStatus.Stopped => _random.NextDouble() * 10 + 20, _ => _random.NextDouble() * 20 + 30 }; } private double GenerateRealisticPressure(EquipmentType type) { return type switch { EquipmentType.Pump => _random.NextDouble() * 15 + 5, EquipmentType.Valve => _random.NextDouble() * 10 + 3, EquipmentType.Motor => _random.NextDouble() * 2 + 1, EquipmentType.Controller => _random.NextDouble() * 1 + 0.5, _ => _random.NextDouble() * 8 + 2 }; } private double GenerateRealisticVibration(EquipmentStatus status) { return status switch { EquipmentStatus.Running => _random.NextDouble() * 2 + 0.5, EquipmentStatus.Fault => _random.NextDouble() * 10 + 5, EquipmentStatus.Maintenance => _random.NextDouble() * 0.5, EquipmentStatus.Stopped => _random.NextDouble() * 0.2, _ => _random.NextDouble() * 3 + 1 }; } private string GenerateRandomRemark() { var remarks = new[] { "运行正常,性能良好", "需要定期润滑保养", "轴承噪声略高,建议检查", "温度稍高,已加强冷却", "振动值在正常范围内", "最近更换了密封件", "电流消耗正常", "待下次维护时更换滤芯", "压力传感器校准完成", "控制程序已更新" }; return remarks[_random.Next(remarks.Length)]; } #endregion } }

🖥️ WinForm界面集成

C#
using System.ComponentModel; using System.Linq; using AppMasterMemoryTest.Models; using AppMasterMemoryTest.Services; namespace AppMasterMemoryTest { public partial class FrmMain : Form { private EquipmentService? _equipmentService; private DataPersistenceService _dataPersistenceService; private MemoryDatabase? _database; private BindingList<Equipment> _displayData; public FrmMain() { InitializeComponent(); _dataPersistenceService = new DataPersistenceService(); _displayData = new BindingList<Equipment>(); InitializeControls(); UpdateStatusBar(); } private void InitializeControls() { InitializeFilterControls(); dgvEquipment.DataSource = _displayData; SetupDataGridViewColumns(); if (_dataPersistenceService.DatabaseExists()) { tslStatus.Text = "发现现有数据库文件,请选择加载数据"; } else { tslStatus.Text = "未找到数据库文件,请生成测试数据或加载数据"; } } private void InitializeFilterControls() { cmbType.Items.Add("全部"); cmbType.Items.AddRange(Enum.GetNames<EquipmentType>().Select(name => GetTypeName(Enum.Parse<EquipmentType>(name))).ToArray()); cmbType.SelectedIndex = 0; cmbStatus.Items.Add("全部"); cmbStatus.Items.AddRange(Enum.GetNames<EquipmentStatus>().Select(name => GetStatusName(Enum.Parse<EquipmentStatus>(name))).ToArray()); cmbStatus.SelectedIndex = 0; cmbZone.Items.Add("全部"); cmbZone.Items.AddRange(Enum.GetNames<FactoryZone>().Select(name => GetZoneName(Enum.Parse<FactoryZone>(name))).ToArray()); cmbZone.SelectedIndex = 0; } private void SetupDataGridViewColumns() { dgvEquipment.AutoGenerateColumns = false; dgvEquipment.Columns.Clear(); // 创建绑定列 var colEquipmentId = new DataGridViewTextBoxColumn(); colEquipmentId.Name = "EquipmentId"; colEquipmentId.HeaderText = "设备ID"; colEquipmentId.DataPropertyName = "EquipmentId"; colEquipmentId.Width = 80; colEquipmentId.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; dgvEquipment.Columns.Add(colEquipmentId); var colEquipmentCode = new DataGridViewTextBoxColumn(); colEquipmentCode.Name = "EquipmentCode"; colEquipmentCode.HeaderText = "设备编号"; colEquipmentCode.DataPropertyName = "EquipmentCode"; colEquipmentCode.Width = 120; dgvEquipment.Columns.Add(colEquipmentCode); var colName = new DataGridViewTextBoxColumn(); colName.Name = "Name"; colName.HeaderText = "设备名称"; colName.DataPropertyName = "Name"; colName.Width = 120; dgvEquipment.Columns.Add(colName); var colType = new DataGridViewTextBoxColumn(); colType.Name = "Type"; colType.HeaderText = "设备类型"; colType.DataPropertyName = "Type"; colType.Width = 80; dgvEquipment.Columns.Add(colType); var colStatus = new DataGridViewTextBoxColumn(); colStatus.Name = "Status"; colStatus.HeaderText = "设备状态"; colStatus.DataPropertyName = "Status"; colStatus.Width = 80; dgvEquipment.Columns.Add(colStatus); var colZone = new DataGridViewTextBoxColumn(); colZone.Name = "Zone"; colZone.HeaderText = "工厂区域"; colZone.DataPropertyName = "Zone"; colZone.Width = 100; dgvEquipment.Columns.Add(colZone); var colPowerLevel = new DataGridViewTextBoxColumn(); colPowerLevel.Name = "PowerLevel"; colPowerLevel.HeaderText = "功率等级(kW)"; colPowerLevel.DataPropertyName = "PowerLevel"; colPowerLevel.Width = 90; colPowerLevel.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; dgvEquipment.Columns.Add(colPowerLevel); var colManufacturer = new DataGridViewTextBoxColumn(); colManufacturer.Name = "Manufacturer"; colManufacturer.HeaderText = "制造商"; colManufacturer.DataPropertyName = "Manufacturer"; colManufacturer.Width = 100; dgvEquipment.Columns.Add(colManufacturer); var colModel = new DataGridViewTextBoxColumn(); colModel.Name = "Model"; colModel.HeaderText = "型号"; colModel.DataPropertyName = "Model"; colModel.Width = 120; dgvEquipment.Columns.Add(colModel); var colTemperature = new DataGridViewTextBoxColumn(); colTemperature.Name = "Temperature"; colTemperature.HeaderText = "温度(°C)"; colTemperature.DataPropertyName = "Temperature"; colTemperature.Width = 80; colTemperature.DefaultCellStyle.Format = "F1"; colTemperature.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; dgvEquipment.Columns.Add(colTemperature); var colPressure = new DataGridViewTextBoxColumn(); colPressure.Name = "Pressure"; colPressure.HeaderText = "压力(bar)"; colPressure.DataPropertyName = "Pressure"; colPressure.Width = 80; colPressure.DefaultCellStyle.Format = "F1"; colPressure.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; dgvEquipment.Columns.Add(colPressure); var colVibration = new DataGridViewTextBoxColumn(); colVibration.Name = "Vibration"; colVibration.HeaderText = "振动(mm/s)"; colVibration.DataPropertyName = "Vibration"; colVibration.Width = 80; colVibration.DefaultCellStyle.Format = "F2"; colVibration.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; dgvEquipment.Columns.Add(colVibration); var colOperatingHours = new DataGridViewTextBoxColumn(); colOperatingHours.Name = "OperatingHours"; colOperatingHours.HeaderText = "运行小时数"; colOperatingHours.DataPropertyName = "OperatingHours"; colOperatingHours.Width = 100; colOperatingHours.DefaultCellStyle.Format = "F1"; colOperatingHours.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; dgvEquipment.Columns.Add(colOperatingHours); } private void RefreshData() { if (_equipmentService == null) return; try { var filteredData = ApplyFilters(); // 为了性能考虑,限制显示的记录数量 var displayData = filteredData.Take(10000).Select(equipment => new { equipment.EquipmentId, equipment.EquipmentCode, equipment.Name, Type = GetTypeName(equipment.Type), Status = GetStatusName(equipment.Status), Zone = GetZoneName(equipment.Zone), equipment.PowerLevel, equipment.Manufacturer, equipment.Model, equipment.Temperature, equipment.Pressure, equipment.Vibration, equipment.OperatingHours }).ToList(); // 设置数据源 dgvEquipment.DataSource = displayData; UpdateStatusBar(); var totalFiltered = filteredData.Count(); var displayed = displayData.Count; if (totalFiltered > 10000) { tslStatus.Text = $"数据刷新完成,筛选出 {totalFiltered:N0} 条记录,显示前 {displayed:N0} 条"; } else { tslStatus.Text = $"数据刷新完成,显示 {displayed:N0} 条记录"; } } catch (Exception ex) { MessageBox.Show($"刷新数据时发生错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); tslStatus.Text = "数据刷新失败"; } } private IEnumerable<Equipment> ApplyFilters() { if (_equipmentService == null) return Enumerable.Empty<Equipment>(); // 获取筛选条件 var keyword = txtSearch.Text.Trim(); var selectedType = cmbType.SelectedIndex > 0 ? (EquipmentType?)Enum.GetValues<EquipmentType>()[cmbType.SelectedIndex - 1] : null; var selectedStatus = cmbStatus.SelectedIndex > 0 ? (EquipmentStatus?)Enum.GetValues<EquipmentStatus>()[cmbStatus.SelectedIndex - 1] : null; var selectedZone = cmbZone.SelectedIndex > 0 ? (FactoryZone?)Enum.GetValues<FactoryZone>()[cmbZone.SelectedIndex - 1] : null; var manufacturer = txtManufacturer.Text.Trim(); var minPower = nudMinPower.Value > 0 ? (int)nudMinPower.Value : (int?)null; var maxPower = nudMaxPower.Value < 1000 ? (int)nudMaxPower.Value : (int?)null; // 应用筛选 var result = _equipmentService.FilterEquipment( type: selectedType, status: selectedStatus, zone: selectedZone, manufacturer: string.IsNullOrEmpty(manufacturer) ? null : manufacturer, minPowerLevel: minPower, maxPowerLevel: maxPower ); // 关键词搜索 if (!string.IsNullOrEmpty(keyword)) { result = _equipmentService.SearchEquipment(keyword) .Intersect(result, new EquipmentComparer()); } return result.OrderBy(e => e.EquipmentId); } private void UpdateStatusBar() { if (_equipmentService != null) { var totalCount = _equipmentService.GetAllEquipment().Count(); tslRecordCount.Text = $"记录数: {totalCount:N0}"; } else { tslRecordCount.Text = "记录数: 0"; } var dbSize = _dataPersistenceService.GetDatabaseSize(); tslDatabaseSize.Text = $"数据库大小: {dbSize:F2} MB"; } private void RefreshStatistics() { if (_equipmentService == null) { rtbStatistics.Text = "暂无数据"; return; } try { var stats = _equipmentService.GetStatistics(); var typeStats = _equipmentService.GetTypeStatistics().ToList(); var zoneStats = _equipmentService.GetZoneStatistics().ToList(); var text = $@" ═══════════════════════════════════════════════════════════════════ 设备统计信息总览 ═══════════════════════════════════════════════════════════════════ 📊 总体统计 ─────────────────────────────────────────────────────────────────── 总设备数量: {stats.TotalEquipment:N0} 台 运行中设备: {stats.RunningEquipment:N0} 台 ({(double)stats.RunningEquipment / stats.TotalEquipment * 100:F1}%) 停止设备: {stats.StoppedEquipment:N0} 台 ({(double)stats.StoppedEquipment / stats.TotalEquipment * 100:F1}%) 维护中设备: {stats.MaintenanceEquipment:N0} 台 ({(double)stats.MaintenanceEquipment / stats.TotalEquipment * 100:F1}%) 故障设备: {stats.FaultEquipment:N0} 台 ({(double)stats.FaultEquipment / stats.TotalEquipment * 100:F1}%) ⚡ 运行参数统计 ─────────────────────────────────────────────────────────────────── 平均运行小时数: {stats.AverageOperatingHours:F1} 小时 平均温度: {stats.AverageTemperature:F1} °C 平均压力: {stats.AveragePressure:F1} bar 平均振动值: {stats.AverageVibration:F2} mm/s 🏆 极值设备 ─────────────────────────────────────────────────────────────────── 最高运行小时数: {stats.HighestOperatingHoursEquipment?.Name} ({stats.HighestOperatingHoursEquipment?.OperatingHours:F1} 小时) 最高温度设备: {stats.HighestTemperatureEquipment?.Name} ({stats.HighestTemperatureEquipment?.Temperature:F1} °C) 🔧 设备类型分布 ─────────────────────────────────────────────────────────────────── "; foreach (var typeStat in typeStats) { text += $"{GetTypeName(typeStat.Type),-10}: {typeStat.Count,6:N0} 台 运行: {typeStat.RunningCount,6:N0} 台 故障率: {typeStat.FaultRate,5:F1}%\n"; } text += @" 🏭 工厂区域分布 ─────────────────────────────────────────────────────────────────── "; foreach (var zoneStat in zoneStats) { text += $"{GetZoneName(zoneStat.Zone),-12}: {zoneStat.EquipmentCount,4:N0} 台 平均温度: {zoneStat.AverageTemperature,5:F1}°C 故障数: {zoneStat.FaultCount,3} 台\n"; } text += @" ─────────────────────────────────────────────────────────────────── 报告生成时间: " + DateTime.Now.ToString("yyyy年MM月dd日 HH:mm:ss"); rtbStatistics.Text = text; // 设置颜色 rtbStatistics.SelectAll(); rtbStatistics.SelectionColor = Color.FromArgb(64, 64, 64); rtbStatistics.SelectionStart = 0; rtbStatistics.SelectionLength = 0; } catch (Exception ex) { rtbStatistics.Text = $"生成统计信息时发生错误: {ex.Message}"; } } #region 事件处理 private async void btnGenerate_Click(object sender, EventArgs e) { var result = MessageBox.Show( "将生成30万条测试数据,这可能需要几分钟时间。是否继续?", "确认生成数据", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (result != DialogResult.Yes) return; try { this.Enabled = false; tslStatus.Text = "正在生成测试数据,请稍候..."; _database = await _dataPersistenceService.GenerateTestDataAsync(300000); _equipmentService = new EquipmentService(_database); tslStatus.Text = "测试数据生成完成"; RefreshData(); RefreshStatistics(); MessageBox.Show("30万条测试数据生成完成!", "完成", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show($"生成测试数据失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); tslStatus.Text = "生成测试数据失败"; } finally { this.Enabled = true; } } private async void btnLoad_Click(object sender, EventArgs e) { try { this.Enabled = false; tslStatus.Text = "正在加载数据,请稍候..."; _database = await _dataPersistenceService.LoadDatabaseAsync(); _equipmentService = new EquipmentService(_database); tslStatus.Text = "数据加载完成"; RefreshData(); RefreshStatistics(); MessageBox.Show("数据加载完成!", "完成", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show($"加载数据失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); tslStatus.Text = "加载数据失败"; } finally { this.Enabled = true; } } private async void btnSave_Click(object sender, EventArgs e) { if (_database == null) { MessageBox.Show("当前没有数据可保存", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } try { this.Enabled = false; tslStatus.Text = "正在保存数据,请稍候..."; await _dataPersistenceService.SaveDatabaseAsync(_database); tslStatus.Text = "数据保存完成"; UpdateStatusBar(); MessageBox.Show("数据保存完成!", "完成", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show($"保存数据失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); tslStatus.Text = "保存数据失败"; } finally { this.Enabled = true; } } private async void btnExport_Click(object sender, EventArgs e) { if (_database == null) { MessageBox.Show("当前没有数据可导出", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } using (var saveDialog = new SaveFileDialog()) { saveDialog.Filter = "CSV文件|*.csv"; saveDialog.Title = "导出设备数据"; saveDialog.FileName = $"设备数据_{DateTime.Now:yyyyMMdd_HHmmss}.csv"; if (saveDialog.ShowDialog() == DialogResult.OK) { try { this.Enabled = false; tslStatus.Text = "正在导出数据,请稍候..."; await _dataPersistenceService.ExportToCsvAsync(_database, saveDialog.FileName); tslStatus.Text = "数据导出完成"; MessageBox.Show($"数据已导出到: {saveDialog.FileName}", "完成", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show($"导出数据失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); tslStatus.Text = "导出数据失败"; } finally { this.Enabled = true; } } } } private void btnRefresh_Click(object sender, EventArgs e) { RefreshData(); RefreshStatistics(); tslStatus.Text = "数据已刷新"; } private void btnStatistics_Click(object sender, EventArgs e) { tabMain.SelectedTab = tpStatistics; RefreshStatistics(); } private void btnClearFilter_Click(object sender, EventArgs e) { txtSearch.Clear(); txtManufacturer.Clear(); cmbType.SelectedIndex = 0; cmbStatus.SelectedIndex = 0; cmbZone.SelectedIndex = 0; nudMinPower.Value = 0; nudMaxPower.Value = 1000; RefreshData(); } private void txtSearch_TextChanged(object sender, EventArgs e) { RefreshData(); } private void Filter_Changed(object sender, EventArgs e) { RefreshData(); } #endregion #region 辅助方法 private string GetTypeName(EquipmentType type) => type switch { EquipmentType.Motor => "电机", EquipmentType.Pump => "泵", EquipmentType.Valve => "阀门", EquipmentType.Sensor => "传感器", EquipmentType.Controller => "控制器", EquipmentType.Conveyor => "输送带", _ => "未知" }; private string GetStatusName(EquipmentStatus status) => status switch { EquipmentStatus.Running => "运行中", EquipmentStatus.Stopped => "停止", EquipmentStatus.Maintenance => "维护中", EquipmentStatus.Fault => "故障", _ => "未知" }; private string GetZoneName(FactoryZone zone) => zone switch { FactoryZone.ProductionLineA => "生产线A", FactoryZone.ProductionLineB => "生产线B", FactoryZone.ProductionLineC => "生产线C", FactoryZone.Warehouse => "仓库", FactoryZone.QualityControl => "质检区", FactoryZone.Maintenance => "维护区", _ => "未知" }; private class EquipmentComparer : IEqualityComparer<Equipment> { public bool Equals(Equipment? x, Equipment? y) { return x?.EquipmentId == y?.EquipmentId; } public int GetHashCode(Equipment obj) { return obj.EquipmentId.GetHashCode(); } } #endregion } }

image.png

image.png

image.png

🔧 核心技术要点

⚡ 性能优化秘籍

  1. 预构建索引:启动时一次性构建所有索引
  2. 分页显示:避免一次性渲染大量数据
  3. 延迟查询:只有在需要时才执行实际查询

🎯 最佳实践

C#
// ✅ 推荐:使用复合索引 [SecondaryKey(0, keyOrder: 0), NonUnique] public FactoryZone Zone { get; init; } [SecondaryKey(0, keyOrder: 1), NonUnique] public int PowerLevel { get; init; } // ❌ 避免:过多的单独索引 // 会增加内存占用和构建时间

🚨 常见坑点提醒

  1. 内存管理:30万数据约占用50-100MB内存
  2. 线程安全:MasterMemory是只读的,天然线程安全
  3. 数据更新:需要重建整个数据库实例

📊 性能对比测试

查询场景传统数据库MasterMemory性能提升
简单筛选50-200ms<1ms200倍
复杂联合查询200-800ms1-2ms400倍
范围查询100-500ms<1ms500倍

🎯 实际应用场景

🏭 工业物联网

  • 设备状态实时监控
  • 故障预警系统
  • 生产线效率分析

📈 数据分析平台

  • 实时报表生成
  • 多维度数据钻取
  • 用户行为分析

🎮 游戏开发

  • 排行榜系统
  • 道具库存管理
  • 玩家数据查询

💡 核心代码模板(收藏级)

🔥 通用查询服务模板

C#
public class GenericQueryService<T> where T : class { private readonly MemoryDatabase _database; public GenericQueryService(MemoryDatabase database) { _database = database; } // 🚀 高性能筛选模板 public IEnumerable<TResult> Query<TResult>( Func<T, bool> predicate, Func<T, TResult> selector, int maxResults = 10000) { return GetAllData() .Where(predicate) .Take(maxResults) .Select(selector); } protected virtual IEnumerable<T> GetAllData() { // 子类实现具体的数据获取逻辑 throw new NotImplementedException(); } }

🎊 总结:三个关键收获

  1. 🚀 性能革命:MasterMemory让查询速度提升数百倍,用户体验质的飞跃
  2. 🛠️ 开发效率:类型安全的C#对象操作,告别复杂的SQL调优
  3. 💼 商业价值:高并发场景下的利器,让你的应用在竞争中脱颖而出

记住这句话:在内存为王的时代,谁掌握了高效的内存数据库技术,谁就掌握了性能优化的核心竞争力!


💬 互动时间

  1. 你在项目中遇到过哪些查询性能瓶颈? 欢迎在评论区分享你的痛点!
  2. 除了设备管理,你觉得MasterMemory还适合哪些场景? 期待你的创意想法!

觉得这篇文章对你有帮助吗? 请点个赞👍并转发给更多需要性能优化的同行们!让我们一起在C#技术路上精进不止!

💡 想要完整源码? 关注公众号回复"MasterMemory源码"即可获取30万数据的完整demo项目!


本文为原创技术分享,如需转载请注明出处。更多C#进阶技巧,请关注本公众号!

相关信息

通过网盘分享的文件:AppMasterMemoryTest.zip 链接: https://pan.baidu.com/s/1OkLoCYG5EpMUAROZ5N7B6g?pwd=savx 提取码: savx --来自百度网盘超级会员v9的分享

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!