Query Store是SQL Server 2016中引入的、語句級別的性能監控和調優工具,它不僅自動捕獲查詢(Query)、執行計划(Plan)、運行時統計信息(Runtime)和等待(Wait)統計的信息,而且還可以識別出由於執行計划更改而導致的性能差異,簡化了性能故障排除的流程,降低了性能優化的難度。從字面上來解釋,Query Store是“查詢的倉庫”,它是由SQL Server引擎自動維護,用於捕獲數據庫中執行的查詢,以及跟其執行性能相關聯的數據。不同於DMV把數據存儲到內存中,Query Store會把捕獲的性能數據存儲到文件(Disk)中,為了最優化數據的寫入,Query Store使用異步更新方式,新捕獲的數據會存儲到內存中,每隔一定時間(分時)就把捕獲的數據存儲到硬盤(Disk)中。
一,啟用Query Store
Query Store默認是關閉的,啟用Query Store對查詢性能還是有一定的影響的,

ALTER DATABASE { database_name | CURRENT } SET QUERY_STORE { = OFF | = ON [ ( <query_store_option_list> [,...n] ) ] | CLEAR [ ALL ] } <query_store_option_list> ::= { OPERATION_MODE = { READ_WRITE | READ_ONLY } | CLEANUP_POLICY = ( STALE_QUERY_THRESHOLD_DAYS = number ) | DATA_FLUSH_INTERVAL_SECONDS = number | MAX_STORAGE_SIZE_MB = number | INTERVAL_LENGTH_MINUTES = number | SIZE_BASED_CLEANUP_MODE = { AUTO | OFF } | QUERY_CAPTURE_MODE = { ALL | AUTO | CUSTOM | NONE } | MAX_PLANS_PER_QUERY = number | WAIT_STATS_CAPTURE_MODE = { ON | OFF } | QUERY_CAPTURE_POLICY = ( <query_capture_policy_option_list> [,...n] ) } <query_capture_policy_option_list> :: = { STALE_CAPTURE_POLICY_THRESHOLD = number { DAYS | HOURS } | EXECUTION_COUNT = number | TOTAL_COMPILE_CPU_TIME_MS = number | TOTAL_EXECUTION_CPU_TIME_MS = number }
參數注釋:
- CLEAR:清空Query Store的數據
- OPERATION_MODE:READ_WRITE是指Query Store會持續收集和持久化數據,而READ_ONLY是指只能從Query Store讀取信息,而不會更新Query Store。
- CLEANUP_POLICY:定義數據留存的時間窗口,超過該窗口,過期的數據從Query Store中清理出去。
- DATA_FLUSH_INTERVAL_SECONDS:定義數據持久化到硬盤的頻率,默認值是900s(15min)。為了優化性能,Query Store收集的數據采用異步寫方式,每隔一定的時間會把捕獲的數據寫到硬盤中。
- MAX_STORAGE_SIZE_MB:設置Query Store的最大存儲空間,如果Query Store達到最大存儲空間的限制,Query Store會把操作模式(OPERATION_MODE)更改為READ_ONLY,不再寫入新的數據。
- INTERVAL_LENGTH_MINUTES:每隔一定的時間窗口對運行時的執行統計數據進行聚合,然后把聚合值存儲到Query Store中。
- SIZE_BASED_CLEANUP_MODE :基於Query Store占用的空間大小控制是否啟動清理程序,清理程序會自動刪除Query Store中過時的數據,以釋放Query Store的空間
- QUERY_CAPTURE_MODE :定義捕獲查詢的捕獲模式,默認值是ALL,表示捕獲所有的查詢,AUTO表示基於執行次數和資源消耗來捕獲相關的查詢。
- MAX_PLANS_PER_QUERY:定義為每個查詢維護的計划數量,默認值是200
- WAIT_STATS_CAPTURE_MODE :是否捕獲等待統計(wait stats),從SQL Server 2017(14.x)開始支持該選項。
- QUERY_CAPTURE_POLICY:定義捕獲Query的策略,
- STALE_CAPTURE_POLICY_THRESHOLD :定義評估的時間間隔(evaluation interval period ),根據以下的選項來確定是否一個query應該被捕獲,evaluation period的默認值是1day
- EXECUTION_COUNT:默認值是30,在evaluation period內,如果一個query的執行次數超過指定的數值,那么捕獲該query。
- TOTAL_COMPILE_CPU_TIME_MS:默認值是1000ms,在evaluation period內,如果一個query的編譯時間超過指定的時間,那么捕獲該query。
- TOTAL_EXECUTION_CPU_TIME_MS:默認值是100,在evaluation period內,如果一個query的執行時間超過指定的時間,那么捕獲該query。
例如,使用以下的腳本來啟用TestDB的Query Store:
ALTER DATABASE [TestDB] SET QUERY_STORE = ON ( OPERATION_MODE = READ_WRITE, CLEANUP_POLICY = ( STALE_QUERY_THRESHOLD_DAYS = 90 ), DATA_FLUSH_INTERVAL_SECONDS = 900, MAX_STORAGE_SIZE_MB = 1000, INTERVAL_LENGTH_MINUTES = 60, SIZE_BASED_CLEANUP_MODE = AUTO, MAX_PLANS_PER_QUERY = 200, WAIT_STATS_CAPTURE_MODE = ON, QUERY_CAPTURE_MODE = CUSTOM, QUERY_CAPTURE_POLICY = ( STALE_CAPTURE_POLICY_THRESHOLD = 24 HOURS, EXECUTION_COUNT = 30, TOTAL_COMPILE_CPU_TIME_MS = 1000, TOTAL_EXECUTION_CPU_TIME_MS = 100 ) );
在啟用Query Store之后,用戶可以通過系統視圖來查看各個選項的配置情況:
sys.database_query_store_options
當然,也可以使用SSMS對Query Store的各個選項進行配置和查看:
二,Query Store捕獲的信息
從總體上來說,Query Store包含四個Store,分別是query store、plan store、runtime stats store和wait stats store:
- query store 用於捕獲查詢的信息,
- plan store用於捕獲執行計划的信息,
- runtime stats store用於捕獲執行計划的變更記錄和統計信息等,
- stats store 用於捕獲等待統計信息。
除了捕獲信息,Query Store還可以根據plan的性能信息,強制查詢使用特定的計划。
1,計划強制(Plan Forcing)
由於一些不可預知的原因,例如統計信息更改,架構更改,索引的創建/刪除等,SQL Server中任意一個查詢的執行計划通常都會隨着時間的推移而變化。由於內存壓力,計划也會從計划緩存中逐出,這導致計划緩存只存儲查詢的最新的執行計划。由執行計划更改引起的查詢性能的下降,就無跡可尋,解決起來很費時間。
由於Query Store為每個查詢保留多個執行計划,因此,它可以指示查詢處理器(Query Processor)對查詢強制使用特定的執行計划,這稱為計划強制(Plan Forcing)。Query Store中的計划強制(Plan Forcing)的工作類似於USE PLAN查詢提示的機制,但是不需要在用戶在應用程序中做任何更改,計划強制可以在很短的時間內解決由計划更改導致的查詢性能下降的問題。
2,等待統計(Wait Stats)
等待統計信息是解決查詢性能問題的另一信息來源,長期以來,等待統計信息僅在實例級別可用,這使得很難將等待回溯到特定查詢語句上。 從SQL Server 2017(14.x)和 Azure SQL數據庫開始,查詢存儲能夠追蹤特定語句的等待信息。
ALTER DATABASE AdventureWorks2012 SET QUERY_STORE = ON ( WAIT_STATS_CAPTURE_MODE = ON );
3,查詢處理的過程
Query Store會在Query的編譯和執行階段收集數據:
- 當query第一次編譯時,query的text和plan存儲到Query Store中;
- 當query重編譯時,如果query產生新的plan,Query Store會存儲新的plan,並把先前的plan的執行信息保留下來。
- 一旦query開始執行,Query Store開始收集query執行時(runtime)的數據。為了減少數據的數量,Query Store每隔一段時間對runtime進行聚合,只存儲統計數據,而不直接存儲詳細數據。
- 在編譯(Compile)和重編譯(Recompile)檢查階段,SQL Server首先檢查Query Store中是否有Plan可以用於當前查詢,如果存在一個forced plan,並且不同於過程緩存(Procedure Cache)中的計划,那么該查詢被重編譯(recompile)。
4,捕獲數據的持久化過程
為了最小化IO消耗,Query Store新捕獲的數據會先暫存到內存中。寫操作會排隊,之后數據會被刷新到硬盤中。
對於Query和Plan 信息,Query Store以最小的時延寫入到硬盤;對於runtime stats,Query Store首先在內存中保留一定的時間,該時間由DATA_FLUSH_INTERVAL_SECONDS
確定,然后把收集的多個數據一起寫入到硬盤中。
三,Query Store 界面化查詢
在啟用Query Store之后,刷新SSMS的Object Explorer面板,用戶會發現,SSMS新增加了一個Query Store的目錄,這是SSMS為了方便用戶查詢數據而內置的查詢界面:
以回歸查詢(Regressed Queries)來舉例,Regressed Queries面板顯示的是查詢和相應的Plan,從Metric列表中選擇需要顯示的度量,就可以查看不同的界面:
四,Query Store相關的視圖
如果不滿足於查詢界面提供的數據,也可以編寫TSQL代碼來查看Query Store追蹤的數據。跟Query Store相關的視圖分為四類:Query、Plan、Runtime Stats和Wait Stats。注意,Wait Stats 是從SQL Server 2017(14.x)開始支持的。
1,關於Query的信息
query的唯一標識字段是query_id
select q.query_id ,t.query_text_id ,t.query_sql_text ,q.object_id as parent_object ,q.is_internal_query ,q.query_parameterization_type ,q.query_parameterization_type_desc ,q.count_compiles ,q.avg_compile_duration ,q.avg_bind_cpu_time ,q.avg_bind_duration ,q.avg_compile_memory_kb ,q.avg_optimize_cpu_time ,q.avg_optimize_duration from sys.query_store_query as q inner join sys.query_store_query_text as t on q.query_text_id = t.query_text_id ;
2,關於Plan的信息
plan關聯的query,可以通過字段query_id來關聯
select p.plan_id ,p.query_id ,t.query_sql_text ,p.query_plan ,p.is_parallel_plan ,p.is_forced_plan ,p.force_failure_count ,p.last_force_failure_reason ,p.last_force_failure_reason_desc ,p.count_compiles ,p.avg_compile_duration from sys.query_store_plan p inner join sys.query_store_query q on p.query_id=q.query_id inner join sys.query_store_query_text t on q.query_text_id=t.query_text_id
3,關於Plan 的 Runtime 統計
runtime stats通過plan_id 關聯到特定的query
select r.runtime_stats_id ,r.plan_id ,r.runtime_stats_interval_id ,i.start_time as interval_start_time ,i.end_time as interval_end_time ,r.execution_type ,r.execution_type_desc ,r.count_executions ,r.avg_duration ,r.avg_cpu_time ,r.avg_dop ,r.avg_logical_io_reads ,r.avg_logical_io_writes ,r.avg_physical_io_reads ,r.avg_query_max_used_memory ,r.avg_rowcount from sys.query_store_runtime_stats r inner join sys.query_store_runtime_stats_interval i on r.runtime_stats_interval_id=i.runtime_stats_interval_id
4,關於等待的統計信息
該視圖統計的等待是跟單個執行計划有關的
select w.wait_stats_id ,w.plan_id ,w.runtime_stats_interval_id ,w.wait_category ,w.wait_category_desc ,w.execution_type ,w.execution_type_desc ,w.avg_query_wait_time_ms ,w.min_query_wait_time_ms ,w.max_query_wait_time_ms from sys.query_store_wait_stats w inner join sys.query_store_plan p on w.plan_id=p.plan_id
參考文檔: