Oracle 10g 引入 Automatic Segment Space Management (ASSM) 並支援 `ALTER TABLE ... SHRINK SPACE` 指令,可在線降低 High Water Mark (HWM) 回收碎片空間,無需傳統 `MOVE` 操作的鎖表停機。本文透過實測案例,詳細展示 ASSM 環境下 SHRINK 的操作步驟、空間回收效果、RowID 變化機制、索引維護行為、鎖模式與 Redo 產生量對比。
-- 確認表空間為 ASSM (SEGMENT_SPACE_MANAGEMENT = AUTO)
SELECT tablespace_name, block_size, extent_management, allocation_type, segment_space_management
FROM dba_tablespaces WHERE tablespace_name = 'ASSM';
-- 結果:ASSM 8192 LOCAL UNIFORM AUTO
關鍵限制:`SHRINK SPACE` **僅支援 ASSM 表空間**,字典管理或手動段空間管理表空間執行將報 `ORA-10635`。
-- 建立測試表
CREATE TABLE my_objects TABLESPACE assm AS SELECT * FROM all_objects; -- 47,828 筆
-- 製造碎片:刪除特定條件資料
DELETE FROM my_objects WHERE object_name LIKE '%C%'; -- 16,950 筆
DELETE FROM my_objects WHERE object_name LIKE '%U%'; -- 4,503 筆
DELETE FROM my_objects WHERE object_name LIKE '%A%'; -- 6,739 筆
COMMIT;
-- 傳統檢視 (非 ASSM 專用)
EXEC show_space('MY_OBJECTS', 'DLINGER');
-- Total Blocks: 680, Unused Blocks: 1
-- ASSM 專用檢視
EXEC show_space_assm('MY_OBJECTS', 'DLINGER');
/*
free space 0-25% : 0
free space 25-50% : 205
free space 50-75% : 180
free space 75-100% : 229
Full Blocks : 45
*/
分析:HWM 下 679 個區塊,僅 45 個滿載,超過 600 個區塊閒置率 25%~100%,嚴重碎片化,適合 SHRINK。
-- 1. 必須先啟用列移動 (Row Movement)
ALTER TABLE my_objects ENABLE ROW MOVEMENT;
-- 2. 執行收縮 (線上操作,無排他鎖)
ALTER TABLE my_objects SHRINK SPACE;
-- 3. 同步收縮索引 (CASCADE)
ALTER TABLE my_objects SHRINK SPACE CASCADE;
EXEC show_space('MY_OBJECTS', 'DLINGER');
/*
Total Blocks: 265 (從 680 降至 265,釋放 61% 空間)
Unused Blocks: 2
*/
EXEC show_space_assm('MY_OBJECTS', 'DLINGER');
/*
free space 25-50% : 1
free space 50-75% : 0
free space 75-100%: 0
Full Blocks : 249 (絕大多數區塊變滿載)
*/
HWM 從 679 降至 264,空間利用率大幅提升。
建立小表 `TEST_HWM` (10 筆資料,分佈 4 個連續區塊),刪除部分資料後觀察 RowID 變化:
| 操作 | RowID 變化 | 區塊範圍變化 | 資料物理順序 | 機制推測 |
|---|---|---|---|---|
| MOVE | **全部改變** | **遷移至新區塊** | 保持不變 | 以區塊為單位 Copy 到新 Segment |
| SHRINK | **部分改變** | **不變** | **改變** | 以**列為單位**,從尾部向頭部填補空洞 |
SHRINK 演算法邏輯:
優點:不需額外臨時空間 (MOVE 需 2 倍空間),線上執行僅持有 Row-X (SX) 鎖,允許並發 DML。
CREATE INDEX i_my_objects ON my_objects (object_id);
-- 刪除資料、SHRINK 後
SELECT index_name, status FROM user_indexes WHERE index_name = 'I_MY_OBJECTS';
-- 結果:VALID
Oracle 在移動列時同步維護索引 Entry,免去 `MOVE` 後必須 `REBUILD INDEX` 的麻煩。但索引數量多時,維護成本會增加 SHRINK 耗時。
SELECT object_id, session_id, oracle_username, locked_mode
FROM v$locked_objects WHERE object_id = (SELECT object_id FROM user_objects WHERE object_name='MY_OBJECTS');
-- LOCKED_MODE = 3 (Row-X / SX)
SX 鎖允許其他 Session 對表進行 `INSERT/UPDATE/DELETE`,僅阻擋 DDL。
-- 相同資料量/碎片度測試
-- SHRINK 產生 Redo: ~4.5 MB
-- MOVE 產生 Redo: ~95 KB
原因:SHRINK 逐列移動、更新索引、修改 Bitmap,每步都產生 Redo;MOVE 以區塊為單位批次複製,Redo 精簡。大表 SHRINK 時需關注 Redo Log 空間與 Archive Log 傳輸壓力。
因在原 Segment 內部重組,不像 MOVE 需分配新 Extent。適合磁碟空間緊張環境。