Azure SQL DB/DW 系列(5)——Query Store案例(2)——計劃迴歸

本文屬於Azure SQL DB/DW系列
上一文:Azure SQL DB/DW 系列(4)——Query Store案例(1)——缺失索引
本文演示如何使用Query Store來找到計劃迴歸並處理

環境準備

  本文以自建數據庫作爲演示,使用SQL Server 2019的兼容級別(150)。

--創建數據庫
USE MASTER 
GO
CREATE DATABASE QS  --For Query Store
GO
USE QS
GO
--啓用Query Store
ALTER DATABASE QS SET QUERY_STORE = ON
GO
--配置Query Store
ALTER DATABASE QS SET QUERY_STORE
(
	OPERATION_MODE = READ_WRITE, 
	CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 367), 
	DATA_FLUSH_INTERVAL_SECONDS = 900, 
	INTERVAL_LENGTH_MINUTES = 1, 
	MAX_STORAGE_SIZE_MB = 100, 
	QUERY_CAPTURE_MODE = ALL, 
	SIZE_BASED_CLEANUP_MODE = OFF
)
GO
--創建一個簡單的表,並創建一個非聚集索引,然後插入10萬行數據
CREATE TABLE Customers
(
	CustomerID INT NOT NULL PRIMARY KEY CLUSTERED,
	CustomerName CHAR(10) NOT NULL,
	CustomerAddress CHAR(10) NOT NULL,
	Comments CHAR(5) NOT NULL,
	Value INT NOT NULL
)
GO
--創建索引
CREATE UNIQUE NONCLUSTERED INDEX idx_Test ON Customers(Value)
GO
 
-- 插入10萬數據
DECLARE @i INT = 1
WHILE (@i <= 100000)
BEGIN
	INSERT INTO Customers VALUES
	(
		@i,
		CAST(@i AS CHAR(10)),
		CAST(@i AS CHAR(10)),
		CAST(@i AS CHAR(5)),
		@i
	)
	
	SET @i += 1
END
GO

  爲了生成計劃迴歸,創建一個存儲過程來實現:

--創建一個存儲過程來訪問
CREATE PROCEDURE QueryCustomers
(
	@Value INT
)
AS
BEGIN
	SELECT * FROM Customers
	WHERE Value < @Value
END
GO

計劃迴歸

  計劃迴歸(Plan Regression)指特定的查詢的執行計劃被改變了。比如某個時間SQL Server緩存了一個合理的執行計劃,但是過了一段時間之後生成了一個不合理的執行計劃,並緩存了,同時還被重用,表現出來就是某個功能之前運行的挺好,突然間就很慢。
  這種現象一直都存在,但是很難捕獲,畢竟很多時候你只能獲取當前的執行計劃或者最近緩存的執行計劃。從SQL 2016引入Query Store之後,這個問題就可以得到解決,最起碼可以緩解。它可以找到是否有計劃迴歸導致了你的系統出現性能問題,一旦你找到了存在計劃迴歸,也可以很簡單地,透明地使用強制特定計劃來解決。

引出問題

  首先使用100000作爲參數並打開實際執行計劃,調用存儲過程:

SET STATISTICS IO ON 
EXEC QueryCustomers 100000
go

在這裏插入圖片描述

在這裏插入圖片描述

  執行計劃如上所示,出現了聚集索引掃描,產生了523個邏輯讀,由於數據量的巨大到達了臨界點,SQL Server並沒有選擇使用非聚集索引。
  然後我們假設現在SQL Server出現了一些問題,比如重啓或者failover,這會導致服務器緩存的計劃丟失。這裏使用“DBCC FREEPROCCACHE”來模擬重啓。

DBCC FREEPROCCACHE
GO

  重啓之後,有人調用這個存儲過程,但是這次使用了1作爲參數,大家可以想象得到,這次應該會有非聚集索引的參與:

SET STATISTICS IO ON 
EXEC QueryCustomers 1
go

在這裏插入圖片描述
  執行計劃如上,確實使用了索引查找(索引查找就是非聚集索引查找),同時搭配了Key Lookup,因爲非聚集索引沒有覆蓋所有列而存儲過程中定義了SELECT *,所以需要聚集索引來輔助。不過由於需要查找的數據量很小,所以這個問題不大。
  接下來再次執行:

SET STATISTICS IO ON 
EXEC QueryCustomers 100000
go

在這裏插入圖片描述
在這裏插入圖片描述
  可以看到邏輯讀變成了 200174, 而之前只是 523,如果在生產環境中出現,絕對是緊急事件。然後如果沒有Query Store你得花很多時間去找問題,不過現在可以使用Query Store來發現這個問題。
  同一個存儲過程,由於參數得不同執行計劃也不同,同時因爲緩存導致計劃被重用到不合適的場景,這個也叫參數嗅探,也叫計劃迴歸。下面來看看怎麼用Query Store解決。

使用Query Store

  我們打開SSMS中的Query Store界面,首先我們打開【Top Resource Consuming Queries,資源消耗量最多的幾個查詢】查看最耗資源的查詢,在【Metri,指標】那裏我們選擇邏輯讀。
在這裏插入圖片描述

  你可以看到有幾個查詢,其中第一名的是剛纔構造環境時用的循環10萬次插入,這裏說明即使服務器被重啓,Query Store還是可以捕獲到,不像執行計劃那樣內存緩存機制。
  現在看第二名,這次是我們剛纔的查詢了。下圖展示的是計劃 ID 28的執行計劃,右上方可以kan’daokandao 有兩個計劃ID,分別是因爲參數不同導致的兩個查詢,三個點代表了三個不同的計劃,我們可以看到橙色的應該就是參數是10萬的,但是因爲計劃迴歸導致執行計劃的變動。
在這裏插入圖片描述
  鼠標分別移到計劃ID爲28的兩個點中,可以看到下面的兩個圖,注意各種邏輯讀的值之間的差異。
在這裏插入圖片描述

在這裏插入圖片描述
  如果把鼠標移到計劃ID25處,注意計劃ID並非固定。可以看到執行計劃完全不同,而且邏輯讀也不同。
在這裏插入圖片描述

  雖然ID 28的下面那個邏輯讀遠小於其他兩個,但是從平均來看,計劃ID25的更加適合大部分情況,所以我們用ID25來強制替代這個語句的所有執行計劃。
在這裏插入圖片描述
在這裏插入圖片描述
  可以看到打了一個勾。現在一起來執行下面的命令看看執行計劃:
在這裏插入圖片描述
  此時已經強制了使用聚集索引掃描作爲所有參數的執行方式。而且I/O都是一樣的:
在這裏插入圖片描述
  現在看上去是解決問題了,但是我個人認爲,完善索引,表設計和語句寫法纔是最終解決問題的辦法,這個僅僅是“強制執行計劃”而已,如果沒有任何一個執行計劃可以覆蓋所有場景,那這個功能是沒有起到效用的。

下一文:Azure SQL DB/DW 系列(6)——Query Store案例(3)——查看等待信息

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