Azure SQL DB/DW 系列(4)——Query Store案例(1)——缺失索引

本文属于Azure SQL DB/DW系列
上一文:Azure SQL DB/DW 系列(3)——Query Store简介
本文演示如何使用Query Store来找到Missing Index

环境搭建

  本文以AdventureWorks2017作为演示,使用SQL Server 2019的兼容级别(150)。现在查看一下,这个库目前是没有查询存储:
在这里插入图片描述
  首先需要启用Query Store,可以在SSMS中实现,不过如果你已经有脚本,那直接用脚本来得更快,另外需要提醒一下,GUI界面的内容根据SSMS版本的不同而不同,比如SSMS 18.4就是一个临界点,下面两个图就是来自于SSMS 17和18.4
在这里插入图片描述

USE master
GO
--启用Query Store
ALTER DATABASE AdventureWorks2017 SET QUERY_STORE = ON
GO
--配置Query Store
ALTER DATABASE AdventureWorks2017 SET QUERY_STORE (OPERATION_MODE = READ_WRITE, CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 30),     DATA_FLUSH_INTERVAL_SECONDS = 300, INTERVAL_LENGTH_MINUTES = 10)
GO

  配置说明:

  • OPERATION_MODE:可以为 READ_WRITE(默认)或 READ_ONLY
  • CLEANUP_POLICY :配置 STALE_QUERY_THRESHOLD_DAYS 参数,以指定数据在查询存储中保留的天数 。 默认值为 30。
  • DATA_FLUSH_INTERVAL_SECONDS :Query Store的数据被刷进磁盘永久保存的频率,这里的单位是秒,用GUI的话显示的是分钟,建议大概15或者5分钟(300秒),太频繁会导致服务器压力上升。
  • INTERVAL_LENGTH_MINUTES :以分钟为单位。确定运行时执行统计数据聚合到查询存储中的时间间隔 。 为了优化空间使用情况,将在固定时间窗口上聚合运行时统计信息存储中的运行时执行统计信息。 此固定时间窗口通过 INTERVAL_LENGTH_MINUTES 进行配置。 默认值是 60秒,也就是1分钟。
    在这里插入图片描述
      执行完之后,【查询存储】就出来了。不过提醒一下,如果系统在运行,甚至负载很重的时候,启用query store可能会挺久,比如10分钟。
      然后创建一个非常简单的Missing Index的场景,第一个会 出现Missing Index,第二个不会,这个可以在执行计划中看到,需要多运行几次以便查询存储能够捕获足够的信息:
/* 多次运行以便产生足够的活动 */
select * 
from [Person].[Person]
where MiddleName='A'
GO



select FirstName,MiddleName,LastName
from [Person].[Person]
where FirstName like 'A%'
GO

在这里插入图片描述

GUI查看结果

  首先使用GUI来查看,查询存储侧重于使用最多资源的查询、总体资源消耗以及使用不同的计划变慢的查询。查询存储便于查看这些类型的查询的计划,并且你可以直观地识别执行计划中是否有该时间段的索引请求。
  我们打开如图所示的[资源消耗量最多的几个查询]:
在这里插入图片描述
  这里会显示当前库在过去一小时内25个资源消耗者(resource consumers)的信息,默认会对第一个也就是top 1显示一些更详细的信息,并且柱状图是绿色的。
  从图上可以看到,missing index在上面,因为下方的执行计划有一行缺少索引的绿色字。从柱状图的高矮可以看出它的逻辑读比第二个高得多。如果你把鼠标移到右上角的那个区域的点上,还可以看到它在这个时间段中运行过了9 次。光这几个信息,Query Store已经比其他工具优秀很多。
在这里插入图片描述
  接下来查看一下不存在Missing Index的请求会是怎样的。现在我们点开第二个柱子,注意这是特例,并不是每个实际环境中都会按照这个方式来显示:
在这里插入图片描述

虽然执行次数不一样,不过简单计算一下也可以看到其IO远比上面的低。当然,其实这两个查询可比性不高,这里只是为了演示如何找到缺少索引而已。注重方法而不是实际内容。

DMV查看结果

  如果嫌GUI操作慢,可以直接使用下面的DMV语句来查看。我这里显示的是24小时内的内容,读者可以按照自己的需要调整时间间隔,但是需要提醒一下,下面的SQL 只是显示最近收集的执行计划,在一个长时间内,某个查询的执行计划可能会因为很多因素而变化,并且不是所有的执行计划都一定会存在缺失索引,所以其实DMV可能并不可以完全替代GUI,需要搭配来用:

SELECT
    SUM(qrs.count_executions) * AVG(qrs.avg_logical_io_reads) as est_logical_reads,
    SUM(qrs.count_executions) AS sum_executions,
    AVG(qrs.avg_logical_io_reads) AS avg_avg_logical_io_reads,
    SUM(qsq.count_compiles) AS sum_compiles,
    (SELECT TOP 1 qsqt.query_sql_text FROM sys.query_store_query_text qsqt
        WHERE qsqt.query_text_id = MAX(qsq.query_text_id)) AS query_text,	
    TRY_CONVERT(XML, (SELECT TOP 1 qsp2.query_plan from sys.query_store_plan qsp2
        WHERE qsp2.query_id=qsq.query_id
        ORDER BY qsp2.plan_id DESC)) AS query_plan,
    qsq.query_id,
    qsq.query_hash
FROM sys.query_store_query qsq
JOIN sys.query_store_plan qsp on qsq.query_id=qsp.query_id
CROSS APPLY (SELECT TRY_CONVERT(XML, qsp.query_plan) AS query_plan_xml) AS qpx
JOIN sys.query_store_runtime_stats qrs on qsp.plan_id = qrs.plan_id
JOIN sys.query_store_runtime_stats_interval qsrsi on qrs.runtime_stats_interval_id=qsrsi.runtime_stats_interval_id
WHERE	
    qsp.query_plan like N'%<MissingIndexes>%'
    and qsrsi.start_time >= DATEADD(HH, -24, SYSDATETIME())
GROUP BY qsq.query_id, qsq.query_hash
ORDER BY est_logical_reads DESC
GO

在这里插入图片描述

小结

  本例子演示了如何使用Query Store查询Missing Index,其实读者可以看出,我实际上是查询资源消耗,因为缺失索引几乎都会造成资源的过度消耗。
  选用这个作为例子是因为首先Missing Index影响非常大,其次它比较好捕获,另外从执行计划里面也很容易看出来。当然还有很多更深入的Query Store案例,接下来会再演示几个。
  关于Missing Index,还可以看一下我过去的文章:SQL Server 索引维护(1)——如何获取索引使用情况

下一文:Azure SQL DB/DW 系列(5)——Query Store案例(2)——计划回归

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章