EF Core查询性能优化求助:存储过程后数据处理耗时过长
EF Core 大数据处理性能优化求助
使用EF Core处理大量数据后返回给终端用户,当前操作耗时约30秒,已尝试添加AsNoTracking().AsEnumerable()但无明显效果,寻求性能优化方案。
架构为控制器调用服务,服务依赖两个辅助函数:一个用于生成存储过程查询参数,另一个根据用户传入参数执行分组。相关代码如下:
核心服务代码
public async Task<List<ResultDto>> MyService(DateTime? startDate, DateTime? endDate,string groupingType) { List<ResultDto> result = new List<ResultDto>(); try { SqlParameter[] param = StoredProcedureHelpers.GetAsSQLParameters2(startDate, endDate); string sql = "EXEC MyStoredProcedure @StartDate,@EndDate"; var storedProcedureResult = _context.MyStoredProceduresResults.FromSqlRaw(sql, param).AsNoTracking().AsEnumerable(); var distinctedResult = storedProcedureResult.DistinctBy(x => x.Some_Id).OrderByDescending(x => x.Value).Take(10).AsEnumerable(); result = distinctedResult.GroupByChoosedGroupingType(groupingType).OrderBy(x => x.GroupingKey).ToList(); } catch (Exception ex) { throw new Exception(ex.Message); } return result; }
存储过程参数辅助函数
public static SqlParameter[]? GetAsSQLParameters2(DateTime? startDate, DateTime? endDate) { SqlParameter[] param = Array.Empty<SqlParameter>(); try { param = new SqlParameter[] { new SqlParameter { ParameterName = "@StartDate", SqlDbType =System.Data.SqlDbType.Date, Direction = System.Data.ParameterDirection.Input, Value = startDate }, new SqlParameter { ParameterName = "@EndDate", SqlDbType =System.Data.SqlDbType.Date, Direction = System.Data.ParameterDirection.Input, Value = endDate } }; } catch (Exception ex) { throw new Exception(ex.Message); } return param; }
分组辅助函数
public static IEnumerable<ResultDto> GroupByChoosedGroupingType(this IEnumerable<MyStoredProceduresResults> resultList, string groupingType) { List<ResultDto> result = new List<ResultDto>(); switch (groupingType) { case "Daily": result = resultList.GroupBy(x => new { x.Date.Value.Date, x.PlaceName }).Select(x => { return new ResultDto { Count = x.Count(), Value = x.Sum(x => x.Value), PlaceName = x.Key.PlaceName, GroupingKey = x.Key.Date.ToString("dd/MM/yyyy") }; }).ToList(); break; case "Monthly": result = resultList.GroupBy(x => new { Year = x.Date.Value.Year, MonthName = x.Date.Value.ToString("MMM"), x.PlaceName }).Select(x => new ResultDto { Count = x.Count(), Value = x.Sum(x => x.Value), PlaceName = x.Key.PlaceName, GroupingKey = $"{x.Key.MonthName}-{x.Key.Year}" }).ToList(); break; } return result; }
性能优化方案
1. 将数据过滤、排序逻辑移至存储过程
当前代码先把存储过程的全部结果拉到内存,再执行DistinctBy、OrderByDescending、Take(10),这会导致大量不必要的数据传输和内存占用。建议直接在存储过程中完成以下操作:
- 按
Some_Id去重 - 按
Value降序排序 - 仅返回前10条数据
这样能大幅减少从数据库传输到应用程序的数据量,是最有效的优化手段。
2. 把分组聚合逻辑下移到数据库
目前分组、求和、计数操作都是在应用内存中完成,如果存储过程能根据传入的groupingType参数直接返回聚合后的结果(比如按日/月分组的统计数据),可以彻底避免拉取原始明细数据,性能提升会非常显著。
3. 优化存储过程本身的查询性能
- 检查存储过程中的SQL查询是否存在全表扫描,为
StartDate、EndDate、Some_Id、Date等查询字段添加合适的索引。 - 查看SQL Server执行计划,定位慢查询的瓶颈(比如缺失索引、表连接效率低等)。
- 处理
DateTime?空值:如果startDate或endDate为null,存储过程可能会扫描全表,建议在参数中设置合理的默认值(比如默认查询最近3个月的数据)。
4. 优化EF Core调用的异步性能
当前代码使用同步的FromSqlRaw,改为异步调用避免阻塞线程,提升并发处理能力:
// 替换原存储过程结果获取代码 var storedProcedureResult = await _context.MyStoredProceduresResults .FromSqlRaw(sql, param) .AsNoTracking() .ToListAsync();
5. 减少不必要的内存枚举操作
代码中多次调用AsEnumerable(),但FromSqlRaw().AsNoTracking().AsEnumerable()已经将数据加载到内存,后续的DistinctBy等操作都是内存操作,无需重复调用AsEnumerable()。此外,尽量延迟ToList()的调用时机,减少内存中数据的复制次数。
6. 优化分组逻辑的内存计算
- 在
Monthly分组中,x.Date.Value.ToString("MMM")的字符串操作可以移到数据库层面(比如用SQL的DATENAME函数生成月份名称),减少应用内存的计算开销。 - 分组键尽量使用值类型(比如
Date、int),避免匿名类中的字符串操作,提升分组效率。
内容的提问来源于stack exchange,提问作者Abdulaziz Burghal
相关产品推荐
相关产品推荐

