表碎片整理测试、原理与实施风险

2020-08-18 00:00:00 数据 执行 业务 方式 碎片

表碎片测试


[root@dg2 ~]# cat 20191207FragmentTest.txt 


create tablespace testlin datafile  '/u01/app/oracle/oradata/prod/testlin01.dbf'  size 20m;


create table scott.testlin tablespace testlin as select * from all_objects;

exec dbms_stats.gather_table_stats('SCOTT','TESTLIN',cascade=>true);

此时都是满块,没有任何DML操作,都是插入数据,随后测试满块和随机删除数据后表段空间使用情况


**************************************************************************************************************************

drop table scott.testlin purge;


 alter table scott.testlin enable row movement;


alter table scott.testlin shrink space;


create table scott.testlin tablespace testlin as select * from all_objects;


drop table scott.testlin purge


select table_name,blocks,num_rows,last_analyzed,round((1-(round((avg_row_len*num_rows),2)/1024/1024)/(round((blocks*8),2)/1024)),2)*100||'%' freerto from dba_tables where owner='SCOTT' and blocks>0 and table_name='TESTLIN';



set serveroutput on

declare

v_unformatted_blocks number;

v_unformatted_bytes number;

v_fs1_blocks number;

v_fs1_bytes number;

v_fs2_blocks number;

v_fs2_bytes number;

v_fs3_blocks number;

v_fs3_bytes number;

v_fs4_blocks number;

v_fs4_bytes number;

v_full_blocks number;

v_full_bytes number;

begin

dbms_space.space_usage ('&owner', '&tab_name', 'TABLE', v_unformatted_blocks,

v_unformatted_bytes, v_fs1_blocks, v_fs1_bytes, v_fs2_blocks, v_fs2_bytes,

v_fs3_blocks, v_fs3_bytes, v_fs4_blocks, v_fs4_bytes, v_full_blocks, v_full_bytes);

dbms_output.put_line('Unformatted Blocks = '||v_unformatted_blocks);

dbms_output.put_line('FS1 Blocks = '||v_fs1_blocks);

dbms_output.put_line('FS2 Blocks = '||v_fs2_blocks);

dbms_output.put_line('FS3 Blocks = '||v_fs3_blocks);

dbms_output.put_line('FS4 Blocks = '||v_fs4_blocks);

dbms_output.put_line('Full Blocks = '||v_full_blocks);

end;

/

**************************************************************************************************************************

满块两种方式统计结果

SYS@prod> select table_name,blocks,num_rows,last_analyzed,round((1-(round((avg_row_len*num_rows),2)/1024/1024)/(round((blocks*8),2)/1024)),2)*100||'%' freerto from dba_tables where owner='SCOTT' and blocks>0 and table_name='TESTLIN';


TABLE_NAME                         BLOCKS   NUM_ROWS LAST_ANAL FREERTO

------------------------------ ---------- ---------- --------- -----------------------------------------

TESTLIN                              1242      84989 07-DEC-19 18%


SYS@prod> set serveroutput on

SYS@prod> declare

  2  v_unformatted_blocks number;

  3  v_unformatted_bytes number;

  4  v_fs1_blocks number;

  5  v_fs1_bytes number;

  6  v_fs2_blocks number;

  7  v_fs2_bytes number;

  8  v_fs3_blocks number;

  9  v_fs3_bytes number;

 10  v_fs4_blocks number;

 11  v_fs4_bytes number;

 12  v_full_blocks number;

 13  v_full_bytes number;

 14  begin

 15  dbms_space.space_usage ('SCOTT', 'TESTLIN', 'TABLE', v_unformatted_blocks,

 16  v_unformatted_bytes, v_fs1_blocks, v_fs1_bytes, v_fs2_blocks, v_fs2_bytes,

 17  v_fs3_blocks, v_fs3_bytes, v_fs4_blocks, v_fs4_bytes, v_full_blocks, v_full_bytes);

 18  dbms_output.put_line('Unformatted Blocks = '||v_unformatted_blocks);

 19  dbms_output.put_line('FS1 Blocks = '||v_fs1_blocks);

 20  dbms_output.put_line('FS2 Blocks = '||v_fs2_blocks);

 21  dbms_output.put_line('FS3 Blocks = '||v_fs3_blocks);

 22  dbms_output.put_line('FS4 Blocks = '||v_fs4_blocks);

 23  dbms_output.put_line('Full Blocks = '||v_full_blocks);

 24  end;

 25  /

Unformatted Blocks = 0

FS1 Blocks = 0

FS2 Blocks = 0

FS3 Blocks = 0

FS4 Blocks = 0

Full Blocks = 1214


PL/SQL procedure successfully completed.

第二种方式更科学(ASSM方式),种属于MSSM方式


SYS@prod> delete from scott.testlin where  mod(object_id,3) = 0;


28325 rows deleted.



SYS@prod>  exec dbms_stats.gather_table_stats('SCOTT','TESTLIN',cascade=>true);


PL/SQL procedure successfully completed.


继续使用两种方式计算碎片


SYS@prod> select table_name,blocks,num_rows,last_analyzed,round((1-(round((avg_row_len*num_rows),2)/1024/1024)/(round((blocks*8),2)/1024)),2)*100||'%' freerto from dba_tables where owner='SCOTT' and blocks>0 and table_name='TESTLIN';


TABLE_NAME                         BLOCKS   NUM_ROWS LAST_ANAL FREERTO

------------------------------ ---------- ---------- --------- -----------------------------------------

TESTLIN                              1242      56664 07-DEC-19 45%


SYS@prod> declare

  2  v_unformatted_blocks number;

  3  v_unformatted_bytes number;

  4  v_fs1_blocks number;

  5  v_fs1_bytes number;

  6  v_fs2_blocks number;

  7  v_fs2_bytes number;

  8  v_fs3_blocks number;

  9  v_fs3_bytes number;

 10  v_fs4_blocks number;

 11  v_fs4_bytes number;

 12  v_full_blocks number;

 13  v_full_bytes number;

 14  begin

 15  dbms_space.space_usage ('SCOTT', 'TESTLIN', 'TABLE', v_unformatted_blocks,

 16  v_unformatted_bytes, v_fs1_blocks, v_fs1_bytes, v_fs2_blocks, v_fs2_bytes,

 17  v_fs3_blocks, v_fs3_bytes, v_fs4_blocks, v_fs4_bytes, v_full_blocks, v_full_bytes);

 18  dbms_output.put_line('Unformatted Blocks = '||v_unformatted_blocks);

 19  dbms_output.put_line('FS1 Blocks = '||v_fs1_blocks);

 20  dbms_output.put_line('FS2 Blocks = '||v_fs2_blocks);

 21  dbms_output.put_line('FS3 Blocks = '||v_fs3_blocks);

 22  dbms_output.put_line('FS4 Blocks = '||v_fs4_blocks);

 23  dbms_output.put_line('Full Blocks = '||v_full_blocks);

 24  end;

 25  /

Unformatted Blocks = 0

FS1 Blocks = 0

FS2 Blocks = 1213

FS3 Blocks = 0

FS4 Blocks = 0

Full Blocks = 1


PL/SQL procedure successfully completed.

确实显示1213个块25-50%空间free,碎片严重


此时查询段大小,空间没有释放。


SYS@prod> select blocks from dba_segments where segment_name='TESTLIN';


    BLOCKS

----------

      1280

在消除碎片之前,创建索引,测试shrink space对索引的影响

SYS@prod>  create index scott.idx_testlin_id on  scott.testlin(object_id);


Index created.


SYS@prod> select index_name,status from dba_indexes  where owner='SCOTT';


INDEX_NAME                     STATUS

------------------------------ --------

PK_DEPT                        VALID

PK_EMP                         VALID

IDX_TESTLIN_ID                 VALID



下面执行shrink space

SYS@prod> alter table scott.testlin enable row movement;


Table altered.


SYS@prod> alter table scott.testlin shrink space; 


Table altered.



种方式

SYS@prod> select table_name,blocks,num_rows,last_analyzed,round((1-(round((avg_row_len*num_rows),2)/1024/1024)/(round((blocks*8),2)/1024)),2)*100||'%' freerto from dba_tables where owner='SCOTT' and blocks>0 and table_name='TESTLIN';


TABLE_NAME                         BLOCKS   NUM_ROWS LAST_ANAL FREERTO

------------------------------ ---------- ---------- --------- -----------------------------------------

TESTLIN                              1242      56664 07-DEC-19 45%

此时需要先收集统计信息,在执行该查询,否则剩余空间率不变

收集统计信息再收集


第二种方式

SYS@prod> declare

  2  v_unformatted_blocks number;

  3  v_unformatted_bytes number;

  4  v_fs1_blocks number;

  5  v_fs1_bytes number;

  6  v_fs2_blocks number;

  7  v_fs2_bytes number;

  8  v_fs3_blocks number;

  9  v_fs3_bytes number;

 10  v_fs4_blocks number;

 11  v_fs4_bytes number;

 12  v_full_blocks number;

 13  v_full_bytes number;

 14  begin

 15  dbms_space.space_usage ('SCOTT', 'TESTLIN', 'TABLE', v_unformatted_blocks,

 16  v_unformatted_bytes, v_fs1_blocks, v_fs1_bytes, v_fs2_blocks, v_fs2_bytes,

 17  v_fs3_blocks, v_fs3_bytes, v_fs4_blocks, v_fs4_bytes, v_full_blocks, v_full_bytes);

 18  dbms_output.put_line('Unformatted Blocks = '||v_unformatted_blocks);

 19  dbms_output.put_line('FS1 Blocks = '||v_fs1_blocks);

 20  dbms_output.put_line('FS2 Blocks = '||v_fs2_blocks);

 21  dbms_output.put_line('FS3 Blocks = '||v_fs3_blocks);

 22  dbms_output.put_line('FS4 Blocks = '||v_fs4_blocks);

 23  dbms_output.put_line('Full Blocks = '||v_full_blocks);

 24  end;

 25  /

Unformatted Blocks = 0

FS1 Blocks = 0

FS2 Blocks = 2

FS3 Blocks = 0

FS4 Blocks = 0

Full Blocks = 807

含义如下:

unformatted_blocks : Total number of blocks unformatted

fs1_blocks : Number of blocks having at least 0 to 25% free space

fs2_blocks : Number of blocks having at least 25 to 50% free space

fs3_blocks : Number of blocks having at least 50 to 75% free space

fs4_blocks : Number of blocks having at least 75 to free space

ful1_blocks : Total number of blocks full in the segment



PL/SQL procedure successfully completed.

比较准确,此时807个块是满块,碎片消除


再次查询段大小

SYS@prod> select blocks from dba_segments where segment_name='TESTLIN';


    BLOCKS

----------

       832

SYS@prod> exec dbms_stats.gather_table_stats('SCOTT','TESTLIN',cascade=>true);


PL/SQL procedure successfully completed.


SYS@prod> select table_name,blocks,num_rows,last_analyzed,round((1-(round((avg_row_len*num_rows),2)/1024/1024)/(round((blocks*8),2)/1024)),2)*100||'%' freerto from dba_tables where owner='SCOTT' and blocks>0 and table_name='TESTLIN';


TABLE_NAME                         BLOCKS   NUM_ROWS LAST_ANAL FREERTO

------------------------------ ---------- ---------- --------- -----------------------------------------

TESTLIN                               809      56664 07-DEC-19 16%


此时在表段testlin几乎满块的情况下freerto依然16%,这个计算是不准确的,只能作为参考


下面提高采样率,发现freerto依然不变,再次这种方法判断碎片严重程度不准确,好使用第二种方法判断碎片严重程度以及碎片整理的效果。


SYS@prod> exec dbms_stats.gather_table_stats('SCOTT','TESTLIN',estimate_percent=>100);


PL/SQL procedure successfully completed.


SYS@prod> select table_name,blocks,num_rows,last_analyzed,round((1-(round((avg_row_len*num_rows),2)/1024/1024)/(round((blocks*8),2)/1024)),2)*100||'%' freerto from dba_tables where owner='SCOTT' and blocks>0 and table_name='TESTLIN';


TABLE_NAME                         BLOCKS   NUM_ROWS LAST_ANAL FREERTO

------------------------------ ---------- ---------- --------- -----------------------------------------

TESTLIN                               809      56664 07-DEC-19 16%


下面查询下表的定义,发现 PCTFREE 10,也就是freerto少为10%,此时应该是满块,索引,从测试看这个数值在15-20之间可以不用考虑碎片,》20可以认为有碎片


碎片占空间大约为(freerto-20%)表段大小



SYS@prod> select dbms_metadata.get_ddl('TABLE','TESTLIN','SCOTT') from dual;


DBMS_METADATA.GET_DDL('TABLE','TESTLIN','SCOTT')

--------------------------------------------------------------------------------


  CREATE TABLE "SCOTT"."TESTLIN"

   (    "OWNER" VARCHAR2(30) NOT NULL ENABLE,

        "OBJECT_NAME" VARCHAR2(30) NOT NULL ENABLE,

        "SUBOBJECT_NAME" VARCHAR2(30),

        "OBJECT_ID" NUMBER NOT NULL ENABLE,

        "DATA_OBJECT_ID" NUMBER,

        "OBJECT_TYPE" VARCHAR2(19),

        "CREATED" DATE NOT NULL ENABLE,

        "LAST_DDL_TIME" DATE NOT NULL ENABLE,

        "TIMESTAMP" VARCHAR2(19),

        "STATUS" VARCHAR2(7),

        "TEMPORARY" VARCHAR2(1),

        "GENERATED" VARCHAR2(1),

        "SECONDARY" VARCHAR2(1),

        "NAMESPACE" NUMBER NOT NULL ENABLE,

        "EDITION_NAME" VARCHAR2(30)

   ) SEGMENT CREATION IMMEDIATE

  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255

 NOCOMPRESS LOGGING

  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645

  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1

  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)

  TABLESPACE "TESTLIN"  ENABLE ROW MOVEMENT



计算碎片的方式

HWM= dba_segments.blocks - dba_tables.empty_blocks

Non_empty=dba_tables.blocks

Fragment=HWM – Non_empty

Note: empty blocks are higher than HWM, so delete will not change empty blocks.


select dba_segments.blocks segment_blocks, dba_tables.empty_blocks, dba_tables.blocks as table_blocks, (dba_segments.blocks- dba_tables.empty_blocks)- dba_tables.blocks as Fragment_blocks from dba_segments, dba_tables where dba_segments.segment_name='TESTLIN' and dba_tables.table_name=dba_segments.segment_name;


表碎片整理的原理以及风险


segment shrink分为两个阶段:

1、数据重组(compact):通过一系列insertdelete操作,将数据尽量排列在段的前面。在这个过程中需要在表上加RX锁,即只在需要移动的行上加锁。由于涉及到rowid的改变,需要enable row movement.同时要disable基于rowidtrigger.这一过程对业务影响比较小。

2HWM调整:第二阶段是调整HWM位置,释放空闲数据块。此过程需要在表上加X锁,会造成表上的所有DML语句阻塞。在业务特别繁忙的系统上可能造成比较大的影响。

shrink space语句两个阶段都执行。

shrink space compact只执行个阶段。

如果系统业务比较繁忙,可以先执行shrink space compact重组数据,然后在业务不忙的时候再执行shrink space降低HWM释放空闲数据块。

shrink必须开启行迁移功能。

alter table table_name enable row movement ;


测试shrink space期间的全表扫描,会报invalid rowid错误,对生产库还是有影响


如果系统业务比较繁忙,可以先执行shrink space compact重组数据,然后在业务不忙的时候再执行shrink space降低HWM释放空闲数据块。

相关文章