SQL Server 2016 查詢存儲性能優(yōu)化小結(jié)
作為一個DBA,排除SQL Server問題是我們的職責(zé)之一,每個月都有很多人給我們帶來各種不能解釋卻要解決的性能問題。
我就多次聽到,以前的SQL Server的性能問題都還好且在正常范圍內(nèi),但現(xiàn)在一切已經(jīng)改變,SQL Server開始糟糕, 瘋狂的事情不能解釋。在這個情況下我介入,分析下整個SQL Server的安裝,最后用一些神奇的調(diào)查方法找出性能問題的根源。
但很多時候問題的根源是一樣的:所謂的計劃回歸(Plan Regression),即特定查詢的執(zhí)行計劃已經(jīng)改變。昨天SQL Server已經(jīng)緩存了在計劃緩存里緩存了一個好的執(zhí)行計劃,今天就生成、緩存最后重用了一個糟糕的執(zhí)行計劃——不斷重復(fù)。
進入SQL Server 2016后,我就變得有點多余了,以為微軟引進了查詢存儲(Query Store)。這是這個版本最熱門的功能!查詢存儲幫助你很容易找出你的性能問題是不是計劃回歸造成的。如果你找到了計劃回歸,這很容易強制一個特定計劃不使用計劃向?qū)?。聽起來很有意思?讓我們通過一個特定的場景,向你展示下在SQL Server 2016里,如何使用查詢存儲來找出并最終修正計劃回歸。
查詢存儲(Query Store)——我的對手
在SQL Server 2016里,在你使用查詢存儲功能前,你要對這個數(shù)據(jù)庫啟用它。這是通過ALTER DATABASE語句實現(xiàn),如你所見的下列代碼:
CREATE DATABASE QueryStoreDemo GO USE QueryStoreDemo GO -- Enable the Query Store for our database ALTER DATABASE QueryStoreDemo SET QUERY_STORE = ON GO -- Configure the Query Store ALTER DATABASE QueryStoreDemo 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
在線幫助為你提供了各個選項的詳細信息。接下來我創(chuàng)建一個簡單的表,創(chuàng)建一個非聚集索引,最后插入80000條記錄。
-- Create a new table 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 a supporting new Non-Clustered Index. CREATE UNIQUE NONCLUSTERED INDEX idx_Test ON Customers(Value) GO -- Insert 80000 records DECLARE @i INT = 1 WHILE (@i <= 80000) 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
為了訪問我們的表,我額創(chuàng)建了一個簡單的存儲過程,傳入value值作為過濾謂語。
-- Create a simple stored procedure to retrieve the data CREATE PROCEDURE RetrieveCustomers ( @Value INT ) AS BEGIN SELECT * FROM Customers WHERE Value < @Value END GO
現(xiàn)在我用80000的參數(shù)值來執(zhí)行存儲過程。
-- Execute the stored procedure. -- This generates an execution plan with a Key Lookup (Clustered). EXEC RetrieveCustomers 80000 GO
現(xiàn)在當(dāng)你查看實際的執(zhí)行計劃時,你會看到查詢優(yōu)化器已經(jīng)選擇了有419個邏輯讀的聚集索引掃描運算符。SQL Server并沒有使用非聚集索引,因為這樣沒有意義,由于臨界點。這個查詢結(jié)果并沒有選擇性。
現(xiàn)在假設(shè)SQL Server發(fā)生了些事情(例如重啟,故障轉(zhuǎn)移),SQL Server忽略已經(jīng)緩存的計劃,這里我通過執(zhí)行DBCC FREEPROCCACHE從計劃緩存里抹掉每個緩存的計劃來模擬SQL Server重啟(不要在生產(chǎn)環(huán)境里使用?。?。
-- Get rid of the cached execution plan... DBCC FREEPROCCACHE GO
現(xiàn)在有人再次調(diào)用你的存儲過程,這次輸入?yún)?shù)值是1。這次執(zhí)行計劃不一樣,因為現(xiàn)在在執(zhí)行計劃里你會有書簽查找。SQL Server估計行數(shù)是1,在非聚集索引里沒有找到任何行。因此與非聚集索引查找結(jié)合的書簽查找才有意義,因為這個查詢是有選擇性的。
現(xiàn)在我再執(zhí)行用80000參數(shù)值的查詢。
-- Execute the stored procedure EXEC RetrieveCustomers 1 GO -- Execute the stored procedure again -- This introduces now a plan regression, because now we get a Clustered Index Scan -- instead of the Key Lookup (Clustered). EXEC RetrieveCustomers 80000 GO
當(dāng)你再次看STATISTICS IO的輸出,你會看到這個查詢現(xiàn)在產(chǎn)生了160139個邏輯讀——剛才的查詢只有419個邏輯讀。這個時候DBA的手機就會響起,性能問題。但今天我們要不同的方式解決——使用剛才啟用的查詢存儲。
當(dāng)你再次看實際的執(zhí)行計劃,在你面前你會看到有一個計劃回歸,因為SQL Server剛重用了書簽查找的的計劃緩存。剛才你有聚集索引掃描運算符的執(zhí)行計劃。這是SQL Server里參數(shù)嗅探的副作用。
讓我們通過查詢存儲來詳細了解這個問題。在SSMS里的對象資源管理器里,SQL Server 2016提供了一個新的結(jié)點叫查詢存儲,這里你會看到一些報表。
【前幾個資源使用查詢】向你展示了最昂貴的查詢,基于你選擇的維度。這里切換到【邏輯讀取次數(shù)】。
這里在你面前有一些查詢。最昂貴的查詢生成了近500000個邏輯讀。這是我們的初始語句。這已經(jīng)是第一個WOW效果的的查詢存儲:SQL Server重啟后,查詢存儲的數(shù)據(jù)還是存在的!第2個是你存儲過程里的SELECT語句。在查詢存儲里每個捕獲的查詢都有一個標(biāo)示號——這里是7。最后當(dāng)你看報告的右邊,你會看這個查詢的不同執(zhí)行計劃。
如你所見,查詢存儲捕獲了2個不同的執(zhí)行計劃,一個ID是7,一個ID是8。當(dāng)你點擊計劃ID時,SQL Server會在報表的最下面為你顯示估計的執(zhí)行計劃。
計劃8是聚集索引掃描,計劃7是書簽查找。如你所見,使用查詢存儲分析計劃回歸非常簡單。但你現(xiàn)在還沒結(jié)束。你現(xiàn)在可以對指定的查詢強制執(zhí)行計劃。 現(xiàn)在你知道包含聚集索引掃描的執(zhí)行計劃有更好的性能。因此現(xiàn)在你可以通過點擊【強制執(zhí)行計劃】強制查詢7使用執(zhí)行計劃。
搞定,我們已經(jīng)解決問題了!
現(xiàn)在當(dāng)你執(zhí)行存儲過程(用80000的輸入?yún)?shù)值),在執(zhí)行計劃里你可以看到聚集索引掃描,執(zhí)行計劃只生成419個邏輯讀——很簡單,是不是?絕對不是?。。?!
微軟告訴我們只給修正SQL Server性能相關(guān)的“新方式”。你只是強制了特定的計劃,一切都還好。這個方法有個大的問題,因為性能問題的根源并沒有解決!這個問題的關(guān)鍵是因為書簽查找計劃沒有穩(wěn)定性。取決于首次執(zhí)行計劃默認的輸入值,執(zhí)行計劃因此就被不斷重用。
通常我會建議調(diào)整下你的索引設(shè)計,創(chuàng)建一個覆蓋索引來保證計劃的穩(wěn)定性。但強制特定執(zhí)行計劃只是臨時解決問題——你還是要修正你問題的根源。
小結(jié)
不要誤解我:SQL Server 2016里的查詢存儲功能很棒,可以幫你更容易理解計劃回歸。它也會幫你“臨時”強制特定的執(zhí)行計劃。但性能調(diào)優(yōu)的目標(biāo)還是一樣:你要找到問題根源,嘗試解決問題——不要在外面晃蕩!
版權(quán)聲明:本站文章來源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請保持原文完整并注明來源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學(xué)習(xí)參考,不代表本站立場,如有內(nèi)容涉嫌侵權(quán),請聯(lián)系alex-e#qq.com處理。