.NET Core 6向126列表插入650万条记录的内存溢出问题求解
解决大批量数据插入的内存溢出与性能问题
核心问题分析
当前代码的内存压力主要来自:
- 一次性加载整个.cat文件到
List<string> lines,650万行直接占用大量内存 - 使用
AddRange让DbContext跟踪实体,额外消耗内存 - 15万条的批量大小过大,单批次实体(126列)占用内存超出容器限制
- 容器未配置内存请求/限制,触发OOM被系统强制重启
具体优化方案
1. 流式读取文件,避免一次性加载全量数据
放弃ReadAllLines一次性加载文件的方式,改用File.ReadLines流式逐行读取,从根源降低初始内存占用:
// 替换原方法的List<string> lines参数,改为接收文件路径 public void InsertCatsFromFileWithBulkInsertAzure(string filePath, string name, string username, Guid id, DateTime? dateCat) { DateTime now = DateTime.Now; int batchSize = 30000; // 调小批量大小,建议2-5万,根据内存情况调整 List<Cat> data = new List<Cat>(batchSize); // 初始化时指定容量,减少内存分配次数 var executionStrategy = _context.Database.CreateExecutionStrategy(); executionStrategy.Execute(() => { foreach (var line in File.ReadLines(filePath).Skip(1)) // Skip(1)跳过表头行 { try { Cat parsedCat = ParseCat(line); // 调整ParseCat方法,直接接收单条line参数 parsedCat.CatGeneralId = id; parsedCat.Date = now; parsedCat.DateCat = dateCat; data.Add(parsedCat); if (data.Count == batchSize) { // 用BulkInsert替代AddRange+BulkSaveChanges,直接跳过实体跟踪 _context.BulkInsert(data, options => { options.BatchSize = batchSize; options.DisableTracking = true; // 禁用实体跟踪,节省内存 options.EnableStreaming = true; // 流式写入数据库,减少内存占用 }); data.Clear(); data.TrimExcess(); // 释放列表多余的内存空间 } } catch (Exception ex) { string type = line.Substring(0, 2); string cadastralParcel = line.Substring(30, 14); Console.WriteLine($"错误:地籍编号{cadastralParcel},类型{type},错误信息:{ex.Message}"); } } // 处理剩余不足一批的数据 if (data.Count > 0) { _context.BulkInsert(data, options => { options.DisableTracking = true; options.EnableStreaming = true; }); } }); }
2. 优化DbContext生命周期
避免长期持有同一个DbContext实例,每处理N批后重建DbContext,防止其内部积累状态占用内存:
// 在批量插入逻辑中加入DbContext重置逻辑 int batchCounter = 0; int resetInterval = 5; // 每5批重置一次DbContext if (data.Count == batchSize) { _context.BulkInsert(/*...配置参数...*/); data.Clear(); data.TrimExcess(); batchCounter++; if (batchCounter >= resetInterval) { _context.Dispose(); _context = new YourDbContext(); // 重新创建DbContext实例 batchCounter = 0; } }
3. 数据库层面提速优化
插入前临时禁用非核心约束和索引,插入完成后重建,大幅提升插入速度:
-- 插入前执行:禁用非聚集索引和外键约束 ALTER INDEX ALL ON Cats DISABLE; ALTER TABLE Cats NOCHECK CONSTRAINT ALL; -- 执行完所有插入操作后执行:重建索引并启用约束 ALTER INDEX ALL ON Cats REBUILD; ALTER TABLE Cats CHECK CONSTRAINT ALL;
4. 容器资源配置
给容器设置合理的内存请求和限制,避免系统因OOM强制重启:
- Docker Compose示例:
services: fifi-web: image: your-image deploy: resources: limits: memory: 8G # 根据实际需求调整,比如8G或16G reservations: memory: 4G # 内存请求,确保调度器分配足够资源 - Azure App Service:在应用服务的「配置-常规设置」中调整内存配额
额外注意事项
- 内存监控:在代码中加入内存日志,比如
Console.WriteLine($"当前内存占用:{GC.GetTotalMemory(false) / 1024 / 1024} MB");,方便调整批量大小 - 错误记录:将错误信息写入日志系统而非仅输出控制台,方便后续排查
- 小批量测试:先测试10万、50万条数据,验证内存占用和插入速度后再放大到650万条
内容的提问来源于stack exchange,提问作者Jeffrey Andres Guette Sanguino
相关产品推荐
相关产品推荐

