很多時候在使用資料庫的時候,都會發現 tempdb 異常長大,那到底是誰使用到那裏呢 ? 以下的文章我們介紹一個尋找的方式,提供給大家參考看看
最近因為剛好要年底了,一些朋友和客戶開始規劃資料庫年度的例行處理,不少都是利用虛擬化的環境來做個演練測試,而大部分的也都沒有甚麼狀況,頂多就是忘記處理步驟或者是遺忘相關指令,討論一下就可以解決了,但就是有個朋友遇到的問題比較棘手一點,就花點時間整理一下相關處理步驟。
由於該朋友所管理的單一資料庫差不多有 400GB 的大小,但是在模擬年度相關作業處理的時候,居然會把 Tempdb 搞到掛掉,造成 SQL Server 無法正常啟動。對我來說這個實在不是很常見到的狀況,於是連線查看一下,原來是不知道甚麼樣的原因,造成 SQL Server 的 tempdb 長大到 150GB ,導致磁碟機 C 都被吃光了。知道問題那就容易處理多了,此部分可以利用 MSDN 上的方式,利用最低組態啟動 SQL Server 之後,便可以下指令 ALTER DATABASE tempdb MODIFY FILE 的方式,將 tempdb 指定存放到其他目錄下並且重新指定大小,再將原本的參數取消之後,就可以順利的重新啟動 SQL Server 了。在這裡為了也能讓效能有所改善,在考量資料庫主機有八個核心的狀況下,我們也同時將 tempdb 再多增加 3 個 ndf 檔案,這樣當有需要使用 tempdb 的時候,可以有較佳的效能。
雖然解決了問題,但朋友還是希望能找到問題的根源,以免下次又再發生同樣的狀況,所以我們用一些方法來找可能占用 tempdb 的相關處理:
- 暫時性物件 : 由於部分程式或者是處理的時候,可能會利用 # 或者是 ## 的方式建立一些暫時性的 Table 物件。使用 # 和 ## 則表示這個物件只存留在 Session 來存留的時候, Session 關閉則該物件也會自動刪除;
但如果是使用 ## 的話,則這個 Table 除非自己下刪除指令,否則會保留到 SQL Server 關閉。為了查看是否有這類型的物件,我們利用以下的指令來處理:
SELECT o.Name table_name, p.used_page_count * 8 used_size, p.reserved_page_count * 8 reserved_size, p.row_count FROM sys.dm_db_partition_stats p INNER JOIN sys.objects AS o ON o.object_id = p.object_id WHERE o.type_desc = 'USER_TABLE' AND o.is_ms_shipped = 0
如果這個部分如果發覺在系統上會有大量使用這類 temporary 的物件時,其實可以視情況將這些給換成 User-defined Table Variable 來取代會更較為合適。
- 查詢指令:當我們在下一些 SQL 指令的時候,像是又加入 Order By , Group By 之類的語法,SQL Server 必須撈大量的資料放到記憶體中來處理,但如果並沒有足夠記憶體的時候,便會使用 tempdb 的空間來做處理,此時我們會希望來找到這些指令,因此在 SQL Server 2005 之外,我們可以配合兩個 DMV 來做處理:
由於這兩個 DMV 所能抓到的是 Session 還存在的時候,當 SQL 命令還在執行的時候,則這時候可以配合 sys.dm_db_task_space_usage 來取得所使用的 Page,因此我們可以用類似以下的語法來進行處理:
;WITH task_space_usage AS ( SELECT session_id, request_id, SUM(internal_objects_alloc_page_count) AS alloc_pages, SUM(internal_objects_dealloc_page_count) AS dealloc_pages FROM sys.dm_db_task_space_usage WITH (NOLOCK) WHERE session_id <> @@SPID GROUP BY session_id, request_id ) SELECT TSU.session_id, TSU.alloc_pages / 128.0 AS [internal object MB space], TSU.dealloc_pages / 128.0 AS [internal object dealloc MB space], EST.text FROM task_space_usage AS TSU INNER JOIN sys.dm_exec_requests ERQ ON TSU.session_id = ERQ.session_id AND TSU.request_id = ERQ.request_id OUTER APPLY sys.dm_exec_sql_text(ERQ.sql_handle) AS EST WHERE EST.text IS NOT NULL ORDER BY 3 DESC如果命令已經執行完畢,上面這個指令就無法取得資訊,因此這個時候可以利用 sys.dm_db_session_space_uage,可以參考以下的使用與法來處理:
SELECT session_id, internal_objects_alloc_page_count/128.0 internal_alloc_size, internal_objects_dealloc_page_count/128.0 internal_dealloc_size FROM sys.dm_db_session_space_usage AS s WHERE s.session_id <> @@SPID and s.session_id > 50上述兩個 DMV 可能因為執行的時間差取到不同的資料,因此可以建立幾個監控的 Table,在可能有異常的時段內利用這兩個 DMV 的配合,定時將相關的資料給記錄下來之後(建議可以 3~5分鐘)再做比對,一般來說這種需要花比較長時間的處理,會比較有機會佔用大量的空間。
目前透過這些方式,再來配合效能監視器,讓我們可以找到一些特殊處理在短時間內耗用大量的 tempdb,希望如果有類似狀況的朋友,這些方法對你們有幫助。如果想要更清楚知道相關處理,可以前往參考資料所列的網址,裡面也都有許多詳細的說明。
備註 :
2016/01/13 修正 : 多謝 Rock 的指正,原本寫法 ## 開頭的物件,應該要寫成可以讓其他的 Session 共用讀取才對。不論用 # 或 ## 設定的暫時資料表,當 Session 關閉的時候正常來說物件都會刪除,但因為應用程式可能會採用 Connection Pool,因此變成程式中雖然看起來似乎 Session 關閉,但實際上 Session 並沒有關閉,只是交回由 Connection Pool 管控,因此造成對 SQL Server 來說並沒有真正關閉,使得建立的暫時性物件不會刪除,此部分 Rock 也建議另外一篇文章提供參考 ( 網址 )。
參考資料:
- 部落客文章 「tempdb 異常修復」
- MSDN 文章 「tempdb 磁碟空間不足的疑難排解」
- Technet 文章「Working with tempdb in SQL Server 2005」
- MSDN Blogs「Monitoring tempdb Transactions and Space usage」
- MSSQL tips 「Properly Sizing the SQL Server TempDB Database」
- MSSQL tips 「SQL Server Tempdb Usage and Bottlenecks tracked with Extended Events」