[SQL SERVER]降低tempdb競爭

之前我寫了一篇 [SQL SERVER]了解制式範圍和混和範圍提到tempdb資源競爭問題,

去年參加SQL PASS年終聚會時,剛好有朋友詢問我當時如何發現該問題,這篇就來記錄整個過程。

有那些操作或功能會使用tempdb空間

1 Order by、distinct、group by、hash和spool操作

2 Query

3 Triggers

4 Snapshot isolation and read committed snapshot (RCSI)

5 MARS

6 Online index create or rebuild

7 Use Temporary tables, table variables, and table-valued functions

8 DBCC CHECK

9 LOB parameters

10 Cursors

11 Service Broker and event notification

12 XML and LOB variables

13 Query notifications

14 Database mail

15 User-defined functions

可以看到很多一般操作和功能都會使用到tempdb資源,所以一般來說tempdb是相當忙碌的,所以好好管理tempdb真的相當重要。

Note: Tempdb分3種物件使用量,使用者、內部和版本存放區。

 

什麼是tempdb I/O分配瓶頸

多核心環境下,當並行連線數量多且使用tempdb資源時(如建立temp table、drop temp table、work table..等),

新增資料至temp table時,這時候PFS(page free space)、GAM(global allocation map)和SGAM(share global allocation map)都必須變更相關資訊,

SQL Server會在這些頁面上使用SH和Update的latches來維持內部資料一致性(由於是記憶體中的操作,所以過程相當短暫),

因預設分配混和範圍,並行性高系統這時有很大機會發生latch上的競爭衝突(因大家都在搶相同資源)。

 

如何確認tempdb有I/O分配瓶頸

我個人是使用擴充事件來監控,沒用過的朋友可以參考我以前寫的文章

[SQL SERVER][Maintain]擴充的事件(1)

[SQL SERVER][Maintain]擴充的事件(2)

[SQL SERVER][Maintain]監控Deadlock

 

使用擴充事件監控,我們要監控sqlos所發生的資源等待事件

--Find the event name allows to look at wait statistics

select xo.name,object_type,xo.[description]

from sys.dm_xe_objects xo INNER JOIN sys.dm_xe_packages xp

   on xp.[guid] = xo.[package_guid]

where xo.[object_type] = 'event' AND xo.name LIKE '%wait%'

order by xp.[name];

下面我會將相關資訊使用非同步方式寫入File來減少Server資源開銷但需注意硬碟空間使用量

--Drop the event if it already exists

DROP EVENT SESSION Monitor_wait_info_tempdb ON SERVER;

GO

--Create the event

CREATE EVENT SESSION Monitor_wait_info_tempdb ON SERVER

ADD EVENT sqlos.wait_info

(

   --Add additional columns to track

   ACTION (sqlserver.database_id, sqlserver.sql_text, sqlserver.session_id, sqlserver.tsql_stack) 

    WHERE sqlserver.database_id = 2 --filter database id = 2 i.e tempdb

    --This allows us to track wait statistics at database granularity

) --As a best practise use asynchronous file target, reduces overhead.

ADD TARGET package0.asynchronous_file_target(

     SET filename='E:\sqlextenevent\Monitor_wait_info_tempdb.etl'

      , metadatafile='E:\sqlextenevent\Monitor_wait_info_tempdb.mta')

GO

--start the session

ALTER EVENT SESSION Monitor_wait_info_tempdb ON SERVER

STATE = START;

GO

模擬tempdb I/O分配瓶頸(多條session同時執行)

--using table variables, temp tables, temp tables with named constraints

declare @test1 TABLE (c1 INT NOT NULL, c2 datetime)

INSERT @test1 SELECT 1, GETDATE()

--drop table #test1

GO 1000

CREATE TABLE #test1 (c1 INT NOT NULL, c2 datetime)

INSERT #test1 SELECT 1, GETDATE()

DROP TABLE #test1

GO 1000

CREATE TABLE #test1 (c1 INT NOT NULL, c2 datetime, CONSTRAINT pk_test PRIMARY KEY CLUSTERED(c1))

INSERT #test1 SELECT 1, GETDATE()

DROP TABLE #test1

GO 1000

 

查詢相關等待資訊

SELECT wait_typeName

      , SUM(total_duration) AS total_duration

      , SUM(signal_duration) AS total_signal_duration

FROM (

SELECT

 FinalData.R.value ('@name', 'nvarchar(50)') AS EventName, 

  FinalData.R.value ('data(data/value)[1]', 'nvarchar(50)') AS wait_typeValue,

  FinalData.R.value ('data(data/text)[1]', 'nvarchar(50)') AS wait_typeName,

  FinalData.R.value ('data(data/value)[3]', 'int') AS total_duration,

  FinalData.R.value ('data(data/value)[4]', 'int') AS signal_duration,

  FinalData.R.value ('(action/.)[1]', 'nvarchar(50)') AS SessionID,

  FinalData.R.value ('(action/.)[2]', 'nvarchar(max)') AS SQLText,

  FinalData.R.value ('(action/.)[3]', 'nvarchar(50)') AS DatabaseID

 

FROM

( SELECT CONVERT(xml, event_data) AS xmldata

   FROM sys.fn_xe_file_target_read_file

   ('E:\sqlextenevent\Monitor_wait_info_tempdb*.etl', 'E:\sqlextenevent\Monitor_wait_info_tempdb*.mta', NULL, NULL)

) AsyncFileData

CROSS APPLY xmldata.nodes ('//event') AS FinalData (R)) xyz

WHERE --wait_typeName like 'page%'

wait_typeName NOT IN ('SLEEP_TASK')

 GROUP BY wait_typeName

 ORDER BY total_duration

 GO

這裡顯示tempdb相關等待資訊,可以看到存在I/O分配瓶頸。

 

如何減少tempdb 資源競爭並優化

*建立多data file。我自己一般會建立CPU core數量的1/2或1/4,但該數量不可超過cpu core數量或8。

*啟用trace flag 1117和1118。如果建立多data file還是無法有效減少tempdb資源競爭的話,那麼啟用trace flag 1117和1118會有不錯的改善。

*避免產生過多I/O。相關查詢請確認都使用正確索引(index seek),可參考[SQL SERVER][TSQL]獲取各種高成本查詢語法 找出高成本I/O TSQL,並進行優化作業。

*合理的檔案初始大小和自動成長大小。過大的成長大小可能導致timeout,過小的成長大小將有更多碎片,所以需依環境取得一個平衡。

 

 

如何壓縮tempdb空間大小

目前聽到大部分都是使用restart sql service方法來減少tempdb所使用空間,因為重新啟動會建立新的tempdb(會參考model某些設定),但我個人實務上很少這樣處理,我建議使用 DBCC SHRINKFILE來壓縮相關檔案大小。

--壓縮tempdb交易紀錄檔案大小

DBCC SHRINKFILE('templog', TRUNCATEONLY )--截斷交易紀錄檔

DBCC SHRINKFILE('templog', 500 )--500mb

 

--壓縮tempdev 檔案大小

DBCC SHRINKFILE('tempdev', 1000 )--1000mb

 

 

參考

tempdb 磁碟空間不足的疑難排解

[SQL SERVER]了解制式範圍和混和範圍

[SQL SERVER][Memo] tempdb datafile該切多少份?

SQL Server Tempdb Usage and Bottlenecks tracked with Extended Events

[SQL SERVER][Performance] tempdb 優化

Monitoring tempdb space usage and scripts for finding queries which are using excessive tempdb space

如何壓縮 SQL Server 中的 Tempdb 資料庫