生产情景:
大的表,如日志表,每天都有大批量的数据插入,假设上面有id,timed字段上建有索引,id的选择率很高;
生产营运期间经常会报执行计划变化导致的问题;
模拟下场景:
SQL> create table aiki_t1 as select * from dba_objects where object_id is not null; Table created. SQL> create index idx_aiki_t1_id on aiki_t1(object_id); Index created. SQL> create index idx_aiki_t1_time on aiki_t1(created); Index created. SQL> exec dbms_stats.gather_table_stats('AIKI','AIKI_T1',CASCADE=>TRUE,ESTIMATE_PERCENT=>100,METHOD_OPT=>'FOR ALL COLUMNS SIZE 1'); PL/SQL procedure successfully completed. SQL> SELECT LOW_VALUE,HIGH_VALUE from dba_tab_col_statistics where owner='AIKI' and table_name='AIKI_T1' AND COLUMN_NAME='CREATED'; LOW_VALUE HIGH_VALUE ---------------------------------------------------------------- ---------------------------------------------------------------- 78710C030B3C23 7872030A12071F 1 row selected. --查询字典中记录的最大最小值,读取RAW类型的数值 declare v1 raw(32):='7872030A12071F'; v2 date; begin dbms_stats.convert_raw_value(v1,v2); dbms_output.put_line(to_char(v2,'yyyy mmdd hh24:mi')); end; SQL> declare 2 v1 raw(32):='78710C030B3C23'; 3 v2 date; 4 begin 5 dbms_stats.convert_raw_value(v1,v2); 6 dbms_output.put_line(to_char(v2,'yyyy mmdd hh24:mi')); 7 end; 8 / 2013 1203 10:59 PL/SQL procedure successfully completed. SQL> declare 2 v1 raw(32):='7872030A12071F'; 3 v2 date; 4 begin 5 dbms_stats.convert_raw_value(v1,v2); 6 dbms_output.put_line(to_char(v2,'yyyy mmdd hh24:mi')); 7 end; 8 / 2014 0310 17:06 PL/SQL procedure successfully completed. --创建一临时表模拟数据量在统计后的大批量插入 SQL> create table zbk as select * from dba_objects where object_id is not null; Table created. --更改列值 SQL> alter session force parallel dml; Session altered. SQL> update zbk set created='2014 0320'; 106429 rows updated. SQL> commit; Commit complete. SQL> l 1* commit SQL> select distinct created from zbk; CREATED -------- 20140320 1 row selected. --数据量统计分析后大批插入 SQL> insert into aiki_t1 select * from zbk; 106429 rows created. SQL> commit; Commit complete. --数据量在最大值和最小值范围情况,预估行为56589与实际有出入,但差别明显比超出范围的要小 SQL> set autot traceonly SQL> SQL> select object_type from aiki_t1 where created>to_date('20140118','yyyymmdd'); 107207 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 335395965 ----------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 56589 | 994K| 436 (1)| 00:00:06 | |* 1 | TABLE ACCESS FULL| AIKI_T1 | 56589 | 994K| 436 (1)| 00:00:06 | ----------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("CREATED">TO_DATE(' 2014-01-18 00:00:00', 'syyyy-mm-dd hh24:mi:ss')) Statistics ---------------------------------------------------------- 1 recursive calls 1 db block gets 10597 consistent gets 1561 physical reads 168 redo size 2330729 bytes sent via SQL*Net to client 79137 bytes received via SQL*Net from client 7149 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 107207 rows processed --列值在最大最小值范围外,预估值44行与实际的结果相差甚远,且成本只有3 SQL> select object_type from aiki_t1 where created>to_date('20140318','yyyymmdd'); 106429 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 2814400874 ------------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 44 | 792 | 3 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| AIKI_T1 | 44 | 792 | 3 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_AIKI_T1_TIME | 44 | | 2 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("CREATED">TO_DATE(' 2014-03-18 00:00:00', 'syyyy-mm-dd hh24:mi:ss')) Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 17018 consistent gets 0 physical reads 39084 redo size 2311172 bytes sent via SQL*Net to client 78565 bytes received via SQL*Net from client 7097 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 106429 rows processed --没有重复值的索引列的成本也要2 SQL> select object_type from aiki_t1 where created>to_date('20140318','yyyymmdd') and object_id=88998899; no rows selected Execution Plan ---------------------------------------------------------- Plan hash value: 2361744790 ---------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 23 | 2 (0)| 00:00:01 | |* 1 | TABLE ACCESS BY INDEX ROWID| AIKI_T1 | 1 | 23 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_AIKI_T1_ID | 1 | | 1 (0)| 00:00:01 | ---------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("CREATED">TO_DATE(' 2014-03-18 00:00:00', 'syyyy-mm-dd hh24:mi:ss')) 2 - access("OBJECT_ID"=88998899) Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 7 consistent gets 0 physical reads 156 redo size 339 bytes sent via SQL*Net to client 509 bytes received via SQL*Net from client 1 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 0 rows processed --方法 1.后台绑定sql profile或spm 2.程序加HINT解决