lob locator 本身不导致空间增大,但它是空间无法释放的关键中介;其生命周期与lob段管理深度耦合,只要被未提交事务、长会话或未关闭游标持有,旧版本lob数据块就无法复用。

LOB locator 本身不“导致”空间增大,但它是空间无法释放的关键中介
LOB locator 是一个指向实际 LOB 数据的指针(类似文件句柄),它本身只占几十字节,存在主表行内。问题出在它的生命周期和 Oracle 对 LOB 段的管理机制耦合太深:只要 locator 还被某个未提交事务、长会话或未关闭的游标持有,对应 LOB 段里的旧版本数据块就无法被标记为可复用。
典型场景包括:
- Java 应用用
ResultSet.getClob()获取 CLOB 后,没调用clob.free(),且连接池长期复用该连接 → locator 持有状态持续,LOB 段碎片堆积 - PL/SQL 中用
DBMS_LOB.CREATETEMPORARY分配临时 LOB,但忘记调用DBMS_LOB.FREETEMPORARY→ 临时段卡在 TEMP 表空间,v$sort_usage.contents = 'TEMPORARY LOB_DATA' - 批量更新含 LOB 列的表时,事务过大或未分批提交 → undo_retention 期间,所有被修改前的 LOB 块都必须保留,物理空间不回收
BasicFile LOB 下 DELETE / TRUNCATE 为何完全无效
BasicFile(即 SECUREFILE=NO)模式下,LOB 数据存储在独立段(SYS_LOBxxxx$$),与主表段解耦。执行 DELETE FROM t 或 TRUNCATE TABLE t 只操作主表堆段,对 LOB 段零影响——段头不重置、高水位线(HWM)不下降、已分配的区(extent)不归还。
结果就是:dba_segments 里看到 SYS_LOB0000136091C00003$$ 仍占 255GB,而 SELECT COUNT(*) FROM t 返回 0。
根本原因在于 Oracle 的延迟回收机制:LOB 删除后空间进入“待回收”状态,需等 undo_retention 超时 + 无活跃读一致性需求,才可能被后台进程复用。若 undo_retention 设为 7200 秒(2 小时)且业务持续写入,空间就一直卡死。
ALTER TABLE MOVE 不带 LOB 子句等于白干
很多人误以为 ALTER TABLE t MOVE 能像普通表一样迁移并整理空间,但对 LOB 列完全无效。MOVE 只重写主表堆段,LOB 段仍钉在原位置,甚至可能导致 locator 失效(因行迁移后物理地址变化,但 LOB index 未更新)。
正确写法必须显式声明 LOB 子句:
ALTER TABLE t MOVE TABLESPACE users LOB (clob_col) STORE AS (TABLESPACE users);
注意三点:
- 必须包含
LOB (col_name),不能省略;否则 LOB 段不动 - 目标表空间必须是
SEGMENT SPACE MANAGEMENT AUTO(ASSM),否则报ORA-43853 - MOVE 后需手动重建索引,且原 LOB index(
SYS_ILxxx$$)不会自动迁移,得用ALTER INDEX ... REBUILD
SHRINK SPACE 是唯一在线、安全、有效的收缩方式
对 BasicFile LOB,ALTER TABLE t MODIFY LOB (clob_col) (SHRINK SPACE) 是生产环境首选。它不是移动段,而是直接清理 LOBSEGMENT 内部的未使用块、合并碎片、下移 HWM,并自动维护 LOB index。
但必须满足前提:
- 先执行
ALTER TABLE t ENABLE ROW MOVEMENT - 表空间为 ASSM,手工管理(MSSM)不支持
- 无长事务或活跃查询正在访问该 LOB 列(否则 SHRINK 卡在 COMPACT 阶段)
- 确认
dba_lobs.chunk和pctversion设置合理,避免过度预留
加 CASCADE 可同时收缩关联的 LOB index:ALTER TABLE t MODIFY LOB (clob_col) (SHRINK SPACE CASCADE)。
真正棘手的是:SHRINK 看似没效果时,往往不是命令错,而是条件没满足——比如应用还在用那个 CLOB 列做流式读取,locator 一直活着,空间就锁死不动。











