當記錄長度發生變化時,數據頁級別會發生什麼
幾天來,我一直在工作中搜尋網路,試圖了解 DBMS(SQL Server 2008 R2 和其他)如何如此快速地處理向巨大表的末尾添加一列。
在較高的層次上,您可能會想:我可以在末尾放置一個指向新列的指針。但是,在頁面級別,數據頁面不是填充了單獨的記錄嗎?添加一列是否意味著每個已滿的頁面都需要拆分?
即使是未滿的頁面也需要大量數據處理才能將該列添加到每條記錄的末尾,更新所有插槽數組,然後通過任何現有索引和/或 IAM 和 GAM 頁面級聯所有指針更改?
我唯一能想到的是,所有新的列數據都添加到新頁面,沒有其餘的記錄,並且在整個表樹結構中添加了指針以引用新的列頁面。但是,這似乎會破壞空間局部性。如果是這樣,即使我們沒有特別請求,DBMS 是否會在幕後處理數據
REBUILD
?我正在談論帶有頁面的 DBMS 記憶體管理的位級別,並詢問 DBMS 如何能夠如此快速地將列(允許或不允許 NULL 值)添加到一組現有記錄中,即使這些記錄已經作為一組存在數據頁中的位。
DBMS(SQL Server 2008 R2 和其他)如何如此快速地將列添加到大表的末尾。
好吧,這裡有一個錯誤的假設,即添加新列總是很快完成。這不是一個真實的說法。
現在,當添加允許
NULL
s的列時*,*可以快速完成,因為表定義的元數據得到更新,但此時NULL
並未物理添加到數據頁中。SQL Server 可以將正確的結果返回NULL
給查詢,因為邏輯上很明顯“值”是什麼。當插入或更新行時,寫入數據頁的記錄確實包括NULL
(對於固定長度的列,除非該SPARSE
選項用於新列或在聚集索引上啟用了數據壓縮)。NULL
但是其餘未更新的行在索引之前不會物理添加REBUILD
。但是,當添加標記為 的列時
NOT NULL
,在 SQL Server 2012 之前(即使在那時,僅當新值是執行時常量時),實際值在那個時刻被物理寫入數據頁,並且該操作可以花費很長時間,具體取決於表中有多少行和/或多少數據。您可以找到大量有關嘗試克服此問題的問題和文章,因為具有許多 GB 數據和/或數億行的表可能需要數小時才能添加新NOT NULL
列。然後在 SQL Server 2012(僅限企業版,這也意味著開發版)中出現了一個非常棒的新功能,其中添加
NOT NULL
具有預設值的新列可能是即時的、僅限元數據的操作,就像添加標記為的列一樣NULL
. 唯一需要注意的是數據類型不是 LOB(例如MAX
-typesXML
等)或基於 CLR 的類型,並且值是執行時常量(即主要是文字值)。類似的東西NEWID()
不會是瞬時的,因為每行都需要不同的值。但是對於執行時常量的值,操作可以通過查看給出邏輯上明顯值SELECT
的元數據來輕鬆獲得正確的值。DEFAULT
ALTER TABLE的 MSDN 頁面在Locks 和 ALTER TABLE部分(在“將 NOT NULL 列添加為線上操作”下)討論了這種行為。