mardi 21 février 2017

Segment Fragmentation


1] Table Fragmentation

Look for candidate ( Gain > 500 MB )

-- table candidate to shrink
select table_name, num_rows, ceil(tt) "Used_Size_MB", ceil(tr) "Total_Size_MB", ceil(lg) "Free_Size_MB" ,
case WHEN ceil(lg) > 500 then 'alter table '||OWNER||'.'||TABLE_NAME||' shrink space cascade ;' end SHRINKAGE
from (
SELECT   t.owner OWNER , t.table_name TABLE_NAME, t.num_rows, t.num_rows*t.avg_row_len/1024/1024 as tt,
         t.blocks*8192/1024/1024 as tr, TRUNC ( (t.blocks*8192 - t.num_rows*t.avg_row_len)/1024/1024,2 ) as lg
FROM dba_tables t WHERE t.num_rows > 0 AND t.owner = 'SCH1' AND t.table_name = 'TABLE1'
ORDER BY t.blocks*8192 - t.num_rows*t.avg_row_len DESC
)

TABLE_NAME
NUM_ROWS
Used_Size_MB
Total_Size_MB
Free_Size_MB
SHRINKAGE
TABLE1
37805913
1515
2429
915
alter table SCH1.TABLE1 shrink space cascade ;

SHRINK

SQLPLUS
alter table SCH1.TABLE1 enable row movement ;
alter table SCH1.TABLE1 shrink space cascade ;
alter table SCH1.TABLE1 disable row movement ;

Analyze table and get new Fragmentation values :

exec DBMS_STATS.GATHER_TABLE_STATS ( 'SCH1','TABLE1',null,DBMS_STATS.AUTO_SAMPLE_SIZE,null,'FOR ALL INDEXED COLUMNS SIZE AUTO',2,'ALL',TRUE) ;

TABLE_NAME
NUM_ROWS
Used_Size_MB
Total_Size_MB
Free_Size_MB
TABLE1
37949085
1231
1585
354

!! The SHRINK Commands are very Log Consuming !!

Another one : TRUNCATE when possible !

-- HFD_DAT 22 GB
-- table candidate to shrink
select table_name, num_rows, ceil(tt) "Used_Size_MB", ceil(tr) "Total_Size_MB", ceil(lg) "Free_Size_MB" ,
case WHEN ceil(lg) > 500 then 'alter table '||OWNER||'.'||TABLE_NAME||' shrink space cascade ;' end SHRINKAGE
from (
SELECT   t.owner OWNER , t.table_name TABLE_NAME, t.num_rows, t.num_rows*t.avg_row_len/1024/1024 as tt,
         t.blocks*8192/1024/1024 as tr, TRUNC ( (t.blocks*8192 - t.num_rows*t.avg_row_len)/1024/1024,2 ) as lg
FROM dba_tables t WHERE t.owner = 'HFD' AND t.table_name = 'DEBUG_RAPPEL'
ORDER BY t.blocks*8192 - t.num_rows*t.avg_row_len DESC
)

-- used 13000 ; total 15877

truncate table HFD.DEBUG_RAPPEL

exec DBMS_STATS.GATHER_TABLE_STATS ( 'HFD','DEBUG_RAPPEL',null,DBMS_STATS.AUTO_SAMPLE_SIZE,null,'FOR ALL INDEXED COLUMNS SIZE AUTO',2,'ALL',TRUE) ;

-- segment without any rows
-- used 0 ; total 0


Then reclaim Tablespace free space.

2] Analyze Index Fragmentation

Looks for in the following order :

BTREE_SPACE-USED_SPACE > 50 AND gain > 250 MB => REBUILD ( ASSM  =AUTO ) OR SHRINK
DEL_LF_ROWS*100/LF_ROWS Deleted >= 20% => REBUILD ( ASSM  =AUTO ) OR SHRINK
HEIGHT number of index levels is > 3 => REBUILD ( ASSM  =AUTO ) OR SHRINK
OPT_CMPR_COUNT compressed gain >= 30 %
index candidate to bitmap ?      


set serveroutput on

-- 1/ variables

Declare

  SCH           VARCHAR2 (20)   := '&1' ;
  tab_tag       varchar2(1000);
  action_name   VARCHAR2 (48)   := 'Analyze index fragmentation 1.0' ;

-- criterias


  pct_used_threshold    number  :=50 ;  -- a/ % used space =< 50 %              PCT_USED
  MB_gain_threshold     number  :=64 ; -- a'/ MB gain > 250 MB                                         BTREE_SPACE-USED_SPACE


  del_rate_threshold    number  :=20 ;  -- b/ Deleted >= 20%                    DEL_LF_ROWS*100/LF_ROWS


  height_threshold      number  :=4 ;   -- c/ number of index levels is > 3     HEIGHT


  opt_cmpr_threshold    number  :=1 ;   -- d/ index key compression             OPT_CMPR_COUNT
  opt_cmpr_gain_threshold       number  :=30 ; -- d'/ compressed gain >= 30 %   OPT_CMPR_PCTSAVE


  dist_keys_threshold   number  :=95 ;  -- e/ index candidate to bitmap ?       LF_ROWS-DISTINCT_KEYS*100/LF_ROWS


-- 2/ cursor


Cursor operation_cur is
select sum(num_rows) over ( partition by table_name),table_name ,index_name ,
       'analyze index '||a.owner||'.'||index_name||' validate structure' order1,
'select '
||'case when PCT_USED <= '||pct_used_threshold||' AND '  -- used space =< 50 %
||'trunc((BTREE_SPACE-USED_SPACE)/1024/1024,2) > '||MB_gain_threshold||' ' -- gain > 250 MB
||' then '
||'decode(SEGMENT_SPACE_MANAGEMENT,''AUTO'','
||'''Alter index '||owner||'.'||index_name||' shrink space ;-- Gain: ''||ceil((BTREE_SPACE-USED_SPACE)/1024/1024)||''MB original size: ''||ceil((BTREE_SPACE)/1024/1024)||''MB'','
||'''Alter index '||owner||'.'||index_name||' rebuild ; -- Gain: ''||trunc((BTREE_SPACE-USED_SPACE)/1024/1024,2)||'' MB'')'
||' else '
||'decode(SIGN(DEL_LF_ROWS*100/decode(LF_ROWS, 0, 1, LF_ROWS)-'||del_rate_threshold||'),1,' -- Deleted >= 20%
||'decode(SEGMENT_SPACE_MANAGEMENT,''AUTO'','
||'''Alter index '||owner||'.'||index_name||' shrink space ; -- Del.rate too high: ''||trunc(DEL_LF_ROWS*100/decode(LF_ROWS, 0, 1, LF_ROWS),2)||''%'','
||'''Alter index '||owner||'.'||index_name||' rebuild ; -- Del.rate too high: ''||trunc(DEL_LF_ROWS*100/decode(LF_ROWS, 0, 1, LF_ROWS),2)||''%''),'
||'decode(SIGN(HEIGHT-'||height_threshold||'),1,' -- number of index levels is > 3
||'decode(SEGMENT_SPACE_MANAGEMENT,''AUTO'','
||'''Alter index '||owner||'.'||index_name||' shrink space ;'','
||'''Alter index '||owner||'.'||index_name||' rebuild ;''),'
||'case when OPT_CMPR_COUNT > '||opt_cmpr_threshold||' AND ' -- index key compression
||'OPT_CMPR_PCTSAVE > '||opt_cmpr_gain_threshold||''  -- compressed gain >= 30 %
||' then '
||'''Alter index '||owner||'.'||index_name||' rebuild compress ''||OPT_CMPR_COUNT||''; -- Gain: ''||OPT_CMPR_PCTSAVE||''% => ''||trunc(OPT_CMPR_PCTSAVE*d.blocks*8192/(1024*1024),2)||''MB'''
||' else '
||'decode(SIGN((LF_ROWS-DISTINCT_KEYS)*100/ decode(LF_ROWS,0,1,LF_ROWS)-'||dist_keys_threshold||'),1,'
||''' -- '||owner||'.'||index_name||' Candidate for Bitmap: ''||trunc((LF_ROWS-DISTINCT_KEYS)*100/ decode(LF_ROWS,0,1,LF_ROWS),2)||''%'')'
||' end )) '
||' end '
||'from index_stats d , dba_segments e , dba_tablespaces f where e.TABLESPACE_NAME = f.TABLESPACE_NAME and e.segment_name = '''||index_name||''' '
||'and name = '''||index_name||''' ' order2
       ,trunc(100*(row_number() over (order by table_name,index_name)/count(*) over ()),2) cnt_l
       ,trunc(100*sum(num_rows) over ( order by  table_name,index_name rows between unbounded preceding and current row )/sum(num_rows) over(),2) cnt_r
    from dba_indexes a , ( select owner sowner ,segment_name,sum(bytes) from dba_segments group by owner,segment_name ) b
    where owner=''||SCH||''
    and   owner=b.sowner
    and   index_name=b.segment_name
--    and table_name like 'TABLE1'
    and num_rows > 0
    --and rownum < 4
    order by table_name ,index_name;
  TYPE    Cur_Typ IS REF CURSOR;
  cur_dyn Cur_Typ;
  order2  varchar2(4000);
  order3  varchar2(4000);
  tables  varchar2(4000):='x';
  Stats   varchar2(4000):='';
begin
        dbms_output.disable;        -- *** Line added ***
        dbms_output.enable(100000); -- *** Line added ***

  dbms_output.put_line('-- ===========================');
  dbms_output.put_line('-- ANALYZE DATABASE INDEX v1.0');
  dbms_output.put_line('-- ===========================');
  dbms_output.put_line('-- Start date:'||to_char(sysdate,'DD/MM/YY HH24:MI:SS'));
  dbms_output.put_line('-- ----------------------------');
  dbms_output.put_line('--');
  dbms_application_info.set_module('ANA.IDX DB v1.0','@');
  dbms_output.put_line('--      Stats: Blevel - Del - Rows - Distinct - used - Opt – PctOpt – Idx_Sz_MB');
  for operation_rec in operation_cur loop
       Stats:='OK';
       if tables<>operation_rec.table_name then
          tables:=operation_rec.table_name;
          dbms_output.put_line('-- ');
          dbms_output.put_line('-- TABLE NAME : '||operation_rec.table_name);
          dbms_application_info.set_module('ANA.IDX DB v1.0-'||operation_rec.cnt_l||'-'||operation_rec.cnt_r,'Tbl:'||operation_rec.table_name);
       end if;
       dbms_application_info.set_client_info('Ana.Idx:'||operation_rec.index_name||'('||to_char(sysdate,'HH24:mi')||')');
       dbms_output.put_line('--      Ana.Idx: ('||to_char(sysdate,'DD/MM/YY HH24:mi:ss')||') -- '||operation_rec.index_name);
       Begin
        execute immediate operation_rec.order1;
       exception
        WHEN OTHERS THEN
          Stats:='NOK';
       end;
       dbms_application_info.set_client_info('Wri.Idx:'||operation_rec.index_name||'('||to_char(sysdate,'HH24:mi')||')');
       open cur_dyn for operation_rec.order2;
        fetch cur_dyn INTO order2;
       close cur_dyn;
       if stats='OK' then
select height||'-'||DEL_LF_ROWS||'-'||LF_ROWS||'-'||DISTINCT_KEYS||'-'||PCT_USED||'-'||OPT_CMPR_COUNT||'-'||OPT_CMPR_PCTSAVE||'-'||ceil((BTREE_SPACE)/1024/1024)
                       into order3 from index_stats;
          dbms_output.put_line('--      Stats: '||order3);
          dbms_output.put_line(order2);
       else
          dbms_output.put_line('--      '||operation_rec.index_name||' --> Resource busy NOT ANALYZED');
       end if;
  end loop;
  dbms_application_info.set_module('GENE.COMPRESS DATABASE v1.0','End date :'||to_date(sysdate,'DD/MM/YY HH24:MI:SS'));
  dbms_output.put_line('--');
  dbms_output.put_line('-- ---------------------------');
  dbms_output.put_line('-- End date:'||to_char(sysdate,'DD/MM/YY HH24:MI:SS'));
  dbms_output.put_line('-- ===========================');
  dbms_output.put_line('-- ANALYZE DATABASE INDEX v1.0');
  dbms_output.put_line('-- ===========================');
end;
/



$ nohup sqlplus system  @ana_idx_fra.sql SCH1 &

old   3:   schema        VARCHAR2 (20)   := '&1' ;
new   3:   schema        VARCHAR2 (20)   := 'SCH1' ;


-- ===========================
-- ANALYZE DATABASE INDEX v1.0
-- ===========================
-- Start date:14/06/16 15:34:03
-- ----------------------------
--
--      Stats: Blevel - Del - Rows - Distinct - used - Opt – PctOpt – Idx_Sz_MB
--
-- TABLE NAME : AM_ACTION
--      Ana.Idx: (14/06/16 15:34:03) -- IDX_AM_ACTION215
--      Stats: 3-0-690588-72828-90-1-23-12
--      Ana.Idx: (14/06/16 15:34:04) -- PK_AM_ACTION
--      Stats: 3-0-690621-690621-90-0-0-12
--
-- TABLE NAME : USERS_MON
--      Ana.Idx: (14/06/16 15:34:04) -- USERS_MON_I01
--      Stats: 3-0-6736638-6728353-46-0-0-256
Alter index ADB.USERS_MON_I01 shrink space ;-- Gain: 140MB original size: 256MB
--
-- ---------------------------
-- End date:14/06/16 15:34:09
-- ===========================
-- ANALYZE DATABASE INDEX v1.0
-- ===========================
PL/SQL procedure successfully completed.

-- ===========================
-- ANALYZE DATABASE INDEX v1.0
-- ===========================
-- Start date:18/06/14 16:26:39
-- ----------------------------
--
--      Stats: Blevel - Del - Rows - Distinct - used - Opt - PctOpt
--
-- TABLE NAME : TABLE1
--      Ana.Idx: (18/06/14 16:26:44) -- I_FK_T_MISSION0
--      Stats: 3-883093-38704947-3194090-59-1-27
Alter index SCH1.I_FK_T_MISSION0 shrink space ;-- Gain: 432MB original size: 1047MB
--      Ana.Idx: (18/06/14 16:38:53) -- I_TABLE1_TRC_HEURE
--      Stats: 4-3472774-41294628-749-42-1-41
Alter index SCH1.I_TABLE1_TRC_HEURE shrink space ;-- Gain: 1068MB original size: 1821MB
--      Ana.Idx: (18/06/14 16:57:42) -- PK_TABLE1
--      Stats: 3-853192-38675046-38675046-66-0-0
Alter index SCH1.PK_TABLE1 shrink space ;-- Gain: 317MB original size: 907MB
--
-- ---------------------------
-- End date:18/06/14 17:06:10
-- ===========================
-- ANALYZE DATABASE INDEX v1.0
-- ===========================

PL/SQL procedure successfully completed.

Elapsed: 00:39:32.31

SHRINK

set timing on
Alter index SCH1.PK_TABLE1 shrink space ;
Elapsed: 00:21:09.88

Ask again for Index Stats :


--      Stats: Blevel - Del - Rows - Distinct - used - Opt – PctOpt – Idx_Sz_MB

-- TABLE NAME : TABLE1
--      Ana.Idx: (20/06/14 10:15:04) -- PK_TABLE1
--      Stats: 3-245-37940694-37940694-97-0-0-601


!! The SHRINK Commands are very Log Consuming !!