关于Oracle表碎片整理

   
   数据库在日常使用过程中,不断的insert,delete,update操作,导致表和索引出现碎片是在所难免的事情,碎片多了,sql的执行效率自然就差了,道理很简单,高水位线(HWL)下的许多数据块都是无数据的,但全表扫描的时候要扫描到高水位线的数据块,也就是说oracle要做许多的无用功!因此oracle提供了shrink space碎片整理功能。对于索引,可以采取rebuild online的方式进行碎片整理,一般来说,经常进行DML操作的对象DBA要定期进行维护,同时注意要及时更新统计信息!

一:准备测试数据,使用HR用户,创建T1表,插入约30W的数据,并根据object_id创建普通索引,表占存储空间34M左右,索引占6M左右的存储空间

点击(此处)折叠或打开

  1. SQL> conn /as sysdba
  2. 已连接。
  3. SQL> select default_tablespace from dba_users where username='HR';
  4.   
  5. DEFAULT_TABLESPACE
  6. ------------------------------------------------------------
  7. USERS
  8.   
  9. SQL> conn hr/hr
  10. 已连接。
  11.   
  12. SQL> insert into t1 select * from t1;
  13. 已创建 74812 行。
  14.   
  15. SQL> insert into t1 select * from t1;
  16. 已创建 149624 行。
  17.   
  18. SQL> commit;
  19. 提交完成。
  20.   
  21. SQL> create index idx_t1_id on t1(object_id);
  22. 索引已创建。
  23.   
  24. SQL> exec dbms_stats.gather_table_stats('HR','T1',CASCADE=>TRUE);
  25. PL/SQL 过程已成功完成。
  26.   
  27. SQL> select count(1) from t1;
  28.   
  29.   COUNT(1)
  30. ----------
  31.     299248
  32.   
  33. SQL> select sum(bytes)/1024/1024 from dba_segments where segment_name='T1';
  34. SUM(BYTES)/1024/1024
  35. --------------------
  36.              34.0625
  37.   
  38. SQL> select sum(bytes)/1024/1024 from dba_segments where segment_name='IDX_T1_ID';
  39. SUM(BYTES)/1024/1024
  40. --------------------
  41.                    6

二:估算表在高水位线下还有多少空间可用,这个值应当越低越好,表使用率越接近高水位线,全表扫描所做的无用功也就越少!

DBMS_STATS包无法获取EMPTY_BLOCKS统计信息,所以需要用analyze命令再收集一次统计信息

点击(此处)折叠或打开

  1. SQL> SELECT blocks, empty_blocks, num_rows FROM user_tables WHERE table_name ='T1';
  2.   
  3.     BLOCKS EMPTY_BLOCKS NUM_ROWS
  4. ---------- ------------ ----------
  5.  4302      0            299248
  6.   
  7. SQL> analyze table t1 compute statistics;
  8. 表已分析。
  9.   
  10. SQL> SELECT blocks, empty_blocks, num_rows FROM user_tables WHERE table_name ='T1';
  11.   
  12.     BLOCKS EMPTY_BLOCKS NUM_ROWS
  13. ---------- ------------ ----------
  14. 4302       50           299248
  15.   
  16. SQL> col table_name for a20
  17. SQL> SELECT TABLE_NAME, (BLOCKS * 8192 / 1024 / 1024) -
  18.  (NUM_ROWS * AVG_ROW_LEN / 1024 / 1024) "Data lower than HWM in MB"
  19.      FROM USER_TABLES
  20.      WHERE table_name = 'T1';
  21.   
  22. TABLE_NAME Data lower than HWM in MB
  23. -------------------- -------------------------
  24. T1                   5.07086182

三: 查看执行计划,全表扫描大概需要消耗CPU 1175

点击(此处)折叠或打开

  1. SQL> explain plan for select * from t1;
  2. 已解释。
  3.   
  4. SQL> select * from table(dbms_xplan.display);
  5.   
  6. PLAN_TABLE_OUTPUT
  7. --------------------------------------------------------------------------------
  8. Plan hash value: 3617692013
  9. --------------------------------------------------------------------------
  10. | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
  11. --------------------------------------------------------------------------
  12. | 0 | SELECT STATEMENT | | 299K| 28M| 1175 (1)| 00:00:15 |
  13. | 1 | TABLE ACCESS FULL| T1 | 299K| 28M| 1175 (1)| 00:00:15 |

四:删除大部分数据,收集统计信息,全表扫描依然需要消耗CPU 1168

点击(此处)折叠或打开

  1. SQL> delete from t1 where object_id>100;
  2. 已删除298852行。
  3.   
  4. SQL> commit;
  5. 提交完成。
  6.   
  7. SQL> select count(*) from t1;
  8.   
  9.   COUNT(*)
  10. ----------
  11.        396
  12.   
  13. SQL> exec dbms_stats.gather_table_stats('HR','T1',CASCADE=>TRUE);
  14. PL/SQL 过程已成功完成。
  15.   
  16. SQL> analyze table t1 compute statistics;
  17. 表已分析。
  18.   
  19. SQL> SELECT blocks, empty_blocks, num_rows FROM user_tables WHERE table_name ='T1';
  20.   
  21.     BLOCKS EMPTY_BLOCKS NUM_ROWS
  22. ---------- ------------ ----------
  23. 4302       50           396
  24.   
  25.   
  26. SQL> explain plan for select * from t1;
  27. 已解释。
  28.   
  29. SQL> select * from table(dbms_xplan.display);
  30.   
  31. PLAN_TABLE_OUTPUT
  32. ------------------------------------------------------------------------------
  33. Plan hash value: 3617692013
  34. --------------------------------------------------------------------------
  35. | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
  36. --------------------------------------------------------------------------
  37. | 0 | SELECT STATEMENT | | 396 | 29700 | 1168 (1)| 00:00:15 |
  38. | 1 | TABLE ACCESS FULL| T1 | 396 | 29700 | 1168 (1)| 00:00:15 |

五:估算表在高水位线下还有多少空间是无数据的,但在全表扫描时又需要做无用功的数

点击(此处)折叠或打开

  1. SQL> SELECT TABLE_NAME, (BLOCKS * 8192 / 1024 / 1024) -
  2.            (NUM_ROWS * AVG_ROW_LEN / 1024 / 1024) "Data lower than HWM in MB"
  3.      FROM USER_TABLES
  4.      WHERE table_name = 'T1';
  5.   
  6. TABLE_NAME Data lower than HWM in MB
  7. -------------------- -------------------------
  8. T1                   33.5791626

六:对表进行碎片整理,重新收集统计信息

点击(此处)折叠或打开

  1. SQL> alter table t1 enable row movement;
  2. 表已更改。
  3.   
  4. SQL> alter table t1 shrink space cascade;
  5. 表已更改。
  6.   
  7. SQL> select sum(bytes)/1024/1024 from dba_segments where segment_name='T1';
  8.   
  9. SUM(BYTES)/1024/1024
  10. --------------------
  11.                 .125
  12.   
  13. SQL> select sum(bytes)/1024/1024 from dba_segments where segment_name='IDX_T1_ID
  14. ';
  15.   
  16. SUM(BYTES)/1024/1024
  17. --------------------
  18.                .0625
  19.   
  20. SQL> SELECT TABLE_NAME, (BLOCKS * 8192 / 1024 / 1024) -
  21.            (NUM_ROWS * AVG_ROW_LEN / 1024 / 1024) "Data lower than HWM in MB"
  22.      FROM USER_TABLES
  23.      WHERE table_name = 'T1';
  24.   
  25. TABLE_NAME Data lower than HWM in MB
  26. -------------------- -------------------------
  27. T1 33.5791626
  28.   
  29. SQL> exec dbms_stats.gather_table_stats('HR','T1',CASCADE=>TRUE);
  30. PL/SQL 过程已成功完成。
  31.   
  32. 这个时候,只剩下0.1M的无用功了,执行计划中,全表扫描也只需要消耗CPU 3
  33. SQL> SELECT TABLE_NAME, (BLOCKS * 8192 / 1024 / 1024) -
  34.            (NUM_ROWS * AVG_ROW_LEN / 1024 / 1024) "Data lower than HWM in MB"
  35.      FROM USER_TABLES
  36.      WHERE table_name = 'T1';
  37.   
  38. TABLE_NAME Data lower than HWM in MB
  39. -------------------- -------------------------
  40. T1 .010738373
  41.   
  42.   
  43. SQL> select * from table(dbms_xplan.display);
  44.   
  45. PLAN_TABLE_OUTPUT
  46. --------------------------------------------------------------------------------
  47. Plan hash value: 3617692013
  48. --------------------------------------------------------------------------
  49. | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
  50. --------------------------------------------------------------------------
  51. | 0 | SELECT STATEMENT | | 396 | 29700 | 3 (0)| 00:00:01 |
  52. | 1 | TABLE ACCESS FULL| T1 | 396 | 29700 | 3 (0)| 00:00:01 |
  53. --------------------------------------------------------------------------
  54.   
  55. 总共只有5个块,空块却有50个,明显empty_blocks信息过期
  56. SQL> select blocks,empty_blocks,num_rows from user_tables where table_name='T1';
  57.   
  58.     BLOCKS EMPTY_BLOCKS NUM_ROWS
  59. ---------- ------------ ----------
  60.          5 50 396
  61.   
  62. SQL> analyze table t1 compute statistics;
  63. 表已分析。
  64.   
  65. SQL> select blocks,empty_blocks,num_rows from user_tables where table_name='T1';
  66.   
  67.   
  68.     BLOCKS EMPTY_BLOCKS NUM_ROWS
  69. ---------- ------------ ----------
  70.          5 3 396

请使用浏览器的分享功能分享到微信等