lob段碎片不能用alter table move清理,因其只移动主表数据而不处理独立的lob段,需用alter table...modify lob(...)(shrink space)在线收缩并降低hwm。
lob段碎片为什么不能直接用 alter table move
因为 alter table move 只移动主表数据,完全不碰 lob 段。你执行完 alter table t move,sys_lobxxxx$$ 这类 lobsegment 依然原地不动,占用几十 gb 空间、布满碎片——甚至可能比主表还大。更糟的是,move 后主表的 lob 列指向的还是旧 lob 段物理地址,但行迁移可能导致 lob locator 失效或读取异常。
- LOB 数据默认存储在独立段(
SEGMENT_TYPE = 'LOBSEGMENT'),和主表段分离管理 -
MOVE不重建 LOB 段,也不更新 LOB index(LOBINDEX)结构,只重写堆表部分 - 若表含多个 LOB 列,每个都会生成独立的
SYS_LOBxxx$$段,需逐个处理
真正回收 LOB 空间的唯一可靠方式:ALTER TABLE ... MODIFY LOB(...) (SHRINK SPACE)
Oracle 10g+ 支持对 LOB 列在线收缩,本质是清理 LOBSEGMENT 内部的未使用块、合并碎片、下移高水位线(HWM)。它不要求锁表(仅短暂 X 锁),且自动维护 LOB index,是生产环境首选。
- 必须先启用行移动:
ALTER TABLE t ENABLE ROW MOVEMENT - 语法为:
ALTER TABLE t MODIFY LOB (clob_col) (SHRINK SPACE)(注意括号嵌套层级) - 加
CASCADE可同时收缩关联的 LOB index:ALTER TABLE t MODIFY LOB (clob_col) (SHRINK SPACE CASCADE) - 不支持 SECUREFILE LOB 的
SHRINK;若用的是SECUREFILE,得换用DBMS_LOB.RETAIN或TRUNCATE配合归档策略
为什么 SHRINK SPACE 有时没效果?检查这三点
执行后空间没释放,大概率不是命令错了,而是条件没满足或数据状态不配合。
- LOB 列必须是
ENABLE STORAGE IN ROW或已禁用该选项(默认禁用);若启用了且数据小到存于行内,则SHRINK不作用于外部段 - 表空间必须是
AUTO SEGMENT SPACE MANAGEMENT(ASSM),手工管理(MSSM)表空间不支持 LOB SHRINK - 存在未提交的事务或长时间运行的查询正在访问该 LOB 列,会导致 SHRINK 卡在 COMPACT 阶段,看似“没反应”
- 可通过
SELECT * FROM V$LOBSTAT查当前 LOB 操作状态,或查DBA_LOBS确认CHUNK大小与PCTVERSION设置是否合理
紧急场景下替代方案:TRUNCATE 或重建 LOB 列
当 SHRINK 失败、卡住、或业务允许短时中断时,可考虑激进但确定的方法。注意:这不是“整理”,而是“重置”。
- 若整张表可清空:
TRUNCATE TABLE t REUSE STORAGE—— 保留段结构但清空所有数据,LOBSEGMENT 也被重置,空间立即释放 - 若只想清空某 LOB 列:
UPDATE t SET clob_col = EMPTY_CLOB()+COMMIT,再跟SHRINK,避免残留临时段 - 终极手段:新建表 +
INSERT /*+ APPEND */导入非空 LOB 数据,然后DROP原表。需重建所有索引、约束、权限,且期间停写 - 切记:任何重建操作后,必须检查应用层是否缓存了 LOB locator,否则后续读取会报
ORA-22285或空值
LOB 段的碎片不像普通表那样能靠一次 MOVE 解决,它的空间生命周期由单独的段管理器控制。哪怕你把主表 shrink 得干干净净,那个 SYS_LOBxxxx$$ 仍可能躺在那里不动如山——盯住 DBA_SEGMENTS 里 SEGMENT_NAME 包含 LOB 且 SEGMENT_TYPE = 'LOBSEGMENT' 的记录,才是真实战场。










