SQL Server 2016生产库性能下降:Profiler数据与非高峰运行结果不符
先帮你明确一个关键细节:SQL Server Profiler里的Duration列单位默认是微秒,但如果用的是SSMS自带的跟踪模板,有时候会被改成毫秒。结合你的CPU time(单位固定为毫秒)是65141(约65秒),Duration为114546的话,显然单位是毫秒(约114秒)——总耗时不可能比CPU执行时间还短,所以这个查询在生产高峰跑了差不多2分钟。
针对你发现的“非业务时段执行飞快、仅读少量数据页”的反差,我推测主要原因集中在这几个方向:
参数嗅探导致的执行计划跑偏
这是生产环境慢查询的头号元凶。如果你的查询用了参数(比如存储过程参数、动态SQL里的变量),SQL Server会根据首次执行的参数值生成执行计划并缓存。要是生产时段传入的参数对应的数据集和非业务时段差得极大——比如高峰时查的是热门大分区,非业务时查的是冷数据小分区——那缓存的低效计划(比如全表/全索引扫描)就会被反复调用,导致3600多万次逻辑读;而非业务时段重新生成的计划则用了高效的索引seek,自然只读少量数据页。生产高峰的资源竞争拖慢执行
就算执行计划没问题,高峰时段CPU、内存、IO被大量业务请求占满,查询也会被迫等待。从你的数据看,CPU time(65秒)远小于Duration(114秒),说明查询有大量等待时间——大概率是PAGEIOLATCH_*这类IO等待(毕竟要读3600多万页,IO系统在高峰时根本扛不住),或者是CPU资源不够导致的SOS_SCHEDULER_YIELD等待,硬生生拉长了总耗时。内存缓存命中率的差异
非业务时段服务器内存压力小,查询需要的数据页都存在缓存里,set statistics IO显示的物理读极少;但生产时段内存被其他业务占满,查询需要的大量数据页只能从磁盘读,这不仅增加了物理读的耗时,还可能因为内存不足,SQL Server被迫选择“省内存但低效”的执行计划(比如放弃哈希匹配改用嵌套循环,或者放弃索引seek改用扫描)。
要是想验证这些推测,可以做这几件事:
- 对比生产时段和非业务时段的执行计划,看看是不是存在扫描和seek的差异;
- 用
sys.dm_exec_query_stats或sys.dm_os_wait_stats查看查询的等待统计,确认主要的等待类型; - 检查存储过程的执行计划缓存,看看有没有多个计划或者不符合预期的计划。
内容的提问来源于stack exchange,提问作者Sam

