複合索引

文章出處:http://space.itpub.net/17203031/viewspace-692364  感謝作者的分享

 

索引是我們經常選擇的數據表檢索優化方案之一。其中,複合索引是我們經常選擇的策略。那麼,構建索引列的順序上,有何種差異和需要注意的方面呢?下面我們通過實驗來進行說明。

實驗環境說明

準備數據表和實驗環境。索引列的差異,主要體現在選擇性上,我們通過構建不同選擇性的列來進行試驗。

SQL> conn scott/tiger@orcl;

Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0

Connected as scott

SQL> create table t as select owner, object_name from dba_objects;

Table created

SQL> select count(distinct owner), count(distinct object_name) from t;

COUNT(DISTINCTOWNER) COUNT(DISTINCTOBJECT_NAME)

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

                 30                     30716

可以看出,在數據表T上不同列具有很大的選擇性差異。

構建方案1——低選擇性列爲前導列

首先我們選擇低選擇性列owner作爲索引列的前導列。

SQL> create index idx_t_cmp1 on t(owner,object_name);

Index created

SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);

PL/SQL procedure successfully completed

首先來觀察一下索引物理體積問題。

SQL> col segment_name for a15;

SQL> select segment_name, bytes, blocks, extents from user_segments where segment_name='IDX_T_CMP1';

SEGMENT_NAME        BYTES    BLOCKS   EXTENTS

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

IDX_T_CMP1        3145728       384        18

佔有空間上爲384個Oracle塊,分佈在18個分區上。

搜索場景執行計劃研究。

場景1:where條件中包括所有索引列;

SQL> explain plan for select * from t where wner='SCOTT' and object_name='T';

Explained

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT

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

Plan hash value: 1474811917

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

| Id | Operation       | Name      | Rows | Bytes | Cost (%CPU)| Time    |

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

|  0 | SELECT STATEMENT |           |    1 |   29 |    1  (0)| 00:00:01 |

|*1 | INDEXRANGESCAN| IDX_T_CMP1 |    1 |   29 |    1  (0)| 00:00:01 |

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

Predicate Information (identified by operation id):

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

  1 - access("OWNER"='SCOTT' AND "OBJECT_NAME"='T')

13 rows selected

當所有列均出現在where條件中時,Oracle選擇的執行計劃中進行“INDEX RANGE SCAN”操作。Oracle索引結構中,葉節點排列的就是索引列排序的結果。進行的“INDEX RANGE SCAN”操作,就是首先根據條件,從根root節點位置向下定位,經過分支節點之後,定位到第一個符合條件索引列鍵值的葉節點。之後順序掃描葉子節點,獲取到符合where條件(或者部分where符合條件)的數據表列rowid值。

Index Range Scan操作是Oracle進行索引操作最常見的形式。

場景2:where中包括低選擇性列

SQL> explain plan for select * from t where wner='SCOTT';

Explained

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT

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

Plan hash value: 1474811917

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

| Id | Operation       | Name      | Rows | Bytes | Cost (%CPU)| Time    |

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

|  0 | SELECT STATEMENT |           | 1901 | 55129 |   12  (0)| 00:00:01 |

|* 1 |INDEXRANGESCAN| IDX_T_CMP1 | 1901 | 55129 |   12  (0)| 00:00:01 |

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

Predicate Information (identified by operation id):

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

  1 - access("OWNER"='SCOTT')

13 rows selected

當條件中只有低選擇性列的時候,Oracle同樣可以通過INDEX RANGE SCAN來獲取rowid值。雖然並不能完全發揮出索引的全部列優勢,但是Oracle通過Cost試算,通常可以判斷出只掃描部分索引樹,也是能帶來較好的搜索性能的。

場景2:where條件中帶高選擇性列

SQL> explain plan for select * from t where object_name='T';

Explained

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT

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

Plan hash value: 3522166362

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

| Id | Operation       | Name      | Rows | Bytes | Cost (%CPU)| Time    |

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

|  0 | SELECT STATEMENT |           |    2 |   58 |   28  (0)| 00:00:01 |

|* 1 |INDEX SKIP SCAN| IDX_T_CMP1 |    2 |   58 |   28  (0)| 00:00:01 |

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

Predicate Information (identified by operation id):

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

  1 - access("OBJECT_NAME"='T')

      filter("OBJECT_NAME"='T')

14 rows selected

此處,where條件中沒有出現索引前導列owner,而是出現了選擇性較強的object_name列。此時,我們發現Oracle選擇利用索引進行了“INDEX SKIP SCAN”操作。首先,我們從CBO的角度看,進行該操作所消耗的成本必然要比進行FTS(全表掃描)的成本要低。

INDEX SKIP SCAN是Oracle 9i中引入的一種執行計劃操作。故名思意,就是對索引葉節點進行“跳躍”式的搜索。在這個問題上,網絡中一些資料認爲:

Oracle中的複合索引順序不同,對索引構建結構上有很大的影響。首先,Oracle依據前導列的取值將索引樹劃分爲多個子索引結構。如果前導列取值較多,也就意味着子樹多。在進行帶前導列搜索時,Oracle首先依據前導列確定子索引樹,之後進行各種的Index Range Scan。此時的Range Scan是進行索引葉子節點的掃描。

無論這種理解是否正確,有一點可以肯定。當where條件中不包括前導列的時候,對葉子節點進行Range Scan應該是不可以的。因爲Range Scan保證的順序是前導列+後導列的順序。Skip Scan應該進行的是在葉子節點上,根據不同的前導列形成子索引樹,葉節點分別進行Scan操作。

筆者以爲:skip scan是Oracle針對特定條件上索引結構,所提供的一種備選搜索操作。Skip scan的使用不是規則,而是成本估算。Index Skip Scan是Oracle提供的一種執行計劃操作,可以應用在執行計劃的生成中。簡單的說,就是Oracle將SQL描述語句轉化爲可執行操作序列(執行計劃)過程中一個操作選擇。

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