Mar*_*ian 11 statistics system-tables sql-server-2016 query-store
我从一开始说,我的问题/问题类似于此之前的一个,但因为我不知道的原因或起始信息是一样的,我决定后,我的问题有一些更多的细节。
手头问题:
初步调查:
有罪查询:
Select qt.query_sql_text,
q.query_id,
qt.query_text_id,
rs1.runtime_stats_id AS runtime_stats_id_1,
interval_1 = DateAdd(minute, -(DateDiff(minute, getdate(), getutcdate())), rsi1.start_time),
p1.plan_id AS plan_1,
rs1.avg_duration AS avg_duration_1,
rs2.avg_duration AS avg_duration_2,
p2.plan_id AS plan_2,
interval_2 = DateAdd(minute, -(DateDiff(minute, getdate(), getutcdate())), rsi2.start_time),
rs2.runtime_stats_id AS runtime_stats_id_2
From sys.query_store_query_text AS qt
Inner Join sys.query_store_query AS q
ON qt.query_text_id = q.query_text_id
Inner Join sys.query_store_plan AS p1
ON q.query_id = p1.query_id
Inner Join sys.query_store_runtime_stats AS rs1
ON p1.plan_id = rs1.plan_id
Inner Join sys.query_store_runtime_stats_interval AS rsi1
ON rsi1.runtime_stats_interval_id = rs1.runtime_stats_interval_id
Inner Join sys.query_store_plan AS p2
ON q.query_id = p2.query_id
Inner Join sys.query_store_runtime_stats AS rs2
ON p2.plan_id = rs2.plan_id
Inner Join sys.query_store_runtime_stats_interval AS rsi2
ON rsi2.runtime_stats_interval_id = rs2.runtime_stats_interval_id
Where rsi1.start_time > DATEADD(hour, -48, GETUTCDATE())
AND rsi2.start_time > rsi1.start_time
AND p1.plan_id <> p2.plan_id
AND rs2.avg_duration > rs1.avg_duration * 2
Order By q.query_id, rsi1.start_time, rsi2.start_time
Run Code Online (Sandbox Code Playgroud)
设置和信息:
经验观察:
快速而肮脏的解决方案:
所以,我真正的问题是:
为什么对 Query Store 的查询会导致整个实例的性能问题?我们是否处于查询存储的错误领域?
PS:我稍后会上传一些文件(打印屏幕、IO 统计数据和计划)。
在Dropbox 上添加的文件。
计划 1 - 生产中最初古怪的估计计划
计划 2 - 实际计划,旧 CE,在测试环境中(相同的行为,相同的古怪统计数据)
计划 3 - 实际计划,新 CE,在测试环境中
正如我在回答中所说,经验测试表明 sys.plan_persisted* 系统对象上有索引,而没有在它们之上创建任何(无)统计信息。我怀疑这是因为数据库是从 SQL 2005 实例迁移的,并在兼容级别 100 下保留了一段时间,因此无法使用新的 CE。
行数检查:
Select count(1) from NoNameDB.sys.plan_persist_runtime_stats with (nolock) --60362
Select count(1) from NoNameDB.sys.plan_persist_plan with (nolock) --1853
Select count(1) from NoNameDB.sys.plan_persist_runtime_stats_interval with (nolock) --671
Select count(1) from NoNameDB.sys.plan_persist_query with (nolock) --1091
Select count(1) from NoNameDB.sys.plan_persist_query_text with (nolock) --911
Run Code Online (Sandbox Code Playgroud)
这表明最初的估计是错误的。使用 DAC 连接完成,否则无法查询表。
统计检查:
DBCC SHOW_STATISTICS ('sys.plan_persist_runtime_stats_interval', plan_persist_runtime_stats_interval_cidx);
DBCC SHOW_STATISTICS ('sys.plan_persist_runtime_stats', plan_persist_runtime_stats_idx1);
DBCC SHOW_STATISTICS ('sys.plan_persist_runtime_stats', plan_persist_runtime_stats_cidx);
DBCC SHOW_STATISTICS ('sys.plan_persist_plan', plan_persist_plan_cidx);
DBCC SHOW_STATISTICS ('sys.plan_persist_plan', plan_persist_plan_idx1);
DBCC SHOW_STATISTICS ('sys.plan_persist_query', plan_persist_query_cidx)
DBCC SHOW_STATISTICS ('sys.plan_persist_query_text', plan_persist_query_text_cidx);
Run Code Online (Sandbox Code Playgroud)
这表明某些索引的统计数据为空(缺失、无、零)。
初步修复:
UPDATE STATISTICS sys.plan_persist_runtime_stats WITH fullscan;
UPDATE STATISTICS sys.plan_persist_plan WITH fullscan;
UPDATE STATISTICS sys.plan_persist_runtime_stats_interval WITH fullscan;
UPDATE STATISTICS sys.plan_persist_query WITH fullscan;
UPDATE STATISTICS sys.plan_persist_query_text WITH fullscan;
Run Code Online (Sandbox Code Playgroud)
这种修复了统计数据并使查询在 10-12 秒内完成。
第二个修复:
(仅在测试环境中验证)并且最有可能的正确方法是将数据库的兼容性级别更改为 130,因为它显示了查询的最佳统计信息。最终结果是查询在大约 10-12 秒内结束正常数字统计(10k 行)。
中级修复:
DBCC TRACEON (2312) -- new CE
Run Code Online (Sandbox Code Playgroud)
有关系统隐藏表统计信息的一些相关帮助。
如果您在 SSMS 中打开实际计划并查看 CPU 使用情况(或检查 XML),则根本问题是可见的,即节点 32(TVF)。查询存储查询缓慢的罪魁祸首是对内存中 TVF 的重复访问。
从这些 TVF 返回多少行并不重要,重要的是它们被访问的次数。解决办法就是你可以采取任何措施来避免多次阅读计划。
基于我有限的调试(无论是技能还是花费的时间),我的假设是,分配给查询存储数据的特定内存组件的整个内存都会在每次 TVF 执行时进行扫描。我无法使用 或 影响此内存sp_query_store_flush_db分配DBCC FREESYSTEMCACHE。
到目前为止,成功的解决方法包括计划指南、提示(OPTION(HASH JOIN, LOOP JOIN)到目前为止对我来说效果很好)以及在 AG 的只读节点上运行查询存储查询。