索引列选择错误造成的性能恶化情景

生产情景:

大的表,如日志表,每天都有大批量的数据插入,假设上面有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解决


你可能感兴趣的:(索引列选择错误造成的性能恶化情景)