在對20億記錄大表的查詢中,發現Oracle的執行計劃選擇並不穩定,固然這是CBO的正常行爲,然而當選擇不一樣時,結果是巨大的。
在如下查詢中,使用指定的索引,查詢快速得出結果,可是這依賴於Hints的強制指定:html
SQL> select /*+ index(smsmg IDX_smsmg_DEST_MDN) */ count(*) 2 from smsmg where msg_to_dest_mdn='861318888888' and service_id='54'; COUNT(*) ---------- 1 Elapsed: 00:00:00.00 Execution Plan ---------------------------------------------------------- Plan hash value: 1659057974 -------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | -------------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 18 | 98 (0)| 00:00:02 | | | | 1 | SORT AGGREGATE | | 1 | 18 | | | | | |* 2 | TABLE ACCESS BY GLOBAL INDEX ROWID| smsmg | 1 | 18 | 98 (0)| 00:00:02 | ROWID | ROWID | |* 3 | INDEX RANGE SCAN | IDX_smsmg_msg_to_des_mdn | 106 | | 4 (0)| 00:00:01 | | | -------------------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("SERVICE_ID"='54') 3 - access("msg_to_dest_mdn"='861318888888') Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 82 consistent gets 0 physical reads 0 redo size 515 bytes sent via SQL*Net to client 469 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
而若是不加Hints,查詢是一時沒法得出結果的:ide
SQL> select count(*) from smsmg where msg_to_dest_mdn='861318888888' and service_id='54'; select count(*) from smsmg where msg_to_dest_mdn='861318888888' and service_id='54' * ERROR at line 1: ORA-01013: user requested cancel of current operation Elapsed: 00:04:27.88
其執行計劃顯示,這一缺省的執行方式致使了錯誤的索引選擇:code
SQL> set autotrace trace explain SQL> select count(*) from smsmg where msg_to_dest_mdn='861318888888' and service_id='54'; Elapsed: 00:00:00.00 Execution Plan ---------------------------------------------------------- Plan hash value: 1152948967 ---------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | ---------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 18 | 5 (0)| 00:00:01 | | | | 1 | SORT AGGREGATE | | 1 | 18 | | | | | |* 2 | TABLE ACCESS BY GLOBAL INDEX ROWID| smsmg | 1 | 18 | 5 (0)| 00:00:01 | ROWID | ROWID | |* 3 | INDEX RANGE SCAN | IDX_smsmg_SERVICE_ID | 1 | | 4 (0)| 00:00:01 | | | ---------------------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("msg_to_dest_mdn"='861318888888') 3 - access("SERVICE_ID"='54')
說CBO是Oracle最爲博大精深的技術一點也不爲過,只是這技術越複雜越深奧出錯的機會就越多了。
這個表的數據量大約是500G:orm
SQL> select segment_name,partition_name,bytes/1024/1024/1024 GB,blocks from dba_segments 2 where owner='SMSMSG' and segment_name='SMSSENDMSG'; SEGMENT_NAME PARTITION_NAME GB BLOCKS ------------------------------ --------------- ---------- ---------- SMSSENDMSG M01 30.625 4014080 SMSSENDMSG M02 29.875 3915776 SMSSENDMSG M03 43.25 5668864 SMSSENDMSG M04 38 4980736 SMSSENDMSG M05 43.1875 5660672 SMSSENDMSG M06 50.6875 6643712 SMSSENDMSG M08 55.4375 7266304 SMSSENDMSG M09 32.125 4210688 SMSSENDMSG M10 23.9375 3137536 SMSSENDMSG M11 25.6875 3366912 SMSSENDMSG M12 31.9375 4186112 SEGMENT_NAME PARTITION_NAME GB BLOCKS ------------------------------ --------------- ---------- ---------- SMSSENDMSG M13 .000061035 8 SMSSENDMSG M07 58.5 7667712 13 rows selected. SQL> select sum(bytes)/1024/1024/1024 GB from dba_segments where owner='SMSMSG' and segment_name='SMSSENDMSG'; GB ---------- 463.250061
每一個分區的記錄數量大約是1~2億條:htm
SQL> select table_owner,table_name,partition_name,num_rows from dba_tab_partitions 2 where table_owner='SMS9885' and table_name='SMS_TO_ISMG'; TABLE_OWNER TABLE_NAME PARTITION_NAME NUM_ROWS ------------------------------ ------------------------------ --------------- ---------- SMSMSG SMSSENDMSG M01 135605804 SMSMSG SMSSENDMSG M02 134599287 SMSMSG SMSSENDMSG M03 187959758 SMSMSG SMSSENDMSG M04 169663942 SMSMSG SMSSENDMSG M05 187435468 SMSMSG SMSSENDMSG M06 222079762 SMSMSG SMSSENDMSG M07 256482704 SMSMSG SMSSENDMSG M08 229089535 SMSMSG SMSSENDMSG M09 122453724 SMSMSG SMSSENDMSG M10 104093080 SMSMSG SMSSENDMSG M11 116095184 TABLE_OWNER TABLE_NAME PARTITION_NAME NUM_ROWS ------------------------------ ------------------------------ --------------- ---------- SMSMSG SMSSENDMSG M12 143216009 SMSMSG SMSSENDMSG M13 0 13 rows selected.
前面使用的都是單鍵值索引,其索引選擇性參考前幾天的記錄。
索引