Oracle 表空间碎片整理实战:shrink space 与 move table 全解析

说明:本文面向有基础 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;
  • 提高访问速度,节省磁盘空间。

适用前提(缺一不可):

  1. 仅适用于堆表(Heap Table);
  2. 表所在的表空间必须是自动段空间管理(ASSM,SEGMENT SPACE MANAGEMENT AUTO);
  3. 数据库 compatible 参数需满足段收缩的版本要求(Oracle 10g 及以上,compatible >= 10.0);
  4. 表必须开启行迁移 (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;

推荐的标准操作顺序:

  1. 先逻辑备份待整理的表;
  2. 对大表,建议开启并行和 nologging(减少 redo,不生成 undo);
  3. move 完成后重建索引;
  4. 恢复表和索引的并行度、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

相关推荐
Yyyyyy~1 小时前
【Mysql】表的操作
数据库·mysql
TDengine (老段)1 小时前
工业数据十大痛点系列 · 第 6 篇
数据库·时序数据库·tdengine·涛思数据·idmp·工业数据分析·ai 分析数据
ly76892 小时前
PostgreSQL 流复制与高可用切换:主从延迟、同步提交与 Spring Boot 读写分离的边界
数据库·spring boot·postgresql·读写分离·流复制·高可用切换
l1t2 小时前
修改两处,让SQLite求解数独SQL在datafusion上运行
服务器·数据库·sql
data analyse 4562 小时前
服务端统计时区切换的日期归并
大数据·前端·数据库·人工智能·数据分析
远方16093 小时前
108-ORA-00600 [kdsgrp1-qetlbr] 内部错误排查与 RMAN 块级恢复
大数据·linux·数据库·oracle
福娃筱欢3 小时前
SQLServer迁移至KES SQLServer模式 问题整理
数据库·oracle·sqlserver
hflmwl83 小时前
RedisDesktopManager安装包中文版官方下载,redis可视化客户端工具下载
数据库·redis·缓存·可视化客户端工具
FluxDB4 小时前
《用 Rust + GPUI 做了一个开源数据库客户端:FluxDB》
数据库