оптимизация оракула - PullRequest
       10

оптимизация оракула

1 голос
/ 01 апреля 2010

Мне трудно обернуть голову вокруг этого запроса. это займет почти 200+ секунд, чтобы выполнить. Я также вставил план выполнения.

SELECT       
      user_id    ,
      ROLE_ID              ,
      effective_from_date  ,
      effective_to_date    ,
      participant_code     ,
      ACTIVE
FROM    
      CMP_USER_ROLE E
WHERE   
      ACTIVE = 0
 AND  (SYSDATE BETWEEN effective_from_date AND effective_to_date 
        OR TO_CHAR(effective_to_date,'YYYY-Q') = '2010-2')
 AND  participant_code = 'NY005'
 AND  NOT EXISTS
            ( SELECT 1 FROM CMP_USER_ROLE r 
              WHERE r.USER_ID= E.USER_ID 
              AND r.role_id = E.role_id 
              AND r.ACTIVE = 4
              AND E.effective_to_date 
                      <= ( SELECT MAX(last_update_date)
                           FROM CMP_USER_ROLE S
                           WHERE S.role_id = r.role_id
                           AND S.role_id = r.role_id
                           AND S.ACTIVE = 4 ))

Объяснить план

-----------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |                  |     1 |    37 |   154   (2)| 00:00:02 |
|*  1 |  FILTER                          |                  |       |       |            |          |
|*  2 |   TABLE ACCESS BY INDEX ROWID    | USER_ROLE        |     1 |    37 |    30   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN              | N_USER_ROLE_IDX6 |    27 |       |     3   (0)| 00:00:01 |
|*  4 |   FILTER                         |                  |       |       |            |          |
|   5 |    HASH GROUP BY                 |                  |     1 |    47 |   124   (2)| 00:00:02 |
|*  6 |     TABLE ACCESS BY INDEX ROWID  | USER_ROLE        |   159 |  3339 |   119   (1)| 00:00:02 |
|   7 |      NESTED LOOPS                |                  |    11 |   517 |   123   (1)| 00:00:02 |
|*  8 |       TABLE ACCESS BY INDEX ROWID| USER_ROLE        |     1 |    26 |     4   (0)| 00:00:01 |
|*  9 |        INDEX RANGE SCAN          | N_USER_ROLE_IDX5 |     1 |       |     3   (0)| 00:00:01 |
|* 10 |       INDEX RANGE SCAN           | N_USER_ROLE_IDX2 |   957 |       |    74   (2)| 00:00:01 |
-----------------------------------------------------------------------------------------------------

Статистика:

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
    3433602  consistent gets
          0  physical reads
          0  redo size
      58149  bytes sent via SQL*Net to client
       1260  bytes received via SQL*Net from client
        148  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       2199  rows processed

Ответы [ 3 ]

3 голосов
/ 02 апреля 2010

Хмммм, у меня атака дежавю .

В любом случае, вот над чем нужно работать:

3433602  consistent gets

Три миллиона логических операций ввода-вывода потратят много времени, поэтому вам нужно уменьшить это число.

Ваш запрос состоит из трех наборов обращений к одной и той же таблице. Каждый доступ состоит из чтения индекса, за которым следует чтение таблицы. Из вашего комментария к вопросу Питера кажется, что ваша статистика достаточно точна (запрос возвращает 699415 строк, NUM_ROWS = 697608).

Настройка - сложное занятие с множеством факторов, которые необходимо учитывать. Мы могли бы легко потратить полдня на то, чтобы разобраться во всем, что может быть не так в этом одном запросе.

Например, собираете ли вы статистику для своих индексов, а также для таблицы (в более ранних версиях Oracle по умолчанию не собиралась статистика индекса)? Если у вас есть статистика, каков фактор кластеризации этих индексов? Все ваши обращения к индексу являются сканированием, поэтому фактор кластеризации имеет значение.

Нам нужен низкий коэффициент кластеризации, потому что это означает, что индекс должен выполнять меньше работы, чтобы получить строки из таблицы. Если коэффициент кластеризации ближе к количеству записей в индексе, это плохо, тогда как, если он ближе к количеству блоков в таблице, это хорошо. Увы, учитывая количество ЛИО, которые вы испытываете, мои деньги направлены на плохие факторы кластеризации. Таким образом, вы должны получить больше сока из индексов.

Глядя на ваш запрос, столбцы в самой внешней проекции используются в предложении WHERE запроса и / или подзапросах. Несмотря на этот факт, вы используете три разных индекса, и ни один из них не предоставляет всю информацию, необходимую для соответствия критериям (отсюда дополнительная таблица читает и последующая фильтрация). Одна из тактик, которая может быть очень эффективной в таких ситуациях, - это создать супериндекс, содержащий все необходимые столбцы.

create index N_USER_ROLE_IDX23 on user_role    
       ( active
         , role_id
         , user_id
         , participant_code
         , effective_from_date
         , effective_to_date
         , last_update_date )

Это приводит к ACTIVE и ROLE_ID, потому что эти столбцы используются во всех трех наборах критериев. (Кстати, ваш третий запрос говорит это:

                       WHERE S.role_id = r.role_id
                       AND S.role_id = r.role_id

Это правильно? ) В любом случае, смысл этого индекса в том, что он удовлетворяет всем трем предложениям WHERE и окончательному прогнозу, поэтому он вообще устраняет необходимость касаться таблицы. Следовательно, это может значительно сократить количество последовательных приобретений.

0 голосов
/ 01 апреля 2010

Попробуйте запустить EXPLAIN PLAN для вашего запроса и посмотрите, выполняет ли он сканирование таблицы.

Я думаю, что это предложение будет проблематичным:

OR TO_CHAR(effective_to_date,'YYYY-Q') = '2010-2')

Я думаю, что вызов функции вПредложение WHERE заставляет Oracle сканировать каждую строку, потому что она должна оценивать эту функцию в каждом столбце, чтобы увидеть, является ли эта строка частью набора результатов.Таким способом вы лишаете законной силы свои индексы.

Лучшим решением будет поиск по индексируемому значению столбца, для которого не требуется вызов функции для его оценки.Я бы порекомендовал что-то вроде «DATE_COLUMN BETWEEN x AND y», где x и y - даты начала и окончания квартала.Убедитесь, что есть индекс DATE_COLUMN.

0 голосов
/ 01 апреля 2010

Первым делом проанализируем вашу таблицу:

EXEC dbms_stats.gather_table_stats('YOUR_SCHEMA', 'CMP_USER_ROLE');

Вы все еще получаете тот же план выполнения?

Столбец Time в плане выполнения выглядит так, как будто ваш запрос выглядит не так дорого для оптимизатора Oracle.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...