oracle 優化之組合索引
組合索引適用場景:
1.適用在單獨查詢返回記錄很多,組合查詢後忽然返回記錄很少的情況:
比如where 學歷=碩士以上 返回不少的記錄
比如where 職業=收銀員 同樣返回不少的記錄
於是無論哪個條件查詢做索引,都不合適。
可是,如果學歷為碩士以上,同時職業又是收銀員的,返回的就少之又少了。
於是聯合索引就可以這麼開始建了。
2.組合查詢的組合順序,要考慮單獨的字首查詢情況(否則單獨字首查詢的索引不能生效或者只能用到跳躍索引)
比如你在建id,object_type的聯合索引時,要看考慮是單獨where id=xxx查詢的多,還是單獨where object_type查詢的多。
這裡細節就暫時略去了,在案例的部分中還有描述
3.僅等值無範圍查詢時,組合索引順序不影響效能(比如where col1=xxx and col2=xxx,無論COL1+COL2組合還是COL2+COL1組合)
drop table t purge;
create table t as select * from dba_objects;
insert into t select * from t;
insert into t select * from t;
insert into t select * from t;
update t set object_id=rownum ;
commit;
create index idx_id_type on t(object_id,object_type);
create index idx_type_id on t(object_type,object_id);
set autotrace off
alter session set statistics_level=all ;
set linesize 366
create index idx_id_type on t(object_id,object_type);
create index idx_type_id on t(object_type,object_id);
set autotrace off
alter session set statistics_level=all ;
set linesize 200
select /*+index(t,idx_id_type)*/ * from t where object_id=20 and object_type='TABLE';
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 7qydm6x641kx4, child number 1
-------------------------------------
select /*+index(t,idx_id_type)*/ * from t where object_id=20 and
object_type='TABLE'
Plan hash value: 1470938839
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 5 |
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 1 | 1 |00:00:00.01 | 5 |
|* 2 | INDEX RANGE SCAN | IDX_ID_TYPE | 1 | 1 | 1 |00:00:00.01 | 4 |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_ID"=20 AND "OBJECT_TYPE"='TABLE')
Note
-----
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- cardinality feedback used for this statement
24 rows selected.
可以看出走的是索引範圍掃描,並且回表。
--4.組合索引最佳順序一般是將列等值查詢的列置前。
(測試組合索引在條件是不等的情況下的情況,條件經常是不等的,要放在後面,讓等值的在前面)
Execution Plan
----------------------------------------------------------
Plan hash value: 1470938839
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 16 | 3312 | 52 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 16 | 3312 | 52 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_ID_TYPE | 50 | | 51 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_ID">=20 AND "OBJECT_TYPE"='TABLE' AND "OBJECT_ID"<2000)
filter("OBJECT_TYPE"='TABLE')
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
89 consistent gets
0 physical reads
0 redo size
51596 bytes sent via SQL*Net to client
875 bytes received via SQL*Net from client
34 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
493 rows processed
通過索引快速掃面獲得行ID進行回表。
5.注意組合索引與組合條件中關於IN 的優化
案例1
UPDATE t SET OBJECT_ID=20 WHERE ROWNUM<=26000;
UPDATE t SET OBJECT_ID=21 WHERE OBJECT_ID<>20;
COMMIT;
set linesize 1000
set pagesize 1
alter session set statistics_level=all ;
select /*+index(t,idx1_object_id)*/ * from t where object_TYPE='TABLE' AND OBJECT_ID >= 20 AND OBJECT_ID<= 21;
Execution Plan
----------------------------------------------------------
Plan hash value: 3420768628
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 20755 | 4195K| 1186 (1)| 00:00:15 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 20755 | 4195K| 1186 (1)| 00:00:15 |
|* 2 | INDEX RANGE SCAN | IDX_TYPE_ID | 20755 | | 218 (0)| 00:00:03 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_TYPE"='TABLE' AND "OBJECT_ID">=20 AND "OBJECT_ID"<=21)
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
5059 consistent gets
0 physical reads
0 redo size
1191984 bytes sent via SQL*Net to client
18464 bytes received via SQL*Net from client
1633 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
24472 rows processed
select /*+index(t,idx1_object_id)*/ * from t where object_TYPE='TABLE' AND OBJECT_ID in (20,21);
以上兩個語句:
select /*+index(t,idx1_object_id)*/ * from t where object_TYPE='TABLE' AND OBJECT_ID >= 20 AND OBJECT_ID<= 21; --CPU cost消耗1186
select /*+index(t,idx1_object_id)*/ * from t where object_TYPE='TABLE' AND OBJECT_ID in (20,21); -- CPU cost消耗僅僅為5
為何消耗的CPU成本差距這麼大?
在人為的思考中,我們人為認為 (OBJECT_ID >= 20 AND OBJECT_ID<= 21) = in (20,21),而其實oracle 不這麼認為in (20,21) 只有兩個值,而(OBJECT_ID >= 20 AND OBJECT_ID<= 21)走索引中間有無窮個鍵值。所以第二句消耗的CPU COST僅僅為5。
6.依然是關於IN的優化 (col1,col2,col3的索引情況,如果沒有為COL2賦予查詢條件時,COL3只能起到檢驗作用)
drop table t purge;
create table t as select * from dba_objects;
UPDATE t SET OBJECT_ID=20 WHERE ROWNUM<=26000;
UPDATE t SET OBJECT_ID=21 WHERE OBJECT_ID<>20;
Update t set object_id=22 where rownum<=10000;
COMMIT;
create index idx_union on t(object_type,object_id,owner);
set autotrace traceonly
select * from t where object_type='VIEW' and OWNER='SYS';
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
Execution Plan
----------------------------------------------------------
Plan hash value: 1570829420
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3369 | 681K| 20 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 3369 | 681K| 20 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_UNION | 14 | | 19 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_TYPE"='VIEW' AND "OWNER"='SYS')
filter("OWNER"='SYS')
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
686 consistent gets
0 physical reads
0 redo size
157650 bytes sent via SQL*Net to client
3405 bytes received via SQL*Net from client
264 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
3938 rows processed
SQL>
23 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3713220770
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 8168 | 16336 | 29 (0)| 00:00:01 |
| 1 | COLLECTION ITERATOR PICKLER FETCH| DISPLAY_CURSOR | 8168 | 16336 | 29 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
27 recursive calls
0 db block gets
136 consistent gets
0 physical reads
0 redo size
1925 bytes sent via SQL*Net to client
534 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
23 rows processed
select /*+INDEX(T,idx_union)*/ * from t T where object_type='VIEW' and OBJECT_ID IN (20,21,22) AND OWNER='SYS';
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
Execution Plan
----------------------------------------------------------
Plan hash value: 306189815
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3369 | 681K| 6 (0)| 00:00:01 |
| 1 | INLIST ITERATOR | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| T | 3369 | 681K| 6 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_UNION | 1 | | 5 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("OBJECT_TYPE"='VIEW' AND ("OBJECT_ID"=20 OR "OBJECT_ID"=21 OR
"OBJECT_ID"=22) AND "OWNER"='SYS')
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
687 consistent gets
0 physical reads
0 redo size
157650 bytes sent via SQL*Net to client
3405 bytes received via SQL*Net from client
264 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
3938 rows processed
SQL>
23 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3713220770
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 8168 | 16336 | 29 (0)| 00:00:01 |
| 1 | COLLECTION ITERATOR PICKLER FETCH| DISPLAY_CURSOR | 8168 | 16336 | 29 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
15 recursive calls
0 db block gets
0 consistent gets
0 physical reads
0 redo size
1862 bytes sent via SQL*Net to client
534 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
23 rows processed
可以看出,select * from t where object_type='VIEW' and OWNER='SYS'; 這一條語句跟select /*+INDEX(T,idx_union)*/ * from t T where object_type='VIEW' and OBJECT_ID IN (20,21,22) AND OWNER='SYS';這一條語句代價等價,因為此案例中object_type='VIEW' 且OBJECT_ID 只有20,21,22 這三條記錄,所以如果沒有為COL2賦予查詢條件時,COL3只能起到檢驗作用。