Oracle объясняет, что план оценивает неверное количество элементов для сканирования диапазона индекса - PullRequest
5 голосов
/ 01 июля 2010

У меня есть база данных Oracle 10.2.0.3 и такой запрос:

select count(a.id) 
from LARGE_PARTITIONED_TABLE a
join SMALL_NONPARTITIONED_TABLE b on a.key1 = b.key1 and a.key2 = b.key2
where b.id = 1000

Таблица LARGE_PARTITIONED_TABLE (a) имеет около 5 миллионов строк и разделена столбцом, отсутствующим в запросе.Таблица SMALL_NONPARTITIONED_TABLE (b) не секционирована и содержит около 10000 строк.

Статистика актуальна, и в столбцах key1 и key2 таблицы a есть сбалансированные по высоте гистограммы таблицы.

Таблица a имеет первичный ключ и глобальный, нераздельный уникальный индекс для столбцов key1, key2, key3, key4 и key5.

План объяснения для запроса отображает следующие результаты:

---------------------------------------------------------------------------------------------------
| Id  | Operation          | Name                         | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |                              |     1 |    31 |     4   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |                              |     1 |    31 |            |          |
|   2 |   NESTED LOOPS     |                              |   406 | 12586 |     4   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN| INDEX_ON_TABLE_B            |     1 |    19 |     2   (0)| 00:00:01 |
|*  4 |    INDEX RANGE SCAN| PRIMARY_KEY_INDEX_OF_TABLE_A |   406 |  4872 |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------

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

   3 - access("b"."id"=1000)
   4 - access("a"."key1"="b"."key1" and
              "a"."key2"="b"."key2")

Таким образом, строки (количество элементов), оцененные для шага 4, равны 406 .

Теперь трассировка tkprof показывает следующее:

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE (cr=51 pr=9 pw=0 time=74674 us)
   7366   NESTED LOOPS  (cr=51 pr=9 pw=0 time=824941 us)
      1    INDEX RANGE SCAN INDEX_ON_TABLE_B (cr=2 pr=0 pw=0 time=36 us)(object id 111111)
   7366    INDEX RANGE SCAN PRIMARY_KEY_INDEX_OF_TABLE_A (cr=49 pr=9 pw=0 time=810173 us)(object id 222222)

Таким образом, кардинальность в действительности была 7366 , а не 406!

Мой вопрос такой: Откуда Oracle получает предполагаемую мощность из 406в этом случае и как я могу улучшить его точность , чтобы оценка соответствовала тому, что действительно происходит во время выполнения запроса?


Обновление: Вот фрагмент трассировки 10053, который я выполнил по запросу.

NL Join
  Outer table: Card: 1.00  Cost: 2.00  Resp: 2.00  Degree: 1  Bytes: 19
  Inner table: LARGE_PARTITIONED_TABLE  Alias: a
  ...
  Access Path: index (IndexOnly)
    Index: PRIMARY_KEY_INDEX_OF_TABLE_A
    resc_io: 2.00  resc_cpu: 27093
    ix_sel: 1.3263e-005  ix_sel_with_filters: 1.3263e-005
    NL Join (ordered): Cost: 4.00  Resp: 4.00  Degree: 1
      Cost_io: 4.00  Cost_cpu: 41536
      Resp_io: 4.00  Resp_cpu: 41536
  ****** trying bitmap/domain indexes ******
  Best NL cost: 4.00
          resc: 4.00 resc_io: 4.00 resc_cpu: 41536
          resp: 4.00 resp_io: 4.00 resp_cpu: 41536
Using concatenated index cardinality for table SMALL_NONPARTITIONED_TABLE
Revised join sel: 8.2891-e005 = 8.4475e-005 * (1/12064.00) * (1/8.4475e-005)
Join Card:  405.95 = outer (1.00) * inner (4897354.00) * sel (8.2891-e005)
Join Card - Rounded: 406 Computed: 405.95

Так вот откуда исходит значение 406,Как ответил Адам, кардинальное число присоединений равно join selectivity * filter cardinality (a) * filter cardinality (b), что видно на второй-последней строке приведенной выше цитаты следа.

Что я не понимаю, так это строка Revised join sel.1/12064 - это селективность индекса, используемого для поиска строки из таблицы b (12064 строки в таблице и выбор на основе уникального идентификатора).Но что с того?

  1. Кажется, что кардинальность рассчитывается путем умножения мощности фильтра таблицы b (4897354) на селективность таблицы a (1/12064).Зачем?Как избирательность в таблице a связана с тем, сколько строк ожидается найти в таблице b, , когда соединение a -> b не основано на a.id?

  2. Откуда берется число 8.4475e-005 (его больше нигде нет на всем пути следования)?Не то чтобы это влияло на вывод, но я все же хотел бы знать.

Я понимаю, что оптимизатор, вероятно, выбрал правильный путь здесь.Но все же кардинальность просчитана - и это может оказать существенное влияние на путь выполнения, который выбирается с этого момента (как в случае, если у меня IRL - этот пример является упрощением этого).

Ответы [ 3 ]

7 голосов
/ 01 июля 2010

Создание файла трассировки 10053 поможет точно показать, что именно делает выбор оптимизатора в отношении оценки его мощности и селективности.Jonathan Lewis 'excellect Основы Oracle на основе затрат - это отличный ресурс для понимания того, как работает оптимизатор, и у меня есть печати от 8i до 10,1.*

Однако, поскольку у нас есть объединение из нескольких столбцов, селективность объединения не на уровне таблицы, это произведение (пересечение) селективностей объединения в каждом столбце.Предполагая, что в игре нет нулей:

Join Selectivity = Join Selectivity (key1) * Join Selectivity (key2)

Join Selectivity (key1) =   ((5,000,000 - 0) / 5,000,000)
                          * ((10,000 - 0)) / 10,000)
                          / max (116, ?)  -- distinct key1 values in B

                        = 1 / max(116, distinct_key1_values_in_B)

Join Selectivity (key2) =   ((5,000,000 - 0) / 5,000,000)
                          * ((10,000 - 0)) / 10,000)
                          / max (650, ?)  -- distinct key2 values in B

                        = 1 / max(650, distinct_key2_values in B)

Join Cardinality =  JS(key1) * JS(key2) 
                  * Filter_cardinality(a) * Filter_cardinality(b)

Мы знаем, что на А нет фильтров, так что количество элементов фильтра таблиц - это количество строк.Мы выбираем значение ключа из B, так что количество элементов таблицы в таблице равно 1.

Таким образом, наилучший случай для оценки предполагаемой мощности соединения теперь равен

Join Cardinality  = 1/116 * 1/650 * 5,000,000 * 1

                  =~ 67

Возможно, работать прощеназад.Ваша предполагаемая мощность в 406, учитывая то, что мы знаем, приводит к выборочности объединения 406/5 000 000, или приблизительно 1/12315.Это действительно очень близко к 1 / (116 ^ 2), что является проверкой работоспособности в оптимизаторе, чтобы он не нашел слишком агрессивного количества элементов в соединениях с несколькими столбцами.

ДляTL; DR толпа:

  1. Получить Основы Oracle на основе затрат Джонатана Льюиса .
  2. Получить трассировку 10053 запроса, поведение которого вы можетене понимаю.
2 голосов
/ 01 июля 2010

Возможно, вам будет интересно прочитать эту превосходную статью Вольфганга Брайтлинга, в которой много информации о расчетах CBO: http://www.centrexcc.com/A%20Look%20under%20the%20Hood%20of%20CBO%20-%20the%2010053%20Event.pdf.

Как объясняется там, поскольку у вас есть гистограммы, вычисление коэффициента фильтра для этихВ столбцах используется не количество отдельных значений (NDV), а плотность, которая каким-то образом выводится из гистограммы.

Какие значения DENSITY в USER_TAB_COLUMNS для a.key1 и a.key2?

Как правило, проблема в подобных случаях заключается в том, что Oracle не собирает статистику по парам столбцов и предполагает, что их объединенный коэффициент фильтрации будет результатом их отдельных факторов.Это приведет к низким оценкам, если будет какая-либо корреляция между значениями двух столбцов.

Если это вызывает серьезную проблему с производительностью, я полагаю, вы могли бы создать основанный на функции индекс для функции этих столбцов, ииспользуйте это, чтобы сделать поиск.Тогда Oracle соберет статистику по этому индексу и, вероятно, даст более точные оценки.

2 голосов
/ 01 июля 2010

Оценка мощности будет основана на произведении селективности a.key1 и a.key2, которые (по крайней мере, в 10g) будут основаны на количестве различных значений для этих двух столбцов, как записано в столбце. статистика.

Для таблицы из 5M строк оценка мощности 406 существенно не отличается от 7366. Вопрос, который вы должны себе задать, заключается в том, является ли «неточная» оценка здесь причиной проблемы?

Вы можете проверить, какой план выберет Oracle, если бы он смог сгенерировать совершенно точную оценку, получив план объяснения для этого:

select /*+CARDINALITY(a 7366)*/ count(a.id) 
from LARGE_PARTITIONED_TABLE a
join SMALL_NONPARTITIONED_TABLE b on a.key1 = b.key1 and a.key2 = b.key2
where b.id = 1000;

Если это соответствует одному и тому же плану, то оценка, которую рассчитывает Oracle, уже адекватна.

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