SQL Server內存占用的執行緩存的正確優化
以下的文章主要向大家講述的是正確優化SQL Server內存占用的執行緩存的實際操作方案,使用參數化查詢來對執行緩存占用進行減少。我們主要是通過如下例子來說明一下正確使用參數化查詢對緩存占用的影響。
為方便試驗,我們使用了一臺沒有其它負載的SQL Server進行如下實驗。
下面的腳本循環執行一個簡單的查詢,共執行10000次。首先,我們清空一下SQL Server已經占用的緩存:
dbcc freeproccache然后,執行腳本:
- DECLARE @t datetimeSET @t = getdate()SET NOCOUNT ONDECLARE @i INT,
- @count INT, @sql nvarchar(4000)SET @i = 20000WHILE @i <= 30000BEGIN SET
- @sql = 'Select @count=count(*) FROM P_Order Where MobileNo = ' + cast( @i as varchar(10) )
- EXEC sp_executesql @sql ,N'@count INT OUTPUT',
- @count OUTPUT SET @i = @i + 1ENDPRINT DATEDIFF( second, @t, current_timestamp
)輸出:DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯系。
11使用了11秒完成10000次查詢。我們看一下SQL Server緩存中所占用的查詢計劃:Select Count(*) CNT,sum(size_in_bytes) TotalSizeFrom sys.dm_exec_cached_plans查詢結果:共有2628條執行計劃緩存在SQL Server中。它們所占用的緩存達到:
92172288字節 = 90012KB = 87 MB。我們也可以使用dbcc memorystatus 命令來檢查SQL Server的執行緩存和數據緩存占用。執行結果如下:
執行緩存占用了90088KB,有2629個查詢計劃在緩存里,有1489頁空閑內存(每頁8KB)可以被數據緩存和其他請求所使用。我們現在修改一下前面的腳本,然后重新執行一下dbcc freeproccache。
再執行一遍修改后的腳本:
- DECLARE @t datetimeSET @t = getdate()SET NOCOUNT ONDECLARE @i INT,
- @count INT, @sql nvarchar(4000)SET @i = 20000WHILE @i <= 30000BEGIN SET
- @sql = 'select @countcount=count(*) FROM P_Order Where MobileNo =
- @i' EXEC sp_executesql @sql, N'@count int output, @i int', @count OUTPUT,
- @i SET @i = @i + 1ENDPRINT DATEDIFF( second, @t, current_timestamp )
輸出:DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯系。
1即這次只用1秒鐘即完成了10000次查詢。我們再看一下sys.dm_exec_cached_plans中的查詢計劃:Select Count(*) CNT,sum(size_in_bytes) TotalSize From sys.dm_exec_cached_plans查詢結果:共有4條執行計劃被緩存。
它們共占用內存: 172032字節 = 168KB。如果執行dbcc memorystatus,則得到結果:
有12875頁空閑內存(每頁8KB)可以被數據緩存所使用。到這里,我們已經看到了一個反差相當明顯的結果。在現實中,這個例子中的前者,正是經常被使用的一種執行SQL腳本的方式(例如:在程序中通過合并字符串方式拼成一條SQL語句,然后通過ADO.NET或者ADO方式傳入SQL Server執行)。
解釋一下原因:我們知道,SQL語句在執行前首先將被編譯并通過查詢優化引擎進行優化SQL Server內存占用,從而得到優化后的執行計劃,然后按照執行計劃被執行。對于整體相似、僅僅是參數不同的SQL語句,SQL Server可以重用執行計劃。但對于不同的SQL語句,SQL Server并不能重復使用以前的執行計劃,而是需要重新編譯出一個新的執行計劃。
同時,SQL Server在內存足夠使用的情況下,此時并不主動清除以前保存的查詢計劃(注:對于長時間不再使用的查詢計劃,SQL Server也會定期清理)。這樣,不同的SQL語句執行方式,就將會大大影響SQL Server中存儲的查詢計劃數目。
如果限定了SQL Server最大可用內存,則過多無用的執行計劃占用,將導致SQL Server可用內存減少,從而在執行查詢時尤其是大的查詢時與磁盤發生更多的內存頁交換。如果沒有限定最大可用內存,則SQL Server由于可用內存減少,從而會占用更多內存。
對此,我們一般可以通過兩種方式實現參數化查詢:一是盡可能使用存儲過程執行SQL語句(這在現實中已經成為SQL Server DBA的一條原則),二是使用sp_executesql 方式執行單個SQL語句(注意不要像上面的第一個例子那樣使用sp_executesql)。
在現實的同一個軟件系統中,大量的負載類型往往是類似的,所區別的也只是每次傳入的具體參數值的不同。所以使用參數化查詢是必要和可能的。另外,通過這個例子我們也看到,由于使用了參數化查詢,不僅僅是優化了SQL Server內存占用,而且由于能夠重復使用前面被編譯的執行計劃,使后面的執行不需要再次編譯,最終執行10000次查詢總共只使用了1秒鐘時間。
【編輯推薦】