Skip to Main Content
  • Questions
  • Why we should segment advisor to get fragmented space for tablespaces where segment space management is AUTO

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Venkatesh.

Asked: January 10, 2018 - 8:28 am UTC

Last updated: January 16, 2018 - 2:21 pm UTC

Version: 12.1.0.2

Viewed 1000+ times

You Asked

Hi AskTom Team,

Normally, to get fragmentation we do (num_rows*avg_row_length) from dba_tables and compare that with
bytes in dba_segments to see if there is any fragmentation.

But for tablespaces with segment space management "AUTO" oracle recommends to go with segment advisor.
Any reason for using segment advisor to identify fragmentation.

Thanks,
Venkatesh.

and Chris said...

Because taking num_rows*avg_row_length from *_tables, then comparing this to bytes in *_segments is a crude measure that doesn't tell you:

- How much space (if any) you're likely to save in a re-org
- How to do the re-org
- If you've got chained rows
- etc.

And the information in *_tables only updates when you gather stats. So the figures there may be misleading if it's been a while since you did this.

Whereas the segment advisor contains a whole load of possible findings and what do about them:

https://docs.oracle.com/database/121/ADMIN/schema.htm#GUID-047BCDDF-0015-43D6-AF09-294DA42B13CA__BABGGBHI

For example, I create a table, then delete half the rows from it this frees up a bunch of space:

create table t as 
  select level x, lpad('x', 1000, 'x') y from dual
  connect by level <= 100000;
  
delete t
where  mod(x, 2) = 0;
commit;
exec dbms_stats.gather_table_stats(user, 't');

select avg_row_len * num_rows
from   user_tables
where  table_name = 'T';

AVG_ROW_LEN*NUM_ROWS   
              50300000 

select bytes from user_segments
where  segment_name = 'T';

BYTES       
  125829120


But how do I go about reclaiming it? How much am I likely to recover?

declare
  id number;
begin
  declare
  name varchar2(100);
  descr varchar2(500);
  obj_id number;
  begin
  name:='T1';
  descr:='Segment Advisor Example';

  dbms_advisor.create_task (
    advisor_name     => 'Segment Advisor',
    task_id          => id,
    task_name        => name,
    task_desc        => descr);

  dbms_advisor.create_object (
    task_name        => name,
    object_type      => 'TABLE',
    attr1            => 'CHRIS',
    attr2            => 'T',
    attr3            => NULL,
    attr4            => NULL,
    attr5            => NULL,
    object_id        => obj_id);

  dbms_advisor.set_task_parameter(
    task_name        => name,
    parameter        => 'recommend_all',
    value            => 'TRUE');

  dbms_advisor.execute_task(name);
  end;
end; 
/

select tablespace_name,
      segment_name,
      segment_type,
      partition_name,
      recommendations,
      c1
from table ( dbms_space.asa_recommendations( ) );

TABLESPACE_NAME                SEGMENT_NAME                   SEGMENT_TYPE
------------------------------ ------------------------------ ------------------
PARTITION_NAME
------------------------------
RECOMMENDATIONS
----------------------------------------------------------------------------------------------------
C1                                                                                                  
----------------------------------------------------------------------------------------------------
ASSM_TBLSP                     T                              TABLE              
<null>                         
Enable row movement of the table CHRIS.T and perform shrink, estimated savings is 54376518 bytes.   
alter table "CHRIS"."T" shrink space                                         


That's much more useful if you ask me :)

Rating

  (3 ratings)

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

Comments

Venkatesh Guttapally, January 12, 2018 - 9:51 am UTC

Thanks Chris.

If stats are up-to-date can I use avg_row_len*num_rows.

Also in the example you demonstrated
bytes-(avg_row_len*num_rows) = 125829120-50300000 = 75529120 bytes which means 75529120 is space that can be released if say do reorg (using ALTER table move) or shrink command.

But segment advisor reports estimated space savings to be 54376518 bytes.

why is this difference?


Chris Saxon
January 12, 2018 - 11:08 am UTC

Remember it's an estimate. It's not going to be 100% accurate.

Also, although bytes-(avg_row_len*num_rows) shows 75,529,120 "wasted" space, in practice you're not going to save that much. Continuing the example above:

alter table t enable row movement;
alter table t shrink space;

select bytes from user_segments
where  segment_name = 'T';

BYTES      
  59572224 


So the saving was only 66,256,896.

Averages also hide a lot of detail.

Let's rebuild the table, with all the string columns set to null. Then update every other one to be 2,000 characters long:

drop table t purge;
create table t as 
  select level x, cast(null as varchar2(2000)) y from dual
  connect by level <= 100000;
  
update t
set    y = lpad('x', 2000, 'x')
where  mod(x, 2) = 0;
commit;
exec dbms_stats.gather_table_stats(user, 't');

select avg_row_len * num_rows
from   user_tables
where  table_name = 'T';

AVG_ROW_LEN*NUM_ROWS   
             100600000 

select bytes from user_segments
where  segment_name = 'T';

BYTES       
  142606336 


So we could save (142,606,336 - 100,600,000) = 42,006,336 bytes, right?

Let's try:

alter table t enable row movement;
alter table t shrink space;

select bytes from user_segments
where  segment_name = 'T';

BYTES       
  142606336 


WTF?! No change?!

Why's that?

Let's run the segment advisor instead:

declare
  id number;
begin
  declare
  name varchar2(100);
  descr varchar2(500);
  obj_id number;
  begin
  name:='T2';
  descr:='Segment Advisor Example';

  dbms_advisor.create_task (
    advisor_name     => 'Segment Advisor',
    task_id          => id,
    task_name        => name,
    task_desc        => descr);

  dbms_advisor.create_object (
    task_name        => name,
    object_type      => 'TABLE',
    attr1            => 'CHRIS',
    attr2            => 'T',
    attr3            => NULL,
    attr4            => NULL,
    attr5            => NULL,
    object_id        => obj_id);

  dbms_advisor.set_task_parameter(
    task_name        => name,
    parameter        => 'recommend_all',
    value            => 'TRUE');

  dbms_advisor.execute_task(name);
  end;
end; 
/

select af.message , af.more_info
from   dba_advisor_findings af, dba_advisor_objects ao
where  ao.task_id = af.task_id
and    ao.object_id = af.object_id
and    af.task_name = 'T2';

MESSAGE                                                      MORE_INFO                                                                      
The object has chained rows that can be removed by re-org.   49 percent chained rows can be removed by re-org.                              
The free space in the object is less than 10MB.              Allocated Space:142606336: Used Space:137898749: Reclaimable Space :4707587:   


Ahhh. There are chained rows. So we can't reclaim that space with a move/shrink anyway...

If you want to use avg_row_len*num_rows, go ahead. Just don't be surprised if your savings are nowhere near the figures this gives you.

Personally I'd stick with the segment advisor.

And?

Moris, January 12, 2018 - 5:14 pm UTC

49 percent chained rows can be removed by re-org. The free space in the object is less than 10MB. Allocated Space:142606336: Used Spa...


So? How do the reorg?
Chris Saxon
January 15, 2018 - 11:29 am UTC

You have to re-create the table.

Venkatesh Guttapally, January 16, 2018 - 10:26 am UTC

Thanks Chris. Will use segment advisor.
Also I have heard that using "ALTER TABLE MOVE" may result in performance issue. Why is it so. Does "SHRINK" also result in performance issues?

More to Explore

Administration

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