Dsvolk > > Oracle > > Tuning > > Index_choice My Blog | Search | About
(Not Logged In)
[ welcome! ] [ news ] [ install ] [ jump-jet ] [ app ] [ rac ] [ papers ] [ dba ] [ dvp ] [ racdd4d ] [ oem ] [ statspack ] [ education ] [ tuning ] [ ias ] [ backup ] [ dataprotection ] [ security ] [ oid ] [ options ] [ integration ] [ sales ] [ sun ] [ linux ] [ consulting ] [ faq ]

Выбор индекса

Соглашение о материалах на этом сайте

Мой oracle blog
true dsvolk!
индекс выбрать - не поле перейти  
Часто возникает вопрос, почему оптимизатор выбирает один индекс, а не другой. Ниже рассматривается такой пример и делается попытка объяснения.

Рассмотрим некоторую гипотетическую ситуацию, когда у пользователя PORTAL есть 2 индекса на 1 таблице и попытаемся понять когда и какой из выбирается оптимизатором и почему.

Таблицу наше зовут ADM_CRIME_TEST, индексированные поля FIRST_NAME, LAST_NAME, SECOND_NAME, индексы INDX_L_F_S_NAME и INDX_SECOND_NAME. 

Описание таблицы и индексов получено при помощи tabdesc.sql

Table               Number             Empty Average Chain Average Global User       Sample Date
Name               of Rows   Blocks   Blocks   Space Count Row Len Stats  Stats        Size MM-DD-YYYY
--------------- ---------- -------- -------- ------- ----- ------- ------ ------ ---------- ----------
ADM_CRIME_TEST       1,212       61       25   1,585     0     309 YES    NO          1,212 08-26-2003

Column                    Column                   Distinct          Number  Number Global User       Sample Date
Name                      Details                    Values Density Buckets   Nulls Stats  Stats        Size MM-DD-YYYY
------------------------- ------------------------ -------- ------- ------- ------- ------ ------ ---------- ----------
LAST_NAME                 VARCHAR2(30) NOT NULL           4       0       3       0 YES    NO          1,212 08-26-2003
FIRST_NAME                VARCHAR2(30) NOT NULL          81       0      32       0 YES    NO          1,212 08-26-2003
SECOND_NAME               VARCHAR2(30)                   90       0      30       3 YES    NO          1,209 08-26-2003

                                   B                                Average     Average
Index                           Tree Leaf   Distinct     Number Leaf Blocks Data Blocks  Cluster Global User       Sample Date
Name                 Unique    Level Blks       Keys    of Rows     Per Key     Per Key   Factor Stats  Stats        Size MM-DD-YYYY
-------------------- --------- ----- ---- ---------- ---------- ----------- ----------- -------- ------ ------ ---------- ----------
INDX_L_F_S_NAME      NONUNIQUE     1    7        503      1,212           1           1      160 NO     NO          1,212 08-26-2003
INDX_SECOND_NAME     NONUNIQUE     1    4         90      1,209           1           5      499 NO     NO          1,209 08-26-2003

Index                Column                     Col Column
Name                 Name                       Pos Details
-------------------- ------------------------- ---- ------------------------
INDX_L_F_S_NAME      LAST_NAME                    1 VARCHAR2(30) NOT NULL   
                     FIRST_NAME                   2 VARCHAR2(30) NOT NULL   
                     SECOND_NAME                  3 VARCHAR2(30)            
INDX_SECOND_NAME     SECOND_NAME                  1 VARCHAR2(30)  


Будем выполнять запрос вида: 

SELECT a.birthday_region, a.birthday_date, a.entry_date
FROM adm_crime_test a where last_name = :lname
and SECOND_NAME = :sname
/

Сначала удалим статистику:

exec dbms_stats.delete_table_stats (ownname => 'PORTAL', tabname => 'ADM_CRIME_TEST');

Оказывается, без статистики вне зависимости от того, используются ли bind переменные или литералы используется индекс INDX_SECOND_NAME. Я думаю это такая установка оптимизатора, использовать индекс с меньшим количеством полей. Поскольку он явно меньше, и следовательно потребуется меньше  I/O операций.

Соберем статистику

exec dbms_stats.gather_table_stats (ownname => 'PORTAL', tabname => 'ADM_CRIME_TEST', method_opt => 'for all indexed columns', cascade => TRUE)

Если использовать bind переменные то всегда используется индекс INDX_L_F_S_NAME, вероятно из-за лучшего cluster_factor (и это несмотря даже на то что при его сканировании делается больше чтений) , а вот если использовать вместо bind переменных константы, то план выполнения зависит от их значений, и могут выбираться оба индекса. Это происходит, очевидно, из-за использования оптимизатором гистограммы. И выбор индекса тут более оптимален, чем в предыдущем случае. 

Планы выполнения:

with bind 
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=3 Bytes=159)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'ADM_CRIME_TEST' (Cost=4
          Card=3 Bytes=159)

   2    1     INDEX (RANGE SCAN) OF 'INDX_L_F_S_NAME' (NON-UNIQUE) (Co
          st=3 Card=3)
          

          0  recursive calls
          0  db block gets
         18  consistent gets
          0  physical reads
          0  redo size
          

          
with some literals

 0      SELECT STATEMENT Optimizer=CHOOSE (Cost=6 Card=10 Bytes=530)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'ADM_CRIME_TEST' (Cost=6
          Card=10 Bytes=530)

   2    1     INDEX (RANGE SCAN) OF 'INDX_SECOND_NAME' (NON-UNIQUE) (C
          ost=1 Card=10)

         0  recursive calls
          0  db block gets
         13  consistent gets
          0  physical reads
          0  redo size


Из trace файла (как его получить)           


  Access path: index (scan)
      Index: INDX_L_F_S_NAME
  TABLE: ADM_CRIME_TEST
      RSC_CPU: 0   RSC_IO: 10
  IX_SEL:  9.9422e-01  TB_SEL:  8.0115e-03
  Access path: index (equal)
      Index: INDX_SECOND_NAME
  TABLE: ADM_CRIME_TEST
      RSC_CPU: 0   RSC_IO: 6


select count(*) from adm_crime_test where last_name = 'СМИРНОВА';
COUNT(*)
----------
      1205

select count(*) from adm_crime_test where second_name  = 'ИГОРЕВНА';
  COUNT(*)
----------
        16

Из документации:

The optimizer uses the following criteria when determining which index to use:

  • Number of rows in the index (cardinality).
  • Number of distinct keys. These define the selectivity of the index.
  • Level or height of the index. This indicates how deeply the data probe must search in order to find the data.
  • Number of leaf blocks in the index. This is the number of I/Os needed to find the desired rows of data.
  • Clustering factor (CF). This is the collocation amount of the index block relative to data blocks. The higher the CF, the less likely the optimizer is to select this index.
  • Average leaf blocks for each key (ALFBKEY). Average number of leaf blocks in which each distinct value in the index appears, rounded to the nearest integer. For indexes that enforce UNIQUE and PRIMARY KEY constraints, this value is always one.
Dsvolk > > Oracle > > Tuning > > Index_choice Last Modified: 27-08-2003 18:11