INDEX FULL SCAN vs INDEX FAST FULL SCAN

轉自:http://blog.csdn.net/robinson_0612/article/details/7452310


    INDEX FULL SCAN 與 INDEX FAST FULL SCAN兩個長相差不多,乃是一母同胞,因此既有其共性,也有其個性。兩者來說其共性是不用掃描
表而是通過索引就可以直接返回所需要的所有數據。這對提高查詢性能而言,無疑是一個難得的數據訪問方式之一,因爲索引中存儲的數據通常
是遠小於原始表的數據。下面具體來看看兩者之間的異同。

 

一、何時INDEX FULL SCAN 或 INDEX FAST FULL SCAN
   1、select 與where子句中出現的所有列必須存在索引
   2、查詢返回的數據行總數佔據整個索引10%以上的比率。取決於db_file_multiblock_read_count值與並行度的值
   3、滿足像統計行數這樣的一些特定的標準,如count(*)這樣的操作。count(*)操作幾乎總是使用INDEX FAST FULL SCAN
   4、對於索引列上order by之類的操作幾乎總是使用INDEX FULL SCAN

   注: 參數db_file_multiblock_read_count會在index fast full scan 像full table scan一樣生效,因爲整個索引都被訪問,Oracle 此時
 允許出現多塊讀(multiblock_read)。db_file_multiblock_read_count與paralle僅僅對index fast full scan情形。其次paralle在新版Oracle
 中是否支持待證實。一旦上述幾個條件滿足,基於成本的優化器根據表和索引的統計信息來調用 index full scan 或者index fast full scan。
 對於index fast full scan可以通過使用提示index_ffs來實現。
 
二、何謂INDEX FULL SCAN 與 INDEX FAST FULL SCAN  

  1. -->創建演示表t  
  2. scott@CNMMBO> create table t as select * from dba_objects where 1=2;  
  3.   
  4. -->爲表t填充數據  
  5. scott@CNMMBO> insert into t select * from dba_objects where object_id is not null;  
  6.   
  7. scott@CNMMBO> commit;  
  8.   
  9. --爲表t的object_id列添加索引  
  10. scott@CNMMBO> create index i_t_object_id on t(object_id);  
  11.   
  12. -->收集表t上的統計信息  
  13. scott@CNMMBO> exec dbms_stats.gather_table_stats('SCOTT','T',cascade=>true);  
  14.   
  15. scott@CNMMBO> set autot trace exp;  
  16.   
  17. scott@CNMMBO> select object_id from t;  
  18.   
  19. Execution Plan  
  20. ----------------------------------------------------------  
  21. Plan hash value: 1601196873  
  22.   
  23. --------------------------------------------------------------------------  
  24. | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  25. --------------------------------------------------------------------------  
  26. |   0 | SELECT STATEMENT  |      | 50422 |   246K|   166   (1)| 00:00:02 |  
  27. |   1 |  TABLE ACCESS FULL| T    | 50422 |   246K|   166   (1)| 00:00:02 |  
  28. --------------------------------------------------------------------------  
  29.   
  30. --從上面的執行計劃中可知,此時走了全表掃描。  
  31. --由於我們需要查詢的列爲object_id,因此理論上只需要讀取索引就應該可以返回所有數據,而此時爲什麼是全表掃描呢?  
  32. --這是因爲NULL值與索引的特性所決定的。即null值不會被存儲到B樹索引。因此應該爲表 t 的列 object_id 添加 not null 約束。  
  33. 有關null值與索引請參考  
  34.     NULL 值與索引(一) http://blog.csdn.net/robinson_0612/article/details/7437561  
  35.    NULL 值與索引(二) http://blog.csdn.net/robinson_0612/article/details/7438397  
  36.   
  37. --爲列object_id添加 not null約束  
  38. scott@CNMMBO> alter table t modify(object_id not null);  
  39.   
  40. --添加約束後的執行計劃  
  41. scott@CNMMBO> select object_id from t;  
  42.   
  43. Execution Plan  
  44. ----------------------------------------------------------  
  45. Plan hash value: 2036340805  
  46.   
  47. --------------------------------------------------------------------------------------  
  48. | Id  | Operation            | Name          | Rows  | Bytes | Cost (%CPU)| Time     |  
  49. --------------------------------------------------------------------------------------  
  50. |   0 | SELECT STATEMENT     |               | 50422 |   246K|    26   (0)| 00:00:01 |  
  51. |   1 |  INDEX FAST FULL SCAN| I_T_OBJECT_ID | 50422 |   246K|    26   (0)| 00:00:01 |  
  52. --------------------------------------------------------------------------------------    
  53.   
  54. --從上面的執行計劃可知,此時走的是索引快速全掃描,整個cost比全表掃描呈數量級下降  
  55.   
  56. INDEX FAST FULL SCAN  
  57.        類似於full table scan,使用該方式當在高速緩存中沒有找到所需的索引塊時,則根據db_file_multiblock_read_count的值進行多塊讀操  
  58.     作。對於索引的分支結構只是簡單的獲取,然後掃描所有的葉結點。其結果是導致索引結構沒有訪問,獲取的數據沒有根據索引鍵的順序排序。  
  59.     INDEX FAST FULL SCAN使用multiblock_read,故產生db file scattered reads 事件。  
  60.           
  61. --對於上面的情形能否使用索引全掃描方式來實現呢?答案是肯定的,需要增加一個提示  
  62. scott@CNMMBO> select /*+ index(t i_t_object_id) */ object_id from t;  
  63.   
  64. Execution Plan  
  65. ----------------------------------------------------------  
  66. Plan hash value: 431110666  
  67.   
  68. ----------------------------------------------------------------------------------  
  69. | Id  | Operation        | Name          | Rows  | Bytes | Cost (%CPU)| Time     |  
  70. ----------------------------------------------------------------------------------  
  71. |   0 | SELECT STATEMENT |               | 50422 |   246K|   113   (1)| 00:00:02 |  
  72. |   1 |  INDEX FULL SCAN | I_T_OBJECT_ID | 50422 |   246K|   113   (1)| 00:00:02 |  
  73. ----------------------------------------------------------------------------------  
  74.   
  75. INDEX FULL SCAN  
  76.        與INDEX FAST FULL SCAN所不同的是,INDEX FULL SCAN會完全按照索引存儲的順序依次訪問整個索引樹。當訪問到葉結點之後,按照雙向  
  77.     鏈表方式讀取相連節點的值。換言之,對於索引上所有的數據是按照有序的方式來讀取的。如果索引塊沒有在高速緩存中被找到時,則需要從數  
  78.     據文件中單塊進行讀取。對於需要讀取大量數據的全索引掃描而言,這將使其變得低效。INDEX FULL SCAN使用single read,故產生  
  79.     db file sequential reads事件。新版的Oracle支持db file parallel reads方式。   

三、INDEX FULL SCAN 與 INDEX FAST FULL SCAN 兩者的差異

  1. --還是使用上面的查詢,我們爲原來的語句增加order by子句  
  2. scott@CNMMBO> set autot trace ;  
  3. scott@CNMMBO> select object_id from t order by object_id ;  
  4.   
  5. 50422 rows selected.  
  6.   
  7. Execution Plan  
  8. ----------------------------------------------------------  
  9. Plan hash value: 431110666  
  10.   
  11. ----------------------------------------------------------------------------------  
  12. | Id  | Operation        | Name          | Rows  | Bytes | Cost (%CPU)| Time     |  
  13. ----------------------------------------------------------------------------------  
  14. |   0 | SELECT STATEMENT |               | 50422 |   246K|   113   (1)| 00:00:02 |  
  15. |   1 |  INDEX FULL SCAN | I_T_OBJECT_ID | 50422 |   246K|   113   (1)| 00:00:02 |  
  16. ----------------------------------------------------------------------------------  
  17.   
  18. Statistics  
  19. ----------------------------------------------------------  
  20.           1  recursive calls  
  21.           0  db block gets  
  22.        3467  consistent gets                     
  23.           0  physical reads  
  24.           0  redo size  
  25.      918087  bytes sent via SQL*Net to client  
  26.       37463  bytes received via SQL*Net from client  
  27.        3363  SQL*Net roundtrips to/from client  
  28.           0  sorts (memory)  
  29.           0  sorts (disk)  
  30.       50422  rows processed  
  31.         
  32. --當增加order by子句之後,我們發現此時未添加任何提示的情形下,CBO優化器選擇了INDEX FULL SCAN方式進行掃描        
  33. --觀察上面的統計信息可知,consistent gets 爲3467,sorts (memory)爲0  
  34.   
  35. --下面通過提示來使得走INDEX FAST FULL SCAN掃描方式  
  36. scott@CNMMBO> select /*+ index_ffs(t i_t_object_id) */ object_id from t order by object_id;  
  37.   
  38. 50422 rows selected.  
  39.   
  40. Execution Plan  
  41. ----------------------------------------------------------  
  42. Plan hash value: 2527678987  
  43.   
  44. -----------------------------------------------------------------------------------------------  
  45. | Id  | Operation             | Name          | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |  
  46. -----------------------------------------------------------------------------------------------  
  47. |   0 | SELECT STATEMENT      |               | 50422 |   246K|       |   185   (3)| 00:00:03 |  
  48. |   1 |  SORT ORDER BY        |               | 50422 |   246K|  1208K|   185   (3)| 00:00:03 |  
  49. |   2 |   INDEX FAST FULL SCAN| I_T_OBJECT_ID | 50422 |   246K|       |    26   (0)| 00:00:01 |  
  50. -----------------------------------------------------------------------------------------------  
  51.   
  52. Statistics  
  53. ----------------------------------------------------------  
  54.           1  recursive calls  
  55.           0  db block gets  
  56.         118  consistent gets  
  57.           0  physical reads  
  58.           0  redo size  
  59.      918087  bytes sent via SQL*Net to client  
  60.       37463  bytes received via SQL*Net from client  
  61.        3363  SQL*Net roundtrips to/from client  
  62.           1  sorts (memory)  
  63.           0  sorts (disk)  
  64.       50422  rows processed  
  65.   
  66. --執行計劃中提示得以生效,即按照INDEX FAST FULL SCAN方式掃描  
  67. --注意執行計劃中的第2步爲SORT ORDER BY操作,而對於INDEX FULL SCAN操作則沒有這樣一部。此執行計劃中多出一列TempSpc,值爲1208K  
  68. --此時的consistent gets爲118,較3467呈數量級下降,其次可以看到sorts (memory)的值爲1,而上一步的sorts (memory)的值爲0。  
  69.   
  70. --下面使用提示使其按全表掃描方式來觀察其統計信息  
  71. scott@CNMMBO> select /*+ full(t) */ object_id from t order by object_id;  
  72.   
  73. 50422 rows selected.  
  74.   
  75. Execution Plan  
  76. ----------------------------------------------------------  
  77. Plan hash value: 961378228  
  78.   
  79. -----------------------------------------------------------------------------------  
  80. | Id  | Operation          | Name | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |  
  81. -----------------------------------------------------------------------------------  
  82. |   0 | SELECT STATEMENT   |      | 50422 |   246K|       |   324   (2)| 00:00:04 |  
  83. |   1 |  SORT ORDER BY     |      | 50422 |   246K|  1208K|   324   (2)| 00:00:04 |  
  84. |   2 |   TABLE ACCESS FULL| T    | 50422 |   246K|       |   166   (1)| 00:00:02 |  
  85. -----------------------------------------------------------------------------------  
  86.   
  87. Statistics  
  88. ----------------------------------------------------------  
  89.           1  recursive calls  
  90.           0  db block gets  
  91.         726  consistent gets  
  92.           0  physical reads  
  93.           0  redo size  
  94.      918087  bytes sent via SQL*Net to client  
  95.       37463  bytes received via SQL*Net from client  
  96.        3363  SQL*Net roundtrips to/from client  
  97.           1  sorts (memory)  
  98.           0  sorts (disk)  
  99.       50422  rows processed  
  100.   
  101. --執行計劃中按全表方式讀取數據  
  102. --全表掃描方式等同於第二步的INDEX FAST FULL SCAN,在執行計劃中的第二步多出了SORT ORDER BY操作。以及列TempSpc,值爲1208K  
  103. --此次全表掃描的consistent gets爲726,高於INDEX FAST FULL SCAN的consistent gets,低於INDEX FULL SCAN的consistent gets  
  104.   
  105. --下面是使用降序的情形  
  106. scott@CNMMBO> set autot trace exp;  
  107. scott@CNMMBO> select object_id from t order by 1 desc;  
  108.   
  109. Execution Plan  
  110. ----------------------------------------------------------  
  111. Plan hash value: 2808014233  
  112.   
  113. --------------------------------------------------------------------------------------------  
  114. | Id  | Operation                  | Name          | Rows  | Bytes | Cost (%CPU)| Time     |  
  115. --------------------------------------------------------------------------------------------  
  116. |   0 | SELECT STATEMENT           |               | 50422 |   246K|   113   (1)| 00:00:02 |  
  117. |   1 |  INDEX FULL SCAN DESCENDING| I_T_OBJECT_ID | 50422 |   246K|   113   (1)| 00:00:02 |  
  118. --------------------------------------------------------------------------------------------  
  119.   
  120. --從上面的執行計劃中可以看出,只要是涉及到排序操作,Oracle會毫不猶豫地選擇INDEX FULL SCAN,因爲INDEX FULL SCAN方式掃描一定是  
  121. --按創建索引是的方式來排序的。當order by使用降序時,可以看到操作1種相應的爲降序操作INDEX FULL SCAN DESCENDING  

四、聚合操作count(*)時的INDEX FULL SCAN 與 INDEX FAST FULL SCAN

  1. scott@CNMMBO> set autot trace exp;  
  2. scott@CNMMBO> select count(*) from t;  
  3.   
  4. Execution Plan  
  5. ----------------------------------------------------------  
  6. Plan hash value: 3095383276  
  7.   
  8. -------------------------------------------------------------------------------  
  9. | Id  | Operation             | Name          | Rows  | Cost (%CPU)| Time     |  
  10. -------------------------------------------------------------------------------  
  11. |   0 | SELECT STATEMENT      |               |     1 |    26   (0)| 00:00:01 |  
  12. |   1 |  SORT AGGREGATE       |               |     1 |            |          |  
  13. |   2 |   INDEX FAST FULL SCAN| I_T_OBJECT_ID | 50422 |    26   (0)| 00:00:01 |  
  14. -------------------------------------------------------------------------------  
  15.   
  16. scott@CNMMBO> select /*+ index(t i_t_object_id) */ count(*) from t;  
  17.   
  18. Execution Plan  
  19. ----------------------------------------------------------  
  20. Plan hash value: 3079973526  
  21.   
  22. --------------------------------------------------------------------------  
  23. | Id  | Operation        | Name          | Rows  | Cost (%CPU)| Time     |  
  24. --------------------------------------------------------------------------  
  25. |   0 | SELECT STATEMENT |               |     1 |   113   (1)| 00:00:02 |  
  26. |   1 |  SORT AGGREGATE  |               |     1 |            |          |  
  27. |   2 |   INDEX FULL SCAN| I_T_OBJECT_ID | 50422 |   113   (1)| 00:00:02 |  
  28. --------------------------------------------------------------------------  
  29. --> Author : Robinson Cheng  --> Blog : http://blog.csdn.net/robinson_0612  
  30. 使用countcount(*)的注意事項  
  31.     如果是基於可以爲 null 值列進行count,則該查詢優化器會選擇包含該列的任意索引  
  32.     如果是基於not null值列進行count,或count(*),則至少包含一個非null列且最小的索引會被原則,因爲null值不會被B樹索引存儲。  
  33.   
  34. --附尋找INDEX FULL SCAN的sql語句  
  35. SELECT p.sql_id,sql_text  
  36. FROM   v$sqlarea t, v$sql_plan p  
  37. WHERE  t.hash_value = p.hash_value AND p.operation = 'INDEX' AND p.options = 'FULL SCAN'  
  38. and p.object_owner not in('SYS','SYSTEM');  

五、位圖索引時的INDEX FULL SCAN 與 INDEX FAST FULL SCAN  
    在使用位圖索引時,index full SCAN 與 index fast full與使用B樹索引有相同的表現。需要注意的是位圖索引存儲null值,在很大程度
上對索引進行壓縮。其次位圖索引不支持降序掃描方式。此處不再演示位圖索引的index full scan 與 index fast full。

 

六、總結
   1、當select和where中出現的列都存在索引是發生index full scan與index fast full scan的前提
   2、查詢返回的數據行總數佔據整個索引10%以上的比率
   3、基於上述前提count(*)操作幾乎總是選擇index fast full scan,而索引列上的order by子句幾乎總是選擇index full scan
   4、index fast full scan使用多塊讀的方式讀取索引塊,產生db file scattered reads 事件,讀取時高效,但爲無序讀取
   5、index full scan使用單塊讀方式有序讀取索引塊,產生db file sequential reads事件,當採用該方式讀取大量索引全掃描,效率低下
   6、絕大多數情況下,index fast full scan性能優於index full scan,但前者在有order by時,一定會存在對讀取的塊重新排序的過程 
   7、index fast full scan通過犧牲內存與臨時表空間換取性能,因此在內存不足或飽和狀態應進行合理權衡

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章