暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Oracle 如何在将lob归零后回收空间

askTom 2017-07-26
1981

问题描述

在这个包含blob的表上,直到今天已经填满了32GB的表空间,我通过使用UPDATE语句将它们设置为NULL来删除了很大比例的blob (当然,其余列保持不变)。实际上没有释放任何空间,因此我不得不添加第二个数据文件4gb,以允许插入查询工作。我做了一个 “改变表X移动”,没有任何好处。我做了一个 “alter table X收缩空间”,它实际上增加了第二个数据文件的占用率,并使第一个不受影响。

我在网上找到了一个实用程序 (https://oracle-base.com/dba/script.php?category=monitoring&file=ts_free_space.sql),它计算出可以节省多少空间,它使我的用户ts只有4004mb可以释放,而我希望第二个数据文件中至少有10gb (4gb) 基本上应该是空的,加上最初填充到容量 = 32gb的第一个数据文件的至少6gb)。

释放空间似乎是不可能的。设置为空是个坏主意吗?为什么Oracle拒绝将任何空间视为可回收空间?

很明显,我不能有一个持续增长的ts ....在这个特定的数据库上,如果您认为有帮助,我可以继续删除我更新的那些行,但是在其他安装上,我只需要将BLOB设置为null,然后keep行。

更具体地说,这个查询:

 select file_name, hwm, blocks total_blocks, blocks-hwm+1 shrinkage_possible
  from dba_data_files a,
         ( select file_id, max(block_id+blocks) hwm
             from dba_extents
            group by file_id ) b
    where a.file_id = b.file_id;


。.. 给出此结果 (用户 *.dbf是相关文件,01是已填充的旧文件,02是我今天创建的新文件)

FILE_NAME                                                                 HWM TOTAL_BLOCKS SHRINKAGE_POSSIBLE
------------------------------------------------------------------ ---------- ------------ ------------------
/home/oracle/app/oracle/oradata/orcl/users01.dbf                      4194176      4194240                 65
/home/oracle/app/oracle/oradata/orcl/users02.dbf                       183816       524288             340473
/home/oracle/app/oracle/oradata/orcl/system01.dbf                       89984        90880                897
/home/oracle/app/oracle/oradata/orcl/sysaux01.dbf                       73640        78080               4441
/home/oracle/app/oracle/oradata/orcl/example01.dbf                      10392        12800               2409
/home/oracle/app/oracle/oradata/orcl/undotbs01.dbf                      53376        64640              11265




-乔治

专家解答

这里要注意的关键是您的lob (clob或blob) 是内联还是离线。

如果它们是内联的,它们将进入表格段。否则它们会进入LOB部分。

要内联:

列值必须为 “小” (小于4,000字节)
lob设置为 “在行中启用存储” (默认)

http://docs.oracle.com/database/122/ADLOB/LOB-storage-with-applications.htm#ADLOB45273

如果所有行的lob都是内联的,则在将它们设为null之后,您可以通过移动或缩小表来回收它们使用的空间。

但是一旦lob离线,它就会离线。因此,要收回空间,您需要移动/缩小lob,而不是表!

在下面的演示中,我从一些小的内联lob开始。将这些设置为null后,缩小表收回空间:

create tablespace lobtest
logging datafile 'lobtest.dbf' size 10m
extent management local
segment space management auto;

create table t ( x clob ) lob (x) store as (enable storage in row)
  enable row movement tablespace lobtest;

insert into t 
  select lpad('x', 1000, 'x') from dual
  connect by level <= 1000;
  
commit;

select segment_name from user_lobs
where  table_name = 'T';

SEGMENT_NAME               
SYS_LOB0000116998C00001$$ 

select segment_name, bytes 
from   user_segments
where  segment_name in ('T', 'SYS_LOB0000116998C00001$$');

SEGMENT_NAME               BYTES    
SYS_LOB0000116998C00001$$  65536    
T                          2097152  

update t 
set    x = null;

commit;

alter table t shrink space;

select segment_name, bytes 
from   user_segments
where  segment_name in ('T', 'SYS_LOB0000116998C00001$$');

SEGMENT_NAME               BYTES  
SYS_LOB0000116998C00001$$  65536  
T                          65536 


但是,如果我重击一个单一的大clob,它就会进入lob部分。所以要把这个空间清理出来后,我需要缩小lob:

truncate table t;

select segment_name, bytes 
from   user_segments
where  segment_name in ('T', 'SYS_LOB0000116998C00001$$');

SEGMENT_NAME               BYTES  
SYS_LOB0000116998C00001$$  65536  
T                          65536 

declare
  l_clob   clob;
  l_size   number := 10000;
begin
  insert into t values ( empty_clob() ) returning x into l_clob;

  for i in 1..10 loop
    dbms_lob.writeappend(
      l_clob,
      l_size,
      lpad('x', l_size, 'x')
    );

    l_size := 31191;
  end loop;

  commit;
end;
/


select segment_name, bytes 
from   user_segments
where  segment_name in ('T', 'SYS_LOB0000116998C00001$$');

SEGMENT_NAME               BYTES   
SYS_LOB0000116998C00001$$  458752  
T                          65536   

update t
set    x = null;

commit;

alter table t shrink space;

select segment_name, bytes 
from   user_segments
where  segment_name in ('T', 'SYS_LOB0000116998C00001$$');

SEGMENT_NAME               BYTES   
SYS_LOB0000116998C00001$$  458752  
T                          65536 

alter table t modify lob (x) (shrink space);

select segment_name, bytes 
from   user_segments
where  segment_name in ('T', 'SYS_LOB0000116998C00001$$');

SEGMENT_NAME               BYTES  
SYS_LOB0000116998C00001$$  65536  
T                          65536  


I can't have a ts that keeps on growing, obviously....

真的吗?数据库不应该随着时间的推移而增长吗?;)
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论