oracle大lob清理

结论:大lob只能通过truncate或开启行移动后delete的方式清理

1.先明确:LOB 字段的存储本质

Oracle 中 LOB,分两种存储方式 核心是LOB 段与表段分离

内联 LOB(INLINE):小 LOB(默认 < 4000 字节)直接存在表的行数据块中,与普通字段无本质区别;

外联 LOB(OUT OF LINE):大 LOB(超过阈值)会在表段外单独创建 LOB 段(对应 SEGMENT_TYPE 为 LOBSEGMENT)、LOB 索引段(LOBINDEX),行数据块中仅存 LOB 的 “指针”(定位到 LOB 段的物理地址)。

我的理解:大lob会存储在一个单独的段里,表中的大lob列实际存储的是指针,指向这个地址

2.模拟操作

CREATE TABLE lob_test (
  id NUMBER,
  big_clob CLOB
)
LOB (big_clob) STORE AS (
  TABLESPACE TEST_DATA
  ENABLE STORAGE IN ROW
);
-- 查看LOB段名
SELECT table_name, column_name, segment_name 
FROM dba_lobs 
WHERE table_name = 'LOB_TEST' AND owner = 'TEST';
LOB_TEST
BIG_CLOB
SYS_LOB0000100703C00002$$
插入大 LOB 数据(模拟 “大 LOB 占用空间”)
    -- 生成5000字节的随机文本(超过内联阈值)
DECLARE
  v_clob CLOB;
BEGIN
  FOR i IN 1..100 LOOP
    v_clob := RPAD(TO_CHAR(DBMS_RANDOM.string('x', 100)), 5000, 'a');
    INSERT INTO lob_test VALUES (i, v_clob);
  END LOOP;
  COMMIT;
END;
/
多执行几次或将 100调为更大

当前占用表空间大小
TEST_DATA LOCAL 1.00 0.00 1.00 0.30

查询当前 LOB 段 / 表空间的空间占用(基准值)


SELECT 
  segment_name,
  tablespace_name,
  bytes/1024/1024 AS used_mb -- 已用MB
FROM dba_segments 
WHERE segment_name = 'SYS_LOB0000100703C00002$$' AND owner = 'TEST';

SEGMENT_NAME TABLESPACE_NAME USED_MB


SYS_LOB0000100703C00002$$ TEST_DATA 18

测试 DELETE 操作
DELETE FROM lob_test;
COMMIT;

再次查询无变化

再次插入数据并查询

又插入了1400行数据

select count(*) from lob_test;
SELECT 
  segment_name,
  tablespace_name,
  bytes/1024/1024 AS used_mb -- 已用MB
FROM dba_segments 
WHERE segment_name = 'SYS_LOB0000100703C00002$$' AND owner = 'TEST';

SEGMENT_NAME									  TABLESPACE_NAME	  USED_MB
--------------------------------------------------------------------------------- -------------------- ----------
SYS_LOB0000100703C00002$$							  TEST_DATA		       23

使用truncate方式删除

TRUNCATE TABLE lob_test;

删除后再次查询

通过结果可以看到已经成功的释放了空间

SQL> select count(*) from lob_test;

  COUNT(*)
----------
	 0
SELECT 
  segment_name,
  tablespace_name,
  bytes/1024/1024 AS used_mb -- 已用MB
FROM dba_segments 
WHERE segment_name = 'SYS_LOB0000100703C00002$$' AND owner = 'TEST';

SEGMENT_NAME									  TABLESPACE_NAME	  USED_MB
--------------------------------------------------------------------------------- -------------------- ----------
SYS_LOB0000100703C00002$$							  TEST_DATA		    .0625

3.为什么truncate可以删除而delete不行

要区分两个命令在lob空间管理层面的本质

DELETE : 仅在 LOB 段内给旧数据块贴 “空闲标签”(逻辑标记),空间仍属于该 LOB 段(仅可复用),不归还表空间 ,(仅作逻辑删除,不做物理删除)

TRUNCATE : 直接重置 LOB 段的元数据(HWM 归 0),将 LOB 段的扩展区(extent)全部归还表空间,

4.为什么要这样delete要做出 复用 的设计

Oracle 设计 “段内空间复用” 的核心目的是提升性能

  • 如果每次 DELETE 都擦除数据块、释放扩展区,再每次 INSERT 都重新申请扩展区,会产生大量磁盘 I/O 和数据字典操作(性能极低)
  • 复用已有的空闲块,只需直接写入新数据(覆盖旧内容),无需申请新空间

5.补充

还有一种清理方式:开启行移动+delete+ SHRINK SPACE

行移动的唯一作用是允许 SHRINK SPACE 调整数据物理位置,从而降低 HWM、释放空间到表空间

再来做一次操作验证

  1. 按之前的方法 插入1000条数据
---查询是否插入成功
select count(*) from lob_test;
  COUNT(*)
----------
      1000
---查询占用空间
SELECT ROUND(bytes/1024/1024,2) AS used_mb 
FROM dba_segments 
WHERE segment_name = 'SYS_LOB0000100703C00002$$' AND owner = 'TEST';

   USED_MB
----------
	16
  1. 打开行移动
ALTER TABLE lob_test ENABLE ROW MOVEMENT;
  1. 使用delete清理,清理后再次查询表空间
DELETE FROM lob_test;
COMMIT;
SELECT ROUND(bytes/1024/1024,2) AS used_mb 
FROM dba_segments 
WHERE segment_name = 'SYS_LOB0000100703C00002$$' AND owner = 'TEST';
   USED_MB
----------
	16
  1. 收缩表空间
--先收缩表本身的空间
ALTER TABLE lob_test SHRINK SPACE CASCADE;

--单独收缩 LOB 段的空间
--收缩 LOB 段必须通过MODIFY LOB (列名) (SHRINK SPACE) 这种独立语法

ALTER TABLE lob_test MODIFY LOB (big_clob) (SHRINK SPACE);
  1. 再次查询表空间

发现空间已经成功清理,成功清理后记得关闭行移动

 SELECT 
  segment_name,
  tablespace_name,
  bytes/1024/1024 AS used_mb -- 已用MB
FROM dba_segments 
WHERE segment_name = 'SYS_LOB0000100703C00002$$' AND owner = 'TEST'; 

SEGMENT_NAME									  TABLESPACE_NAME	  USED_MB
--------------------------------------------------------------------------------- -------------------- ----------
SYS_LOB0000100703C00002$$							  TEST_DATA		    .0625

Posted in:

Leave a Reply

Your email address will not be published. Required fields are marked *