说明:本文面向有基础 Oracle 使用经验的 DBA,围绕堆表(Heap Table)的段空间回收展开,涉及
shrink space与move table两种手段的原理、命令、步骤与风险。文中模拟实验的库表、数据量与输出均为演示示例,请以实际环境为准。
一、shrink space 仅适用于堆表且自动段空间管理
1. 原理
shrink space 的核心是段收缩(Segment Shrink) :从 segment 底部把行移动到 segment 顶部,移动过程等价于一次 DELETE + INSERT 的组合操作,因此会产生大量日志。它真正做到了对段的压缩,包括初始分配,并回收高水位线(HWM)以下的空间。
其能力价值在于:
- 提高缓存(Buffer Cache)利用率;
- 提高 OLTP 性能,减少磁盘 I/O;
- 提高访问速度,节省磁盘空间。
适用前提(缺一不可):
- 仅适用于堆表(Heap Table);
- 表所在的表空间必须是自动段空间管理(ASSM,
SEGMENT SPACE MANAGEMENT AUTO); - 数据库
compatible参数需满足段收缩的版本要求(Oracle 10g 及以上,compatible >= 10.0); - 表必须开启行迁移 (
ENABLE ROW MOVEMENT)------因为收缩过程本质是移动行。
2. 命令与步骤
sql
-- 前提:开启表行迁移
ALTER TABLE xx ENABLE ROW MOVEMENT;
-- 关闭表行迁移(收缩完成后可按需关闭)
ALTER TABLE xx DISABLE ROW MOVEMENT;
-- 仅收缩表,整理碎片并回收空间(调整 HWM)
ALTER TABLE xx SHRINK SPACE;
-- 收缩表及其级联对象(如索引),整理碎片并回收空间
ALTER TABLE xx SHRINK SPACE CASCADE;
-- 单独压缩索引
ALTER INDEX xxx SHRINK SPACE;
-- 仅缩小表和索引,只整理碎片,不回收空间(不调整 HWM)
ALTER TABLE xx SHRINK SPACE COMPACT;
3. 三种模式对比
| 命令 | HWM 调整 | 空间释放 | 索引维护 | 锁级别 |
|---|---|---|---|---|
shrink space |
是 | 立即生效 | 需手动 | X 锁(表锁,短时间) |
shrink space cascade |
是 | 立即生效 | 自动维护 | X 锁(表锁 + 索引锁,短) |
shrink space compact |
否 | 需后续操作 | 需手动 | RX(row exclusive,行锁) |
4. 在线收缩的实用策略
可以利用锁级别的差异做"在线压缩":
- 业务繁忙时 :执行
SHRINK SPACE COMPACT,只整理碎片、不调整 HWM,锁级别低(RX 行锁),对业务影响小; - 业务空闲时 :再调用
SHRINK SPACE完成 HWM 调整与空间回收。
5. 风险与注意事项
- 产生大量日志:移动行等价于 DELETE/INSERT,重做(redo)与撤销(undo)量都很大,需评估归档空间与日志文件增长;
- 锁表风险 :
SHRINK SPACE需要短时 X 表锁,大表仍可能造成明显阻塞; - 必须开启 ROW MOVEMENT:否则会报错;收缩完成后如无必要可关闭;
- LOB 段收缩风险 :对大 CLOB/BLOB 表执行
ALTER TABLE ... MODIFY LOB (...) (SHRINK SPACE)可能因 LOB 段庞大而长时间不返回,极端情况下操作持续数小时仍未完成,最终只能 kill 会话。此类场景建议改用expdp / drop / impdp完整重建表; - 统计信息需刷新 :
shrink space后,若未重新收集统计信息,user_tables.BLOCKS反映的 HWM 可能未下降,需ANALYZE或DBMS_STATS刷新后才准确。
二、move table
1. 原理
ALTER TABLE ... MOVE 通过重建段来整理碎片:把表的数据按新的存储位置重新组织(可迁移到新的表空间),从而降低 HWM、消除碎片。
关键机制:move 会改变记录的物理位置(ROWID) ,而索引条目仍然指向旧的 ROWID,导致索引与数据不一致,索引随之变为 UNUSABLE(失效),必须重建。
2. 命令与步骤
sql
-- 基本语法(可指定新表空间)
ALTER TABLE xx MOVE TABLESPACE new_tablespace;
-- 大表:开启并行与 nologging(减少 redo,不生成 undo)
ALTER TABLE test MOVE NOLOGGING PARALLEL 2;
-- 整理完成后重建索引
ALTER INDEX xx REBUILD NOLOGGING PARALLEL 2;
-- 恢复表和索引的并行度、logging
ALTER TABLE test LOGGING PARALLEL 1;
推荐的标准操作顺序:
- 先逻辑备份待整理的表;
- 对大表,建议开启并行和
nologging(减少 redo,不生成 undo); - move 完成后重建索引;
- 恢复表和索引的并行度、
logging属性。
3. 风险与注意事项
- 会锁表:小表可以在线做,大表会长时间锁表,期间只能查询,影响业务运行;
- 索引失效:move 后索引失效,必须重建,否则相关查询退化为全表扫描,性能显著下降;
- 空间需求:move 需要约两倍空间(新建段 + 原段并存),而 shrink 不需额外空间;
nologging的代价 :nologging减少 redo 提升速度,但该期间数据无法通过归档恢复,操作后应及时补备份,并恢复为logging;- 依赖对象 :move 可能影响物化视图日志表(
MLOG$_段)等对象,迁移前需确认日志已刷新为空,避免数据异常。
4. shrink 与 move 的取舍
| 维度 | shrink space | move table |
|---|---|---|
| 适用对象 | 堆表 + ASSM | 堆表(更通用) |
| 额外空间 | 不需要 | 约需两倍空间 |
| HWM 回收 | 可回收 | 可回收 |
| 索引 | cascade 可自动维护,否则手动 | 必须重建 |
| 锁影响 | compact 为 RX 行锁 | 锁表,大表影响大 |
| 典型用途 | 在线碎片整理、回收空间 | 迁移表空间、重建段 |
三、模拟实验
1. move 方式收缩表:rowid 变化,index 要重建
1)创建表,并插入数据
sql
-- 查询表空间管理方式
SELECT tablespace_name, extent_management, allocation_type, segment_space_management
FROM dba_tablespaces;
-- 创建表空间:ASSM 自动段空间管理(默认段空间管理为 manual)
CREATE TABLESPACE tb_test
DATAFILE '/u01/app/oracle/oradata/prod/tb_test.dbf' SIZE 100m
AUTOEXTEND ON NEXT 50m MAXSIZE UNLIMITED
EXTENT MANAGEMENT LOCAL
SEGMENT SPACE MANAGEMENT AUTO;
-- 创建表并插入数据
CREATE TABLE xq(a INT) TABLESPACE tb_test;
CREATE INDEX index_xq ON xq(a);
BEGIN
FOR i IN 1..100000 LOOP
INSERT INTO xq VALUES(i);
END LOOP;
COMMIT;
END;
/
2)收集统计信息
此时显示用了 244 个 blocks、12 个空闲 blocks,这时的 244 BLOCKS 即是高水位线。
sql
ANALYZE TABLE xq COMPUTE STATISTICS;
SELECT SEGMENT_NAME, EXTENTS, BLOCKS, INITIAL_EXTENT, INITIAL_EXTENT/1024/1024 init
FROM user_segments WHERE SEGMENT_NAME = 'XQ';
SELECT TABLE_NAME, BLOCKS, EMPTY_BLOCKS FROM user_tables WHERE table_name = 'XQ';
3)删除表,高水位不变
sql
DELETE FROM xq;
ANALYZE TABLE xq COMPUTE STATISTICS;
SELECT TABLE_NAME, BLOCKS, EMPTY_BLOCKS FROM user_tables WHERE table_name = 'XQ';
现象:删除数据不会降低 HWM,
BLOCKS仍维持在高位。
4)move 整理碎片,高水位降低
sql
ALTER TABLE xq MOVE [TABLESPACE xx];
ANALYZE TABLE xq COMPUTE STATISTICS;
SELECT TABLE_NAME, BLOCKS, EMPTY_BLOCKS FROM user_tables WHERE table_name = 'TEST';
现象:move 重建段后 HWM 下降,碎片得到整理。
5)查看表中索引情况
此时索引为失效状态------index 通过 rowid 获取数据行,move 改变了 rowid,索引必须重建。
sql
SELECT index_name, status FROM user_indexes WHERE index_name = 'INDEX_XQ';
-- 在线重建索引
ALTER INDEX INDEX_XQ REBUILD ONLINE;
2. shrink space 方式收缩表:真正做到了对段的压缩,包括初始分配,高水位线回收
1)查询表空间段管理方式
sql
SELECT tablespace_name, block_size, extent_management, allocation_type, segment_space_management
FROM dba_tablespaces;
确认目标表空间的
segment_space_management为AUTO(ASSM),这是 shrink 的前提。
2)创建表并插入数据
sql
-- 创建表
CREATE TABLE xq1(a INT) TABLESPACE tb_test;
CREATE INDEX index_xq1 ON xq1(a);
BEGIN
FOR i IN 1..1000000 LOOP
INSERT INTO xq1 VALUES(i);
END LOOP;
COMMIT;
END;
/
3)收集统计信息
sql
ANALYZE TABLE xq1 COMPUTE STATISTICS;
SELECT SEGMENT_NAME, EXTENTS, BLOCKS, INITIAL_EXTENT, INITIAL_EXTENT/1024/1024 init
FROM user_segments WHERE SEGMENT_NAME = 'XQ1';
SELECT TABLE_NAME, BLOCKS, EMPTY_BLOCKS FROM user_tables WHERE table_name = 'XQ1';
4)删除表,高水位不变
sql
DELETE FROM xq1;
ANALYZE TABLE xq1 COMPUTE STATISTICS;
SELECT TABLE_NAME, BLOCKS, EMPTY_BLOCKS FROM user_tables WHERE table_name = 'XQ1';
5)shrink 整理碎片,高水位降低
sql
ALTER TABLE xq1 ENABLE ROW MOVEMENT;
ALTER TABLE xq1 SHRINK SPACE;
ALTER TABLE xq1 DISABLE ROW MOVEMENT;
-- 未刷新统计信息前,高水位未下降
SELECT TABLE_NAME, BLOCKS, EMPTY_BLOCKS FROM user_tables WHERE table_name = 'XQ1';
-- 刷新统计信息后,高水位下降
ANALYZE TABLE xq1 COMPUTE STATISTICS;
SELECT TABLE_NAME, BLOCKS, EMPTY_BLOCKS FROM user_tables WHERE table_name = 'XQ1';
现象:
SHRINK SPACE完成段压缩与 HWM 回收;统计信息刷新前BLOCKS可能仍显示旧值,刷新后可见 HWM 下降。
小结
- 两种方法都能收缩段、消除表空间碎片:shrink 不需额外空间,move 需要两倍空间;
- shrink 适用于堆表 + ASSM 表空间 ,移动行本质是 DELETE/INSERT,日志量大,可借助
compact模式在业务高峰低锁整理、闲时再回收 HWM; - move 会改变 rowid 导致索引失效,必须重建索引,且会锁表,大表需谨慎;
- 无论哪种方式,操作前做好备份、操作后刷新统计信息并验证索引状态,是保障安全与效果的必备动作。
参考资料(示例): Oracle 段收缩特性说明(Note 242090.1);RAC 环境自动段空间管理(Note