.NET Core控制台应用优化:50万条JSON数据读写插入提速
50万条JSON数据批量插入优化方案
原代码性能瓶颈分析
- 删除操作先全量查询再删除,大量占用内存且执行效率低下
- EF上下文长期持有实体追踪,导致内存占用飙升,
SaveChanges执行速度随批次增加逐渐变慢 - JSON解析多了
JObject中间转换步骤,额外消耗CPU资源 - 批次大小设置可能未匹配数据库最优处理能力
核心优化措施
- 批量删除替代查询后删除:用EF Core原生的
ExecuteDelete直接执行SQL级删除,无需将实体加载到内存 - 禁用实体追踪:插入时关闭上下文的自动追踪,减少内存消耗与变更检测开销
- 优化JSON解析:直接反序列化为目标实体,跳过
JObject中间转换 - 调整批次大小:根据数据库性能调整(推荐2000-5000,需测试确定最优值)
- 批次独立上下文:每个批次使用全新上下文,避免追踪缓存累积
优化后完整代码
// 批量删除目标数据,直接执行SQL DELETE语句,无需加载实体 using (var context = new ApplicationDbContext()) { var deleteCount = context.tblDispense .Where(p => p.HfrCode == facilityName) .ExecuteDelete(); // EF Core 3.0+支持,性能远优于查询后删除 Console.WriteLine($"[{facilityName}] 已删除 {deleteCount} 条历史数据"); } // 流式读取JSON并批量插入 using (StreamReader sr = new StreamReader(filePath)) using (JsonTextReader reader = new JsonTextReader(sr)) { var serializer = new JsonSerializer(); while (reader.Read()) { if (reader.TokenType == JsonToken.StartArray && reader.Path == "tblDispense") { const int batchSize = 2000; // 可根据数据库性能调整 List<TblDispense> tblDispenseBatch = new List<TblDispense>(batchSize); // 预分配容量减少内存扩容开销 while (reader.Read() && reader.TokenType != JsonToken.EndArray) { if (reader.TokenType == JsonToken.StartObject) { // 直接反序列化为实体,跳过JObject中间转换 var tblDispense = serializer.Deserialize<TblDispense>(reader); tblDispense.UUID = $"{Guid.NewGuid()}.{facilityName}"; tblDispense.MSDCode = $"{tblDispense.MSDCode}.{facilityName}"; tblDispense.DispenseID = $"{tblDispense.DispenseID}.{facilityName}"; tblDispense.DispenseRecordId = $"{tblDispense.DispenseRecordId}.{facilityName}"; tblDispense.HfrCode = facilityName; tblDispenseBatch.Add(tblDispense); if (tblDispenseBatch.Count >= batchSize) { try { using (var context = new ApplicationDbContext()) { // 禁用实体追踪,大幅提升插入速度 context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; context.tblDispense.AddRange(tblDispenseBatch); context.SaveChanges(); } tblDispenseBatch.Clear(); Console.WriteLine($"[{facilityName}] 已完成 {batchSize} 条数据插入"); } catch (DbUpdateException ex) { Console.WriteLine($"[{facilityName}] 批量插入出错: {ex.InnerException?.Message}"); logger.LogError(ex, $"[{facilityName}] 批量插入出错"); } } } } // 插入剩余未达批次量的数据 if (tblDispenseBatch.Count > 0) { try { using (var context = new ApplicationDbContext()) { context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; context.tblDispense.AddRange(tblDispenseBatch); context.SaveChanges(); } Console.WriteLine($"[{facilityName}] 已完成剩余 {tblDispenseBatch.Count} 条数据插入"); } catch (DbUpdateException ex) { Console.WriteLine($"[{facilityName}] 剩余数据插入出错: {ex.InnerException?.Message}"); logger.LogError(ex, $"[{facilityName}] 剩余数据插入出错"); } } break; } } }
额外性能提升建议
- 引入EF批量扩展库:如果允许使用第三方库(如
Z.EntityFramework.Extensions.EFCore),可直接调用BulkInsert方法,生成数据库原生批量插入语句,性能可提升数倍 - 数据库层面优化:插入前临时关闭表的非必要索引与约束,插入完成后重建;开启SQL Server的
SET NOCOUNT ON减少网络交互 - 并行处理多文件:若存在多个待处理JSON文件,可使用并行任务同时处理不同文件(单文件流式读取不建议并行)
内容的提问来源于stack exchange,提问作者Janeth Jackson
相关产品推荐
相关产品推荐

