Tempdb
系統數據庫是一個全局資源,可供連接到 SQL Server 實例的所有用戶使用,並可用於保存下列各項:
-
顯式創建的臨時用戶對象,例如全局或局部臨時表、臨時存儲過程、表變量或游標。
-
SQL Server 數據庫引擎創建的內部對象,例如,用於存儲假脫機或排序的中間結果的工作表。
-
由使用已提交讀(使用行版本控制隔離或快照隔離事務)的數據庫中數據修改事務生成的行版本。
-
由數據修改事務為實現聯機索引操作、多個活動的結果集 (MARS) 以及 AFTER 觸發器等功能而生成的行版本。
tempdb 中的操作是最小日志記錄操作。 這將使事務產生回滾。 每次啟動 SQL Server 時都會重新創建 tempdb,從而在系統啟動時總是具有一個干凈的數據庫副本。 在斷開聯接時會自動刪除臨時表和存儲過程,並且在系統關閉后沒有活動連接。 因此 tempdb 中不會有什么內容從一個 SQL Server 會話保存到另一個會話。 不允許對 tempdb進行備份和還原操作。

在SQL SERVER 2000時代,TempDB的文件數需要和CPU核數保持1:1的關系,在SQL SERVER 2005和2008版本這條建議也適用,但由於SQL SERVER 2005+后的優化措施,你不再需要嚴格按照1:1的比例關系設置CPU核數和TempDB文件數,而是文件數和CPU核數的比例保持在1:2或是1:4就行了。
[題外話:在SQL PASS 2011我的好朋友Bob Ward,也是SQL CSS最牛的人。給出了一個新的公式:如果CPU核數小於等於8,使其比例保持在1:1,而如果CPU核數大於8,使用8個文件,當你發現閂鎖爭用現象時,每次額外加4個文件]
增加Tempdb文件數,只能有一個文件組(primary):
USE [master]
GO
ALTER DATABASE [tempdb] MODIFY FILE ( NAME = N'tempdev', SIZE = 307200KB )
GO
ALTER DATABASE [tempdb] MODIFY FILE ( NAME = N'templog', SIZE = 307200KB )
GO
ALTER DATABASE [tempdb] ADD FILE ( NAME = N'tempdev1', FILENAME = N'D:\Tempdb\tempdb1.ndf' , SIZE = 307200KB , FILEGROWTH = 102400KB )
遷移Tempdb數據庫到其他獨立磁盤:
select * from sys.sysfiles ---找到數據庫邏輯名
USE master;
GO
ALTER DATABASE tempdb
MODIFY FILE (NAME = tempdev, FILENAME = 'D:\Tempdb\tempdb.mdf');
GO
ALTER DATABASE tempdb
MODIFY FILE (NAME = templog, FILENAME = 'D:\Tempdb\templog.ldf');
GO
重啟生效!
收縮Tempdb及報錯處理:
重要說明:如果運行 DBCC SHRINKDATABASE,則 tempdb 數據庫不能正在發生其他活動。要確保在運行 DBCC SHRINKDATABASE 時其他進程無法使用tempdb,必須以單用戶模式啟動 SQL Server。
1. dbcc shrinkdatabase (tempdb, 'target percent')
-- This command shrinks the tempdb database as a whole
2.
use tempdb go dbcc shrinkfile (tempdev, 'target size in MB') go -- this command shrinks the primary data file dbcc shrinkfile (templog, 'target size in MB') go -- this command shrinks the log file, look at the last paragraph.
在 SQL Server 7.0 中,事務日志收縮是一個推遲操作,您必須執行日志截斷和備份,以幫助進行數據庫中的收縮操作。但是,默認情況下,tempdb 將 trunc log on chkpt 選項設置為“打開”(ON);這樣,您就無需為該數據庫執行日志截斷
當正在使用 tempdb 時,如果您嘗試通過使用 DBCC SHRINKDATABASE 或 DBCC SHRINKFILE 命令收縮它,可能會收到與以下類型相似的多個一致性錯誤,並且收縮操作可能失敗:
Server:Msg 2501, Level 16, State 1, Line 1 Could not find table named '1525580473'.Check sysobjects.
- 或 -
Server:Msg 8909, Level 16, State 1, Line 0 Table Corrupt:Object ID 1, index ID 0, page ID %S_PGID.The PageId in the page header = %S_PGID.
盡管錯誤 2501 可能不表示 tempdb 中的任何損壞,但它會導致收縮操作失敗。與其不同,錯誤 8909 可能表示 tempdb 數據庫中的損壞。應重新啟動 SQL Server 來重新創建 tempdb 並清除一致性錯誤。但是,請記住像錯誤 8909 這樣的物理數據損壞可能有其他原因,這包括輸入/輸出子系統問題。
分配頁:
全局分配映射表(GAM):有每4 GB數據文件對應1 個GAM頁。它始終在數據文件的第2頁中,然后每511232頁加一頁。
共享全局分配映射表(SGAM):該盤區被用作混合(共享)程度的曲目。有每4 GB數據文件的1 SGAM頁。它始終是第3頁中的數據文件,然后重復每511232頁。
頁可用空間(PFS):跟蹤每一頁的分配狀態,大約多少可用空間可以使用。每1/2GB數據文件1 個PFS頁。它始終是第1頁中的數據文件,然后重復每8,088頁。
查找有關分配頁閂鎖爭
您可以使用動態管理視圖(DMV)SYS .dm_os_waiting_tasks 找到正在等待資源上的任務。等待PAGEIOLATCH或PageLatch等類型的任務正在經歷的爭奪。資源描述指向的頁面正經歷爭用,你可以簡單地分析,資源描述得到PageId。那么它只是一個數學問題來確定它是否是一個分配頁。
資源描述(舉例):
資源說明會的形式<數據庫ID>:<文件ID>:<頁碼>在tempdb始終是2.數據庫ID樣本資源描述可能看起來像2:3:18070499。我們希望把重點放在18070499頁ID。
用於確定頁面類型的公式如下:
GAM: (PageID - 2)%511232
SGAM:(頁PageID - 3)%511232
PFS: (PageID - 1)%8088
如果這些式中的一個等於0,則爭用該分配的頁面。
ML 映射頁
ML 代表最小日志(Minimally Logged )。這種頁被用來在BULK_LOGGED 恢復模式中,跟蹤自上次日志備份以來哪些區經過最小日志的修改操作。這樣下次日志備份時除了正常備份日志,還要包括在位圖中標明已修改的所有區。這種備份的區加上事務日志 就是自上次事務日志備份以來所發生的變化。一旦ML 頁被讀取,所有的位圖都會被清除。如果你沒有使用過BULK_LOGGED 恢復模式,那么ML 頁是不會被使用的。
ML 位圖與GAM 位圖的結構和覆蓋區間完全一致,就是位表示的意思不同:
1) 位為1 表示該區自上次日志備份來進過了最小日志操作的修改。
2) 位為0 表示該區未被修改。
DIFF 映射頁
DIFF 表示差異(differential )。這種頁被用來跟蹤自上次完整備份以來哪些區被修改過。說DIFF 位圖用來跟蹤自上次差異備份以后的變化是一個常見的誤解 。一個差異備份包含自上次完整備份以來的所有修改過的區。使用差異備份可以大大節省恢復時間——可以避免必須恢復自上次完整備份到最后一次差異備份期間的日志備份(以后專門介紹)。所有的位圖直到下次完整備份才會被清除。注意:所有上面的解釋我並沒有說是完整的數據庫 備份——因為完整或差異備份包括數據庫、文件組和文件層次的備份。
DIFF 位圖與GAM 位圖的結構和覆蓋區間完全一致,就是位表示的意思不同:
1 )位為1 表示該區自上次完整備份以來進行了修改。
2 )位為0 表示該區未被修改。
PFS 頁
PFS 表示頁可用空間。但是PFS 頁跟蹤的遠不止這些。和GAM 區間相似,每個數據庫文件同樣也被分割成(概念上)PFS 區間。一個PFS 區間是8088 頁或約64MB 。PFS 頁中不是位圖,它是字節圖,每個字節表示PFS 區間中的一頁(不包括PFS 頁本身)。
字節中每位的含義如下:
1 )位0-2 :頁中有多少可用空間
a)0x00 表示空
b)0x01 表示1~50% 滿
c)0x02 表示51~80% 滿
d)0x03 表示81~95% 滿
e)0x04 表示96~100% 滿
2 )位3 (0x08 ):頁中是否至少有一個ghost 記錄?
3 )位4 (0x10 ):是否為IAM 頁?
4 )位5 (0x20 ):是否為混合頁?
5 )位6 (0x40 ):頁是否已分配?(分配狀態位)
比如一個IAM 頁的PFS 字節為0x70 (已分配 + IAM 頁 + 混合頁)。你可以使用DBCC PAGE 來查看PFS 頁。
跟蹤可用空間只適用於存儲LOB 值(比如SQL SERVER 2000 中的text/image 類型;SQL SERVER 2005 中再加上varchar(max)/varbinary(max)/XML 類型以及行溢出數據)和堆數據頁。因為只有這些頁存儲的數據不用排序, 所以可以在任何位置插入。而像索引是有明確的順序的,所以插入點是沒有選擇的。
注意一點:重置PFS字節並不是很直觀。PFS字節不會完全重置(譯注:比如清0)而是要等到重新分配頁時。當頁釋放時,只是改變了PFS字節中的分配狀態位——這樣可以很方便的回滾釋放動作。
下面是一個例子。在數據庫中表T1 只有一行數據。使用DBCC PAGE 檢查IAM 頁如下:
PFS (1:1) = 0x70 IAM_PG MIXED_EXT ALLOCATED 0_PCT_FULL
如果我在一個顯式事務中刪除表,再執行DBCC PAGE ,結果如下:
PFS (1:1) = 0x30 IAM_PG MIXED_EXT 0_PCT_FULL
我們如果回滾事務,那么DBCC PAGE 的輸出結果又變成:
PFS (1:1) = 0x70 IAM_PG MIXED_EXT ALLOCATED 0_PCT_FULL
T_SQL:
Select session_id,
wait_type,
wait_duration_ms,
blocking_session_id,
resource_description,
ResourceType = Case
When Cast(Right(resource_description, Len(resource_description) - Charindex(':',resource_description, 3)) As Int) - 1 % 8088 = 0 Then 'Is PFS Page'
When Cast(Right(resource_description, Len(resource_description) - Charindex(':',resource_description, 3)) As Int) - 2 % 511232 = 0 Then 'Is GAM Page'
When Cast(Right(resource_description, Len(resource_description) - Charindex(':',resource_description, 3)) As Int) - 3 % 511232 = 0 Then 'Is SGAM Page'
Else 'Is Not PFS, GAM, or SGAM page'
End
From sys.dm_os_waiting_tasks
Where wait_type Like 'PAGE%LATCH_%'
And resource_description Like '2:%'
閂鎖(Latch)。閂鎖是SQL Server存儲引擎使用輕量級同步對象,用來保護多線程訪問內存內結構。
鎖(Locks) 閂鎖(Latches)
- 控制…… 事務 線程
- 保護…… 數據庫內容 內存中數據結構
- 模式…… 共享的(Shared), 保持(Keep),
更新(Update), 共享的(Shared),
排它的(Exclusive), 更新(Update),排它的(Exclusive),
意向的(Intension) 銷毀(Destroy)
- 死鎖…… 檢測並解決(detection&resolution) 通過嚴謹代碼來避免
- 保持在…… 鎖管理器的哈希表(Hashtable) 保護的數據結構(Protected Data Structure)
從表里可以看到,閂鎖支持更細粒度保持(Keep)和銷毀(Destroy)模式。保持閂鎖主要用來引用計數,例如當你想知道在指定閂鎖上有多少其它閂鎖在等待。銷毀閂鎖是最有限制的一個(它甚至會阻塞保持閂鎖),當閂鎖被銷毀時會用到,例如當惰性寫入器(Lazy Writer)想要釋放內存中的頁時。我們先介紹下各種閂鎖模式,然后說下各個閂鎖模式的兼容性。
NL(空閂鎖):
- 內部
- 未使用
KP(保持閂鎖):
- 可以由多個任務同時持有
- 只被一個DT模式的閂鎖阻塞
SH(共享閂鎖):
- 讀取數據頁的時候使用
- 可以由多個任務同事持有
- 阻塞EX模式和DT模式的閂鎖
UP(更新閂鎖):
- 寫入系統分配頁面和tempdb的行版本化頁面時使用
- 一個這種模式的閂鎖只能被一個單獨的任務持有
EX(排它閂鎖):
- 寫入數據頁的時候使用
- 一個這種模式的閂鎖只能被一個單獨的任務持有
DT(銷毀閂鎖):
- 很少使用
- 一個這種模式的閂鎖只能被一個單獨的任務持有
在SQL Server里,一致性不能只用鎖來獲得。SQL Server還是可以訪問沒被鎖管理器保護的共享數據結構,例如頁頭。還有SQL Server基於閂鎖基礎的其他組件也是,有單線程代碼路徑。好了,我們繼續講解SQL Server里的各種閂鎖類型,還有如何對它們進行故障排除
SQL Server區分3個不同閂鎖類別
- IO閂鎖 PageIOLatch
- 緩沖區閂鎖(BUF) PageLatch
- 非緩沖區閂鎖(Non-BUF) Latch
通過兩個系統視圖查看等待詳情: sys.dm_os_wait_stats sys.dm_os_latch_stats 查看;
SQL Server 2014內部使用163個閂鎖來同步共享數據結構的訪問。其中一個著名的閂鎖是FGCB_ADD_REMOVE,它用來保護文件組的文件組控制阻塞( File Group Control Block (FGCB)),在以下特定操作期間:
- 文件增長(手動或自動)
- 增加/刪除文件組文件
- 重新計算填充比重(Recalculating proportional fill weightings)
- 在循環分配期間,通過文件組的文件回收。
當你看到這個閂鎖排在前列是,你肯定有太多自動增長操作的問題,原因是你數據庫糟糕的默認配置。當查詢嘗試讀/寫保護的數據結構且需要等待一個閂鎖時,查詢就會進入掛起狀態,直到閂鎖可以成功獲取。因此查詢經過的整個查詢生命周期包括運行(RUNNING),掛起(SUSPENDED),可運行(RUNNABLE),最后再次運行(RUNNING)。因此,當查詢長時間把持閂鎖時,強制共享數據結構保護才有意義。那是因為改變查詢狀態也意味着進行Windows系統里的上下文切換,依據引入的CPU周期是個很昂貴的操作。