在今天的文章裏,我想給你快速展現下當咱們從表裏刪除記錄時,在SQL Server裏發生了什麼。首先咱們來建立一個簡單的表,在8KB的頁上恰好能插入4條記錄。 sql
1 -- Create a simple table where 4 records fit onto 1 page 2 CREATE TABLE TestTable 3 ( 4 Col1 INT IDENTITY(1, 1), 5 Col2 CHAR(2000) 6 ) 7 GO
接下來咱們插入4條記錄,這樣的話一個頁恰好所有填滿。數組
1 -- Insert 4 records 2 INSERT INTO TestTable VALUES 3 ( 4 REPLICATE('1', 2000) 5 ), 6 ( 7 REPLICATE('2', 2000) 8 ), 9 ( 10 REPLICATE('3', 2000) 11 ), 12 ( 13 REPLICATE('4', 2000) 14 ) 15 GO
爲了研究咱們堆表的細節,咱們使用DBCC PAGE命令來傾倒出分配的頁面。所以咱們還要啓用3604跟蹤標誌,這樣的話SQL Server從DBCC PAGE命令直接把結果輸入到咱們SSMS的會話窗口:spa
1 -- Enable the Trace Flag 3604 2 DBCC TRACEON(3604) 3 GO
咱們可使用DBCC IND命令返回全部分配給指定表或索引的頁: code
1 -- Retrieve all pages of the table 2 DBCC IND(DataModifications, TestTable, -1) 3 GO
從輸出能夠看到,2個頁屬於咱們的表:數據頁自己,還有IAM(索引分配圖(index allocation map))頁。blog
我這裏的頁號是118,經過DBCC PAGE命令傾倒出頁面: 索引
1 -- Dump out one specific page 2 DBCC PAGE (DataModifications, 1, 118, 2) 3 GO
當你使用選項2的第3個參數傾倒,SQL Server返回你16進制的頁傾倒,包括在頁尾所謂的行偏移數組(Row Offset Array),不以任何方式影響數據。ci
行偏移數組指向在頁上的物理位置,即每條記錄存儲的地方。第1條記錄老是直接存儲在頁頭偏移量96(0x60h)的地方。你也會看到,行偏移數組是逆向增加的。如今讓咱們從表裏刪除第2條記錄: rem
1 -- Delete a record from the table 2 DELETE FROM TestTable 3 WHERE Col1 = 2 4 GO
一般這裏你會期待記錄從頁裏刪除。但事實上並不如此:當你再次執行DBCC PAGE命令時,你會看到在頁上老記錄的內容仍是能夠看到。在DELETE操做期間,SQL Server惟一作的是,在頁尾行偏移數組裏,對應的槽無效了。get
如你所見,第2個槽的偏移量是0x0,這是無效的,意味着咱們的記錄被刪除了。在頁開始部分,你總會找到96 bytes的頁頭。如今讓咱們從表裏刪除其它的剩餘3條記錄。 it
1 -- Delete all the remaining records from the table 2 DELETE FROM TestTable 3 GO
當你再次用DBCC PAGE命令查看頁,你會看到頁所有內容仍是沒改變:每條記錄的每一個數據在頁上仍是物理存在的!可是在行偏移數據裏每條記錄都指向偏移量0x0,這意味着每條記錄都被刪除。這與你的表是否使用了彙集索引無關——老數據在頁上一直存在。
如今的問題是,SQL Server何時會初始化頁?當你如今插入新的記錄,SQL Server會覆蓋頁的原始內容。但在咱們的狀況裏,這只是物理部分,第1條記錄存儲的位置。你仍是能看到其它「刪除」的記錄內容。當你在頁尾看下行偏移數組,你會看到它已被SQL Server徹底初始化了,也意味着你在行偏移數組裏你如今只有1個槽了:
當你下次受權給程序sysadmin特權時,要考慮下這個狀況了。使用合適的命令,這些程序仍是能看到已經「刪除」的數據。
感謝關注!
https://www.sqlpassion.at/archive/2014/02/11/delete-operations-on-tables/