在对20亿记录大表的查询中,发现Oracle的执行计划选择并不稳定,当然这是CBO的正常行为,然而当选择不同时,结果是巨大的。


在以下查询中,使用指定的索引,查询快速得出结果,但是这依赖于Hints的强制指定:



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,查询是一时无法得出结果的:



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


其执行计划显示,这一缺省的执行方式导致了错误的索引选择:



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:



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亿条:



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.




前面使用的都是单键值索引,其索引选择性参考前几天的记录。



-The End-


oracle视频教程请关注:http://u.youku.com/user_video/id_UMzAzMjkxMjE2.html