Skip to Main Content
  • Questions
  • Does LOB (un)compression affect existing rows?

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, saravanan.

Asked: January 10, 2019 - 4:21 pm UTC

Last updated: January 11, 2019 - 2:01 pm UTC

Version: 12.1.0.2

Viewed 1000+ times

You Asked

Hi,

I have a question regarding LOB compression. I have a lob securefile table with clob datatype. I want to compress this table . I read in articles that if I compress the lob with (low/medium/high) options
only the new records will compress and old data will remain uncompressed unless I perform online redifinition. What if I uncompress the LOB again with below command. Will the compressed data remain the same(unless i do redefinition again) and only new data be uncompressed?

Example syntax below
====================


(</alter table testmodify lob(orig_file)(uncompress);/>)

and Chris said...

As per the docs:

COMPRESS compresses all LOBs in the segment and then returns. NOCOMPRESS uncompresses all LOBs in the segment and then returns.


So both happen when you change this property.

First, let's create an uncompress LOB and load it up with highly-compressible data:

create table t1 ( 
  x int,
  c clob
) lob (c) store as securefile (
  enable storage in row
  nocompress
);

insert into t1
  select rownum, rpad('b',12000,'b')
  from   dual
  connect by level <= 10000;

commit;

select segment_name, sum ( bytes ) 
from   user_segments
where  segment_name = 'T1' or
       segment_name = ( 
  select segment_name from user_lobs
  where table_name = 'T1' 
)
group  by rollup ( segment_name );

SEGMENT_NAME                SUM(BYTES)   
SYS_LOB0000172177C00002$$      100859904 
T1                                655360 
<null>                         101515264 

exec show_space;

Segment blocks = 12312 bytes = 100859904
 Used blocks = 10000 bytes = 81920000
 Expired blocks = 2162 bytes = 17711104
 Unexpired blocks = 0 bytes = 0


Now when we compress the LOB, what happens?

alter table t1 modify
  lob ( c ) ( compress );

select segment_name, sum ( bytes ) 
from   user_segments
where  segment_name = 'T1' or
       segment_name = ( 
  select segment_name from user_lobs
  where table_name = 'T1' 
)
group  by rollup ( segment_name );

SEGMENT_NAME                SUM(BYTES)   
SYS_LOB0000172177C00002$$      100859904 
T1                               2097152 
<null>                         102957056 


Eh? The segments are still the same size! No compression!

Or are they... What does the space report show?

exec show_space;

Segment blocks = 12312 bytes = 100859904
 Used blocks = 0 bytes = 0
 Expired blocks = 2094 bytes = 17154048
 Unexpired blocks = 10000 bytes = 81920000


Aha! Used blocks (active LOBs) down from 1,000 to zero. We have compressed the data!

If you move the lob, it creates a new segment. Which is much smaller than before:

alter table t1 move
  lob ( c ) store as securefile;

select segment_name, sum ( bytes ) 
from   user_segments
where  segment_name = 'T1' or
       segment_name = ( 
  select segment_name from user_lobs
  where table_name = 'T1' 
)
group  by rollup ( segment_name );

SEGMENT_NAME                SUM(BYTES)   
SYS_LOB0000172177C00002$$         131072 
T1                               2097152 
<null>                           2228224 

exec show_space;

Segment blocks = 16 bytes = 131072
 Used blocks = 0 bytes = 0
 Expired blocks = 5 bytes = 40960
 Unexpired blocks = 0 bytes = 0


Now if you uncompress the LOB, the data won't fit in the existing segments. So they'll grow:

<codde>alter table t1 modify
lob ( c ) ( nocompress );

select segment_name, sum ( bytes )
from user_segments
where segment_name = 'T1' or
segment_name = (
select segment_name from user_lobs
where table_name = 'T1'
)
group by rollup ( segment_name );

SEGMENT_NAME SUM(BYTES)
SYS_LOB0000172177C00002$$ 84017152
T1 2097152
<null> 86114304

exec show_space;

Segment blocks = 10256 bytes = 84017152
Used blocks = 10000 bytes = 81920000
Expired blocks = 99 bytes = 811008
Unexpired blocks = 0 bytes = 0</code>

The show_space procedure is:

create or replace procedure show_space as
  l_segment_size_blocks number; 
  l_segment_size_bytes number; 
  l_used_blocks number; 
  l_used_bytes number; 
  l_expired_blocks number; 
  l_expired_bytes number; 
  l_unexpired_blocks number; 
  l_unexpired_bytes number; 
  v_segname varchar2(30); 
  
begin 

  select segment_name 
  into   v_segname 
  from   user_lobs 
  where  table_name = 'T1' 
  and    column_name = 'C'; 
 
  dbms_space.space_usage( 
    segment_owner => user, 
    segment_name => v_segname, 
    segment_type => 'LOB', 
    segment_size_blocks => l_segment_size_blocks, 
    segment_size_bytes => l_segment_size_bytes, 
    used_blocks => l_used_blocks, 
    used_bytes => l_used_bytes, 
    expired_blocks => l_expired_blocks, 
    expired_bytes => l_expired_bytes, 
    unexpired_blocks => l_unexpired_blocks, 
    unexpired_bytes => l_unexpired_bytes 
  ); 

  dbms_output.put_line(' Segment blocks = '||l_segment_size_blocks||' bytes = '||l_segment_size_bytes); 
  dbms_output.put_line(' Used blocks = '||l_used_blocks||' bytes = '||l_used_bytes); 
  dbms_output.put_line(' Expired blocks = '||l_expired_blocks||' bytes = '||l_expired_bytes); 
  dbms_output.put_line(' Unexpired blocks = '||l_unexpired_blocks||' bytes = '||l_unexpired_bytes); 

end show_space;
/

Rating

  (2 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

saravanan kalyan, January 10, 2019 - 5:56 pm UTC

Thanks Chris for the nice explanation.

So if the LOB size is larger then it will take time for the alter command to return the output since it will be performing the compression of the LOB. So compressing a Production LOB table is not advised during normal business operations and we do it either during a maintenance window or go with online redefinition option?
Chris Saxon
January 11, 2019 - 1:52 pm UTC

Correct.

saravanan kalyan, January 10, 2019 - 6:14 pm UTC

I did a compress and no compress for a normal table without LOB. In this case just issuing compress command does not seems to compress the data. So compress works different for LOB and a normal table then??



-----TABLE CREATION-----

SQL> create table test5 as select * from dba_objects;

Table created.

-----CHECK SIZE-----

SQL> select sum(bytes)/1024 from dba_segments where segment_name='TEST5';

SUM(BYTES)/1024
---------------
4096


-----COMPRESS TABLE-----

SQL> alter table TEST5 compress;

Table altered.

-----CHECK SIZE-----

SQL> select sum(bytes)/1024 from dba_segments where segment_name='TEST5';

SUM(BYTES)/1024
---------------
4096

-----MOVE AND COMPRESS TABLE-----

SQL> alter table TEST5 move compress;

Table altered.

-----CHECK SIZE-----

SQL> select sum(bytes)/1024 from dba_segments where segment_name='TEST5';

SUM(BYTES)/1024
---------------
1024

-----UNCOMPRESS TABLE-----

SQL> alter table TEST5 nocompress;

Table altered.

-----CHECK SIZE-----

SQL> select sum(bytes)/1024 from dba_segments where segment_name='TEST5';

SUM(BYTES)/1024
---------------
1024

-----MOVE AND NOCOMPRESS-----

SQL> alter table TEST5 move nocompress;

Table altered.

-----CHECK SIZE-----

SQL> select sum(bytes)/1024 from dba_segments where segment_name='TEST5';

SUM(BYTES)/1024
---------------
3072



Chris Saxon
January 11, 2019 - 2:01 pm UTC

Unlike LOB compression, table compression only applies to new rows. Or when you move the table.

Also, in general, the database won't shrink segments unless you tell it to or move the data.

Ensure you're looking at the used space in the blocks. Not the size of the segment.

More to Explore

Administration

Need more information on Administration? Check out the Administrators guide for the Oracle Database