在今天的文章里,我想給你快速展示下當我們從表里刪除記錄時,在SQL Server里發生了什么。首先我們來創建一個簡單的表,在8KB的頁上剛好能插入4條記錄。
1 -- Create a simple table where 4 records fit onto 1 page2 CREATE TABLE TestTable3 (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的會話窗口:
1 -- Enable the Trace Flag 36042 DBCC TRACEON(3604)3 GO
我們可以使用DBCC IND命令返回所有分配給指定表或索引的頁:
1 -- Retrieve all pages of the table2 DBCC IND(DataModifications, TestTable, -1)3 GO
從輸出可以看到,2個頁屬于我們的表:數據頁本身,還有IAM(索引分配圖(index allocation map))頁。
我這里的頁號是118,通過DBCC PAGE命令傾倒出頁面:
1 -- Dump out one specific page2 DBCC PAGE (DataModifications, 1, 118, 2)3 GO
當你使用選項2的第3個參數傾倒,SQL Server返回你16進制的頁傾倒,包括在頁尾所謂的行偏移數組(Row Offset Array),不以任何方式影響數據。
行偏移數組指向在頁上的物理位置,即每條記錄存儲的地方。第1條記錄總是直接存儲在頁頭偏移量96(0x60h)的地方。你也會看到,行偏移數組是逆向增長的?,F在讓我們從表里刪除第2條記錄:
1 -- Delete a record from the table2 DELETE FROM TestTable3 WHERE Col1 = 24 GO
通常這里你會期待記錄從頁里刪除。但事實上并不如此:當你再次執行DBCC PAGE命令時,你會看到在頁上老記錄的內容還是可以看到。在DELETE操作期間,SQL Server唯一做的是,在頁尾行偏移數組里,對應的槽無效了。
如你所見,第2個槽的偏移量是0x0,這是無效的,意味著我們的記錄被刪除了。在頁開始部分,你總會找到96 bytes的頁頭?,F在讓我們從表里刪除其它的剩余3條記錄。
1 -- Delete all the remaining records from the table2 DELETE FROM TestTable3 GO
當你再次用DBCC PAGE命令查看頁,你會看到頁全部內容還是沒改變:每條記錄的每個數據在頁上還是物理存在的!但是在行偏移數據里每條記錄都指向偏移量0x0,這意味著每條記錄都被刪除。這與你的表是否使用了聚集索引無關——老數據在頁上一直存在。現在的問題是,SQL Server什么時候會初始化頁?當你現在插入新的記錄,SQL Server會覆蓋頁的原始內容。但在我們的情況里,這只是物理部分,第1條記錄存儲的位置。你還是能看到其它“刪除”的記錄內容。當你在頁尾看下行偏移數組,你會看到它已被SQL Server完全初始化了,也意味著你在行偏移數組里你現在只有1個槽了:
當你下次授權給程序sysadmin特權時,要考慮下這個情況了。使用合適的命令,這些程序還是能看到已經“刪除”的數據。
感謝關注!
新聞熱點
疑難解答