本文屬於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都是一樣的:
現在看上去是解決問題了,但是我個人認爲,完善索引,表設計和語句寫法纔是最終解決問題的辦法,這個僅僅是“強制執行計劃”而已,如果沒有任何一個執行計劃可以覆蓋所有場景,那這個功能是沒有起到效用的。