使用EF Core批量插入百万条数据时内存不足异常的解决方法
修复EF Core批量插入内存不足异常的最优方法
核心问题分析
当前代码通过AsEnumerable()把stagingUnits全量加载到内存,同时创建百万个ImportJobValidation对象存入List后一次性提交,导致内存被大量对象占满,触发内存溢出异常。
最优解决方案:分批处理+优化匹配逻辑
第一步:优化错误匹配逻辑
把errors集合转成以StagingId为键的HashSet,避免每次判断都遍历整个errors列表,既减少内存占用又提升匹配效率:var errorStagingIds = new HashSet<int>(errors.Select(e => e.StagingId));第二步:分批读取并插入数据
不要全量加载stagingUnits,而是用EF Core分页查询,每次处理固定数量的记录(比如1000条,可根据内存情况调整),处理完一批就提交并清理上下文,释放内存:var batchSize = 1000; var totalCount = await stagingUnits.CountAsync(); var batchCount = (int)Math.Ceiling((double)totalCount / batchSize); var errorStagingIds = new HashSet<int>(errors.Select(e => e.StagingId)); for (int i = 0; i < batchCount; i++) { var currentBatch = await stagingUnits .Skip(i * batchSize) .Take(batchSize) .ToListAsync(); var importJobValidations = currentBatch.Select(stg => new ImportJobValidation { Guid = Guid.NewGuid(), StudyProductStagingID = stg.Id, IsValidationSuccessful = !errorStagingIds.Contains(stg.Id), Type = "UPLOAD", IsActive = true, CreatedBy = userId, CreatedOn = DateTime.UtcNow }).ToList(); await _supplyManagementHangFireContext.AddRangeAsync(importJobValidations); await _supplyManagementHangFireContext.SaveChangesAsync(); // 清理上下文,避免跟踪过多实体占用内存 _supplyManagementHangFireContext.ChangeTracker.Clear(); }额外优化:使用无跟踪查询
如果stagingUnits不需要被上下文跟踪,可加上AsNoTracking()进一步减少内存占用:var currentBatch = await stagingUnits .AsNoTracking() .Skip(i * batchSize) .Take(batchSize) .ToListAsync();
方案优势
- 分批处理仅在内存中保留少量数据,从根源上避免内存溢出;
- HashSet将匹配操作的时间复杂度从O(n)降到O(1),提升整体执行效率;
- 清理上下文跟踪的实体,避免上下文积累大量未释放的对象。
内容的提问来源于stack exchange,提问作者Able
相关产品推荐
相关产品推荐

