2017-02-09 9 views
5

У нас есть довольно простая инструкция выбора, обращающаяся к данным с уникальными полями индекса. Тем не менее, оптимизатор решает использовать плохой индекс, а выбор занимает около 4 секунд вместо 0.0x секунд.Оптимизатор использует неверный индекс

Эта проблема не существует с оракулом 11g, но с оракулом 12c.

Данные статистики в актуальном состоянии.

Похоже, оценка плохого плана неверна (см. Ниже), как мы можем избежать этого?

Я думаю, добавив статистику поля или исходные записи, но я надеюсь, что есть другое решение.

Заранее спасибо.

Определение таблицы

create table PS_CS_AKT_PROD_TB(business_unit VARCHAR2(5) not null, 
           ra_cmpgn_wave_id VARCHAR2(15) not null, 
           product_id VARCHAR2(18) not null, 
           cs_aboart_cd VARCHAR2(20) not null, 
           cs_einweis_id VARCHAR2(20) not null, 
           row_added_dttm TIMESTAMP(6), 
           row_added_oprid VARCHAR2(30) not null, 
           row_lastmant_dttm TIMESTAMP(6), 
           row_lastmant_oprid VARCHAR2(30) not null, 
           cs_recstat_xl VARCHAR2(4) not null, 
           /* ... further fields ... */ 
           cs_kondition VARCHAR2(20) not null) 
           tablespace CS_APP pctfree 10 initrans 1 
           maxtrans 255 storage(initial 40K next 104K 
           minextents 1 maxextents unlimited); 

Индексы

create unique index PS_CS_AKT_PROD_TB on PS_CS_AKT_PROD_TB(
BUSINESS_UNIT,RA_CMPGN_WAVE_ID,PRODUCT_ID,CS_ABOART_CD) 
tablespace PSINDEX pctfree 10 initrans 2 maxtrans 255 
storage(initial 40K next 104K minextents 1 maxextents unlimited); 

create index PSBCS_AKT_PROD_TB on PS_CS_AKT_PROD_TB(
BUSINESS_UNIT,PRODUCT_ID) 
tablespace PSINDEX pctfree 10 initrans 2 maxtrans 255 
storage(initial 40K next 104K minextents 1 maxextents unlimited); 

Размер таблицы

select count(*) from PS_CS_AKT_PROD_TB; 
--> 6372395 

Оператор Select

Все поля обязательные для заполнения уникального индекса приведены:

SELECT CS_STEUERUNG_XL, CS_EWF2EVT 
    FROM PS_CS_AKT_PROD_TB 
WHERE BUSINESS_UNIT = :1 
    AND RA_CMPGN_WAVE_ID = :2 
    AND PRODUCT_ID = :3 
    AND CS_ABOART_CD = :4; 

Исполнение Детали и Объяснять план

select v.CHILD_NUMBER, 
     elapsed_time/1000000 elapsed_time, 
     executions, 
     round((elapsed_time/decode(executions, 0, 1, executions))/
      1000000, 
      4) elapsed_time_per_exec, 
     disk_reads, 
     buffer_gets, 
     rows_processed, 
     cpu_time 
    from v$sql v 
where v.SQL_ID = 'dqrktmcraprvp'; 

    /* 
    CHILD_NUMBER ELAPSED_TIME EXECUTIONS ELAPSED_TIME_PER_EXEC DISK_READS BUFFER_GETS ROWS_PROCESSED CPU_TIME 
    0    400,874709 100   4,0087    98457  495295  86    5929096 
    1    0,017217  8   0,0022    2   36   4    2108 
    2    0,002038  2   0,001     0   9   1    0 
    */ 

select plan_table_output 
from table(dbms_xplan.display_cursor('dqrktmcraprvp', 0)) t; 

/* 
    SQL_ID dqrktmcraprvp, child number 0 
    ------------------------------------- 
    SELECT CS_STEUERUNG_XL, CS_EWF2EVT FROM PS_CS_AKT_PROD_TB WHERE 
    BUSINESS_UNIT = :1 AND RA_CMPGN_WAVE_ID = :2 AND PRODUCT_ID = :3 AND 
    CS_ABOART_CD = :4 

    Plan hash value: 1118713352 

    --------------------------------------------------------------------------------------------------------- 
    | Id | Operation       | Name    | Rows | Bytes | Cost (%CPU)| Time  | 
    --------------------------------------------------------------------------------------------------------- 
    | 0 | SELECT STATEMENT     |     |  |  |  1 (100)|   | 
    |* 1 | TABLE ACCESS BY INDEX ROWID BATCHED| PS_CS_AKT_PROD_TB |  1 | 46 |  1 (0)| 00:00:01 | 
    |* 2 | INDEX RANGE SCAN     | PSBCS_AKT_PROD_TB |  1 |  |  1 (0)| 00:00:01 | 
    --------------------------------------------------------------------------------------------------------- 

    Predicate Information (identified by operation id): 
    --------------------------------------------------- 

     1 - filter(("RA_CMPGN_WAVE_ID"=:2 AND "CS_ABOART_CD"=:4)) 
     2 - access("BUSINESS_UNIT"=:1 AND "PRODUCT_ID"=:3)  */ 


select plan_table_output 
from table(dbms_xplan.display_cursor('dqrktmcraprvp', 1)) t; 

    /* 

    SQL_ID dqrktmcraprvp, child number 1 
    ------------------------------------- 
    SELECT CS_STEUERUNG_XL, CS_EWF2EVT FROM PS_CS_AKT_PROD_TB WHERE 
    BUSINESS_UNIT = :1 AND RA_CMPGN_WAVE_ID = :2 AND PRODUCT_ID = :3 AND 
    CS_ABOART_CD = :4 

    Plan hash value: 619225732 

    ------------------------------------------------------------------------------------------------- 
    | Id | Operation     | Name    | Rows | Bytes | Cost (%CPU)| Time  | 
    ------------------------------------------------------------------------------------------------- 
    | 0 | SELECT STATEMENT   |     |  |  |  1 (100)|   | 
    | 1 | TABLE ACCESS BY INDEX ROWID| PS_CS_AKT_PROD_TB |  1 | 46 |  1 (0)| 00:00:01 | 
    |* 2 | INDEX UNIQUE SCAN   | PS_CS_AKT_PROD_TB |  1 |  |  1 (0)| 00:00:01 | 
    ------------------------------------------------------------------------------------------------- 

    Predicate Information (identified by operation id): 
    --------------------------------------------------- 

     2 - access("BUSINESS_UNIT"=:1 AND "RA_CMPGN_WAVE_ID"=:2 AND "PRODUCT_ID"=:3 AND 
        "CS_ABOART_CD"=:4) 

    */  

Поправка (см комментарии)

Значения полей PROCUCT_ID неравномерно распределенных

OCCURENCE_OF_PRODUCT_ID TOTAL 
upto 10^1-1    1134 
upto 10^2-1    1607 
upto 10^3-1    1649 
upto 10^4-1    455 
upto 10^5-1    279 

Выход dbms_xplan.display_cursor (NULL, NULL, '+ ОЧЕРК')

ДО 37 секунд:

SQL_ID ga79yhh54r5bu, child number 0 
    ------------------------------------- 
    select a.cs_ewf2evt, a.cs_steuerung_xl from ps_cs_akt_prod_tb a where 
    a.business_unit = :1 and a.ra_cmpgn_wave_id = :2 and a.product_id = :3 
    and a.cs_aboart_cd = :4 

    Plan hash value: 1118713352 

    --------------------------------------------------------------------------------------------------------- 
    | Id | Operation       | Name    | Rows | Bytes | Cost (%CPU)| Time  | 
    --------------------------------------------------------------------------------------------------------- 
    | 0 | SELECT STATEMENT     |     |  |  |  1 (100)|   | 
    |* 1 | TABLE ACCESS BY INDEX ROWID BATCHED| PS_CS_AKT_PROD_TB |  1 | 46 |  1 (0)| 00:00:01 | 
    |* 2 | INDEX RANGE SCAN     | PSBCS_AKT_PROD_TB |  1 |  |  1 (0)| 00:00:01 | 
    --------------------------------------------------------------------------------------------------------- 

    Outline Data 
    ------------- 

     /*+ 
      BEGIN_OUTLINE_DATA 
      IGNORE_OPTIM_EMBEDDED_HINTS 
      OPTIMIZER_FEATURES_ENABLE('12.1.0.2') 
      DB_VERSION('12.1.0.2') 
      OPT_PARAM('_optimizer_max_permutations' 50) 
      OPT_PARAM('_unnest_subquery' 'false') 
      OPT_PARAM('optimizer_dynamic_sampling' 4) 
      OPT_PARAM('_gby_hash_aggregation_enabled' 'false') 
      OPT_PARAM('_optimizer_use_feedback' 'false') 
      OPT_PARAM('_px_adaptive_dist_method' 'off') 
      OPT_PARAM('_optimizer_dsdir_usage_control' 0) 
      OPT_PARAM('_optimizer_adaptive_plans' 'false') 
      OPT_PARAM('_optimizer_strans_adaptive_pruning' 'false') 
      OPT_PARAM('_optimizer_gather_feedback' 'false') 
      OPT_PARAM('_optimizer_nlj_hj_adaptive_join' 'false') 
      OPT_PARAM('optimizer_index_cost_adj' 20) 
      ALL_ROWS 
      OUTLINE_LEAF(@"SEL$1") 
      INDEX_RS_ASC(@"SEL$1" "A"@"SEL$1" ("PS_CS_AKT_PROD_TB"."BUSINESS_UNIT" 
        "PS_CS_AKT_PROD_TB"."PRODUCT_ID")) 
      BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$1" "A"@"SEL$1") 
      END_OUTLINE_DATA 
     */ 

    Predicate Information (identified by operation id): 
    --------------------------------------------------- 

     1 - filter(("A"."RA_CMPGN_WAVE_ID"=:2 AND "A"."CS_ABOART_CD"=:4)) 
     2 - access("A"."BUSINESS_UNIT"=:1 AND "A"."PRODUCT_ID"=:3) 

~ 0.06 секунды

SQL_ID ga79yhh54r5bu, child number 0 
    ------------------------------------- 
    select a.cs_ewf2evt, a.cs_steuerung_xl from ps_cs_akt_prod_tb a where 
    a.business_unit = :1 and a.ra_cmpgn_wave_id = :2 and a.product_id = :3 
    and a.cs_aboart_cd = :4 

    Plan hash value: 619225732 

    ------------------------------------------------------------------------------------------------- 
    | Id | Operation     | Name    | Rows | Bytes | Cost (%CPU)| Time  | 
    ------------------------------------------------------------------------------------------------- 
    | 0 | SELECT STATEMENT   |     |  |  |  1 (100)|   | 
    | 1 | TABLE ACCESS BY INDEX ROWID| PS_CS_AKT_PROD_TB |  1 | 46 |  1 (0)| 00:00:01 | 
    |* 2 | INDEX UNIQUE SCAN   | PS_CS_AKT_PROD_TB |  1 |  |  1 (0)| 00:00:01 | 
    ------------------------------------------------------------------------------------------------- 

    Outline Data 
    ------------- 

     /*+ 
      BEGIN_OUTLINE_DATA 
      IGNORE_OPTIM_EMBEDDED_HINTS 
      OPTIMIZER_FEATURES_ENABLE('12.1.0.2') 
      DB_VERSION('12.1.0.2') 
      OPT_PARAM('_optimizer_max_permutations' 50) 
      OPT_PARAM('_unnest_subquery' 'false') 
      OPT_PARAM('optimizer_dynamic_sampling' 4) 
      OPT_PARAM('_gby_hash_aggregation_enabled' 'false') 
      OPT_PARAM('_optimizer_use_feedback' 'false') 
      OPT_PARAM('_px_adaptive_dist_method' 'off') 
      OPT_PARAM('_optimizer_dsdir_usage_control' 0) 
      OPT_PARAM('_optimizer_adaptive_plans' 'false') 
      OPT_PARAM('_optimizer_strans_adaptive_pruning' 'false') 
      OPT_PARAM('_optimizer_gather_feedback' 'false') 
      OPT_PARAM('_optimizer_nlj_hj_adaptive_join' 'false') 
      OPT_PARAM('optimizer_index_cost_adj' 20) 
      ALL_ROWS 
      OUTLINE_LEAF(@"SEL$1") 
      INDEX_RS_ASC(@"SEL$1" "A"@"SEL$1" ("PS_CS_AKT_PROD_TB"."BUSINESS_UNIT" 
        "PS_CS_AKT_PROD_TB"."RA_CMPGN_WAVE_ID" "PS_CS_AKT_PROD_TB"."PRODUCT_ID" 
        "PS_CS_AKT_PROD_TB"."CS_ABOART_CD")) 
      END_OUTLINE_DATA 
     */ 

    Predicate Information (identified by operation id): 
    --------------------------------------------------- 

     2 - access("A"."BUSINESS_UNIT"=:1 AND "A"."RA_CMPGN_WAVE_ID"=:2 AND 
        "A"."PRODUCT_ID"=:3 AND "A"."CS_ABOART_CD"=:4) 
+0

Возможно, это может быть помощь: https://carlos-sierra.net/2013/02/28/how-to-flush-one-cursor-out-of-the-shared-pool/ с возможностью запишите конкретный план выполнения: https://github.com/carlos-sierra/cscripts/blob/master/sql/spm/create_spb_from_cur.sql – valentin

+0

@valentin Спасибо, я ищу решение для решения этой проблемы без ручной загрузки все проблематичные планы к базовому уровню. Надеюсь, существует более общий подход к его решению. –

+0

@BobC Это сильно зависит от значений привязки. В некоторых редких случаях оба плана должны быть почти равны. Значения 'PRODUCT_ID' неравномерно распределены, см. Мое добавление –

ответ

1

Значения поля PRODUCT_ID неравномерно распределены ibuted (см. список распространения в моем вопросе выше).

Поэтому в некоторых редких случаях оба плана почти равны. Поскольку оператор вызывается с переменными связывания, оценка обрабатывается только при первом выполнении.

С

alter session set "_optim_peek_user_binds"=false; 

оптимизатор вынужден переоценивать значения связываемых во втором исполнении.

+1

Вы построили гистограммы, предложенные Крисом-Джонсоном? Я бы ожидал, что курсор станет привязкой привязки/связью. – BobC

+0

@BobC Нет, я не перестраивал/не менял гистограммы. При первом выполнении оптимизатор решил использовать соответствующий индекс в соответствии с статистикой поля 'PRODUCT_ID'. Проблема заключалась в повторном использовании этого плана выполнения с другими значениями привязки 'PRODUCT_ID '(с совершенно другой статистикой). –