首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL Server 2005 ALTER TABLE 修改結構對資料儲存的深度影響:DBCC IND/PAGE 實測揭秘資料列遷移、轉發指標與空間膨脹

     編輯:關於SqlServer

前言:ALTER TABLE 的隱藏代價

資料庫開發者常認為 ALTER TABLE ... ALTER COLUMN 只是修改中繼資料。實際上,修改固定長度欄位長度 (如 CHAR(985) → CHAR(1000)) 會觸發資料列重寫、頁面分裂、轉發指標產生、空間嚴重膨脹。本文以實測 DBCC IND/DBCC PAGE 揭示底層儲存行為。

一、實驗環境建立


USE Test;
GO
IF OBJECT_ID('tb') IS NOT NULL DROP TABLE tb;
GO
CREATE TABLE tb (id INT IDENTITY(1,1), col CHAR(985));
INSERT INTO tb SELECT 'aaaa' UNION ALL SELECT 'bbbb' UNION ALL SELECT 'cccc'
UNION ALL SELECT 'dddd' UNION ALL SELECT 'eeee' UNION ALL SELECT 'ffff'
UNION ALL SELECT 'gggg' UNION ALL SELECT 'hhhh' UNION ALL SELECT 'iiii'
UNION ALL SELECT 'jjjj';  -- 10 列
EXEC sp_spaceused 'tb';
-- 結果:24 KB (資料 16 KB + 索引 8 KB),佔 2 頁 (8K×2)

理論計算

  • 列長 = 7 (列標頭) + 4 (id) + 985 (col) + 2 (欄位計數/位移) = 998 bytes。
  • 單頁可用 8096 bytes → 8096/998 ≈ 8.1 列/頁 → 需 2 頁。

二、修改欄位長度:CHAR(985) → CHAR(1000)


ALTER TABLE tb ALTER COLUMN col CHAR(1000);
EXEC sp_spaceused 'tb';
-- 結果:32 KB (資料 24 KB + 索引 8 KB),佔 3 頁!

理論預期 vs 實際

  • 新列長 = 7 + 4 + 1000 + 2 = 1013 bytes。
  • 8096/1013 ≈ 7.99 → 理論仍 2 頁即可。
  • 實際卻變 3 頁,空間暴增 33%。

三、DBCC IND / PAGE 深度剖析

1. DBCC IND - 列出物件頁面


DBCC IND(Test, tb, 0);  -- 0 = 僅列內資料頁 + IAM

關鍵欄位:PageFID (檔案號)、PagePID (頁號)、PageType (1=資料頁)。

2. DBCC PAGE - 檢視頁面內容


DBCC TRACEON(3604);  -- 輸出導向用戶端
DBCC PAGE(Test, 1, 114, 1);  -- 檔案1, 頁114, 選項1(列標頭+資料+位移表)

修改前 - 第 1 頁 (Page 114)

Slot 0, Offset 0x60 (96), Length 996  -- 列標頭 96 bytes 後開始
Slot 1, Offset 0x444 (1092), Length 996
...
Slot 7, Offset 0x1B9C (7068), Length 996  -- 8 列滿
OFFSET TABLE: 7→7068, 6→6072, ..., 0→96

修改後 - 第 1 頁 (Page 114)

Slot 0, Offset 0x60, Length 9    -- 原 Slot 0 變成 9 bytes 轉發指標!
00000000: 04 AF0000 00 010002 00  -- 04=狀態, AF0000=頁面175, 0100=檔案1, 0200=Slot 2

Slot 1, Offset 0x69, Length 1996  -- 原 Slot 1 變雙倍長!
00000000: 10 00C907 02000000 62626262... (bbbb 出現兩次!)
...
0300  -- 欄位計數變 3 (原 2 + 新增 1)

關鍵發現

  1. 並非就地擴充欄位,而是在每列末尾新增一個欄位 (長度 1000),導致列長 996 → 1996 (≈ 996+1000)。
  2. 頁面分裂:原頁面塞不下雙倍長列表,觸發分裂,首列 (Slot 0) 移至新頁 (Page 175),原位置留下 9 bytes 轉發指標 (Forwarding Pointer)。
  3. 欄位計數 +1:每列末尾 0300 顯示現有 3 個欄位 (id, col_old, col_new)。

修改後 - 第 2 頁 (Page 175,新分裂頁)

Slot 0 (原 Slot 0 aaaa), Offset 0x1014, Length 1996
Slot 1 (原 Slot 8 iiii), Offset 0x17E0, Length 1996
Slot 2 (原 Slot 9 jjjj), Offset 0x60,   Length 2010  -- 多 14 bytes!
Slot 3 (原 Slot 2 cccc), Offset 0x83A,  Length 2010  -- 多 14 bytes!
OFFSET TABLE: 3→2106, 2→96, 1→6112, 0→4116
  • 分裂頁首列 (Slot 0) 為從首頁移來的 aaaa,Offset 0x60 (緊接頁標頭)。
  • 原屬第 2 頁的 iiii/jjjj 被擠到較高位移。
  • 從首頁移來的列 (Slot 2,3) 長度 2010 = 1996 + 14,額外 14 bytes 為回溯指標 (Back Pointer),指向原頁面/位置。

修改後 - 第 3 頁 (Page 45,新配置頁)

Slot 0 (原 Slot 4 eeee), Offset 0x452, Length 2010
Slot 1 (原 Slot 6 gggg), Offset 0x101E, Length 2010
OFFSET TABLE: 1→4126, 0→1106
  • 剩餘移來的列 (eeee, gggg) 同樣帶 14 bytes 回溯指標。

四、儲存行為總結模型

列類別 原位置殘留 新位置結構 長度變化
未分裂留在原頁 (bbbb~hhhh)無原列 + 新欄位 (1000 bytes)996 → 1996 (+1000)
分裂移至新頁 (aaaa, cccc, eeee, gggg)9 bytes 轉發指標 (狀態+目標頁+檔案+Slot)原列 + 新欄位 + 14 bytes 回溯指標 (原頁+檔案+Slot)996 → 2010 (+1014)

五、空間膨脹量化估算

修改後實際列長 ≈ 1998 bytes (平均) → 單頁僅容 4 列 → 10 列需 3 頁 (24 KB)。
若表有 100 萬列:
原佔 ≈ 1000000 × 998 / 8096 ≈ 123,000 頁 ≈ 960 MB。
修改後 ≈ 1000000 × 1998 / 8096 ≈ 247,000 頁 ≈ 1.9 GB。
空間近乎翻倍!

六、解決方案:重建表法 (避免轉發指標與膨脹)


-- 1. 建立新結構表
CREATE TABLE tb_new (id INT IDENTITY(1,1), col CHAR(1000));

-- 2. 資料遷移 (最小記錄模式需簡單復原/大量記錄 + TABLOCK)
INSERT INTO tb_new WITH (TABLOCK) SELECT * FROM tb;

-- 3. 切換 (需排他鎖定窗口)
BEGIN TRANSACTION;
    DROP TABLE tb;
    EXEC sp_rename 'tb_new', 'tb';
COMMIT;

-- 4. 重建索引/統計資料
ALTER INDEX ALL ON tb REBUILD;
UPDATE STATISTICS tb;
  • 分割表企業版可用 ALTER TABLE ... SWITCH 瞬間切換,零停機。
  • ALTER TABLE ... REBUILD (SQL 2008+) 同樣會重建表,但仍可能保留轉發指標邏輯,建新表遷移最乾淨。

七、尚存謎題 (供進階研究)

  1. 轉發指標 9 bytes 結構中,狀態位 04 具體含義?
  2. 回溯指標 14 bytes 中,前 6 bytes 0100DA870004 固定值代表何意?
  3. 混合區 混合頁面配置演算法對最後一頁位移表排序的影響?
  4. SQL Server 如何在讀取時透過轉發指標/回溯指標定位真實資料?

結語

ALTER TABLE ALTER COLUMN 修改固定長度欄位,底層實作為「每列尾端新增欄位」而非就地擴充,導致列長暴增、頁面分裂、轉發指標/回溯指標雙重開銷、空間近乎翻倍。大型表務必採用建新表遷移 + 切換模式。DBCC IND/PAGE 是窺探儲存引擎行為的利器,建議 DBA 掌握基本用法,驗證結構變更真實影響。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved