EF6调用表函数执行Count性能远低于SSMS的原因排查
同一查询在EF6与SSMS中性能差异的核心原因
以下是导致这种差异的几个关键因素:
1. 参数嗅探与执行计划不匹配
SQL Server的查询优化器会根据首次执行时的参数值生成执行计划并缓存。EF调用时,如果之前有其他@AspNetUserID值的执行请求,缓存的计划可能并不适用于当前的0值;而SSMS中直接执行时,是全新的执行上下文,优化器会针对@AspNetUserID=0生成最优计划。另外,EF通过sp_executesql传递参数的方式,和SSMS直接执行的参数嗅探逻辑可能存在细微差异,导致计划选择不同。
2. 会话级SET配置差异
EF的数据库连接默认的SET选项和SSMS不一致,比如:
- SSMS默认开启
ARITHABORT ON,而EF默认是ARITHABORT OFF - 还有
ANSI_NULLS、QUOTED_IDENTIFIER等选项的差异
SQL Server会将不同SET选项组合的会话视为独立的执行上下文,即使SQL语句完全相同,也会生成不同的执行计划。这些选项的差异可能导致优化器对表值函数GetProviders的执行路径选择完全不同,进而影响性能。
3. 执行计划缓存的上下文问题
表值函数的执行计划缓存依赖于调用时的上下文信息。EF的调用方式(通过DbContext的方法调用)可能触发了和SSMS直接执行不同的缓存键,导致无法复用之前生成的最优计划,甚至生成了低效的计划。
4. 连接池与资源竞争
EF使用的连接来自连接池,该连接可能残留了之前操作的会话状态,或者执行时数据库服务器正处于高负载(CPU、IO占用高)状态;而SSMS执行时刚好资源空闲,这也会导致性能差异。不过这种情况的概率相对较低。
验证与解决方向
- 模拟EF的SET选项:在SSMS中先执行EF默认的SET选项(可以通过Profiler捕获EF连接的SET语句),再执行捕获的SQL,看是否变慢,以此确认是SET选项导致的问题。
- 强制重新编译:在EF的查询中添加
OPTION (RECOMPILE)(可以通过EF拦截器实现),让优化器针对当前参数重新生成计划。 - 清除缓存测试:执行
DBCC FREEPROCCACHE清除执行计划缓存后,再用EF执行查询,看是否和SSMS速度一致,以此验证参数嗅探的影响。
内容的提问来源于stack exchange,提问作者Arden Inside
相关产品推荐
相关产品推荐

