存储过程在不同数据库执行计划不同,主库无法复现更优执行计划
同存储过程跨库执行计划差异排查方向
- 排查参数嗅探影响:即使清理过过程缓存,也要确认两个库编译存储过程时传入的
@EventId参数对应的数据分布是否一致。生产库首次编译时若传入的是对应大量匹配行的@EventId,优化器会认为Index Scan的顺序IO成本低于Index Seek的随机IO成本,优先选择扫描计划;开发库首次编译若传入小数据量级参数则会生成查找计划。可以在生产库执行存储过程时追加OPTION (RECOMPILE)强制重编译,观察执行计划是否发生变化。 - 核对统计信息的直方图明细:你提到统计信息接近但不完全相同,需要重点核对
Division表EventId列、TeamPlayer表Active列的直方图分段、特定参数值的预估行数差异。即使整体数据量接近,单个参数值的预估行数偏差超过10%就会导致优化器选择不同的访问路径,可以通过DBCC SHOW_STATISTICS('表名','统计信息名称')分别在两个库查询目标参数值对应的预估行数,和实际行数做对比校准。 - 检查索引物理状态差异:即使索引定义完全一致,生产库高频的写入、删除操作会导致索引碎片率升高、页密度下降,优化器会认为Index Seek的随机IO成本更高,从而优先选择扫描计划。可以通过
sys.dm_db_index_physical_stats视图对比两个库相关表上索引的碎片率、平均页密度指标。 - 核对数据库层面的优化器配置差异:除兼容模式外,还要检查两个库的最大并行度(MAXDOP)、并行执行成本阈值、基数估算器版本、参数嗅探开关是否完全一致,这些配置会直接影响优化器的成本计算逻辑,导致执行计划差异。
- 临时修复方案:如果无法快速对齐底层差异,可以通过查询存储(Query Store)强制生产库使用更优的Index Seek执行计划,也可以在存储过程的查询末尾添加
OPTION (FORCESEEK)查询提示强制走索引查找,修改前需要覆盖所有可能的@EventId参数场景做性能验证。
ALTER PROCEDURE [GetTeamPlayerCount] @EventId INT, @Active INT = 1 AS BEGIN SET NOCOUNT ON; SELECT tp.TeamId, COUNT(*) AS [Count] FROM Division d INNER JOIN DivisionTeam dt ON dt.DivisionId = d.Id INNER JOIN TeamPlayer tp ON dt.Id = tp.TeamId WHERE d.EventId = @EventId AND tp.Active = @Active GROUP BY tp.TeamId -- 可选添加强制查找提示 -- OPTION (FORCESEEK) END
内容的提问来源于stack exchange,提问作者Mike Flynn
相关产品推荐
相关产品推荐

