Oracle 收缩表高水位线节省空间
1、模拟在一张表中插入数据并收集统计信息
 SQL> create table ahern.test as select * from dba_objects;
表已创建。
SQL> insert into ahern.test select * from ahern.test;
已创建 72974 行。
SQL> insert into ahern.test select * from ahern.test;
已创建 145948 行。
SQL> insert into ahern.test select * from ahern.test;
已创建 291896 行。
SQL> commit;
提交完成。
SQL> exec dbms_stats.gather_table_stats(ownname => 'AHERN',tabname => 'TEST',DEGREE => 2);
PL/SQL 过程已成功完成。
查询空间情况,可以看到初始表的碎片为 75254.44。
 SQL> SELECT table_name,
   2         ROUND((blocks * 8), 2) "HW_space",
   3         ROUND((num_rows * avg_row_len / 1024), 2) "real_space",
   4         ROUND((blocks * 10 / 100) * 8, 2) "pctfree",
   5         ROUND((blocks * 8 - (num_rows * avg_row_len / 1024) -
   6               blocks * 8 * 10 / 100),
   7               2) "waste_space"
   8    FROM dba_tables
   9   WHERE temporary = 'N'
  10     AND TABLE_NAME = 'TEST'
  11     and OWNER = 'AHERN';
TABLE_NAME HW_space  real_space  pctfree waste_space
 ---------- ---------- ------------ ----------- -------------
 TEST            97352     75254.44      9735.2      12362.36
2、第一次删除数据
 SQL> delete from ahern.test where object_type in ('SYNONYM', 'JAVA CLASS');
已删除 391224 行。
SQL> commit;
提交完成。
SQL> exec dbms_stats.gather_table_stats(ownname => 'AHERN',tabname => 'TEST',DEGREE => 2);
PL/SQL 过程已成功完成。
注:第一次删除了部分数据后查询空间水位线情况,可以看到表的碎片为 23318.78。
 SQL> SELECT table_name,
   2         ROUND((blocks * 8), 2) "HW_space",
   3         ROUND((num_rows * avg_row_len / 1024), 2) "real_space",
   4         ROUND((blocks * 10 / 100) * 8, 2) "pctfree",
   5         ROUND((blocks * 8 - (num_rows * avg_row_len / 1024) -
   6               blocks * 8 * 10 / 100),
   7               2) "waste_space"
   8    FROM dba_tables
   9   WHERE temporary = 'N'
  10     AND TABLE_NAME = 'TEST'
  11     and OWNER = 'AHERN';
TABLE_NAME HW_space  real_space  pctfree  waste_space
 ---------- ---------- ------------ ----------- -------------
 TEST            97352     23318.78      9735.2      64298.02
3、第二次删除数据
 SQL> delete from ahern.test where object_type in('VIEW','INDEX','TABLE','TYPE');
已删除 123824 行。
SQL> commit;
提交完成。
SQL> exec dbms_stats.gather_table_stats(ownname => 'AHERN',tabname => 'TEST',DEGREE => 2);
PL/SQL 过程已成功完成。
注:第二次删除大量数据后查询表的碎片为 8525.87,从这里对比第一次删除数据后的空间使用情况可以看到浪费的空间一直在增加,从第一次删除数据到第二次删除数据,浪费的空间增加倒到了 79090.93,并且高水位线一直没有变化为 97352。
 SQL> SELECT table_name,
   2         ROUND((blocks * 8), 2) "HW_space",
   3         ROUND((num_rows * avg_row_len / 1024), 2) "real_space",
   4         ROUND((blocks * 10 / 100) * 8, 2) "pctfree",
   5         ROUND((blocks * 8 - (num_rows * avg_row_len / 1024) -
   6               blocks * 8 * 10 / 100),
   7               2) "waste_space"
   8    FROM dba_tables
   9   WHERE temporary = 'N'
  10     AND TABLE_NAME = 'TEST'
  11     and OWNER = 'AHERN';
TABLE_NAME HW_space  real_space  pctfree  waste_space 
 ---------- ---------- ------------ ----------- -------------
 TEST            97352      8525.87      9735.2      79090.93
4、模拟整理碎片节省空间
 开始收缩水位线整理碎片:
 SQL> alter table ahern.test enable row movement;
表已更改。
SQL> alter table ahern.test shrink space;
表已更改。
SQL> exec dbms_stats.gather_table_stats(ownname => 'AHERN',tabname => 'TEST',DEGREE => 2);
PL/SQL 过程已成功完成。
注:通过上述收缩进行表碎片整理后,我们可以看到表的高水位已经降下来为 10120,并且浪费的空间也已经被释放,释放前 79090.93 ,释放后占用仅为 582.13。
 SQL> SELECT table_name,
   2         ROUND((blocks * 8), 2) "HW_space",
   3         ROUND((num_rows * avg_row_len / 1024), 2) "real_space",
   4         ROUND((blocks * 10 / 100) * 8, 2) "pctfree",
   5         ROUND((blocks * 8 - (num_rows * avg_row_len / 1024) -
   6               blocks * 8 * 10 / 100),
   7               2) "waste_space"
   8    FROM dba_tables
   9   WHERE temporary = 'N'
  10     AND TABLE_NAME = 'TEST'
  11     and OWNER = 'AHERN';
TABLE_NAME HW_space  real_space  pctfree  waste_space
 ---------- ---------- ------------ ----------- -------------
 TEST            10120      8525.87        1012        582.13
到此,表碎片整理结束。
注意:如需在生产环境中做表碎片整理,建议在业务空闲期间进行,避免影响业务。