У меня есть таблица с 250K строк в моей тестовой базе данных. (В производстве несколько сотен миллионов, мы можем наблюдать ту же проблему.) Таблица имеет строковый идентификатор nvarchar2 (50), а не ноль, с уникальным индексом (это не PK).
Идентификаторы состоят из первой части, имеющей 8 различных значений в моей тестовой базе данных (и около тысячи в производстве), затем знака @ и, наконец, числа длиной от 1 до 6 цифр. Например, может быть 50 тысяч строк, которые начинаются с 'ABCD_BGX1741F_2006_13_20110808.xml @', а за ним следуют 50 тысяч различных номеров.
Когда я запрашиваю одну строку на основе ее идентификатора, количество элементов оценивается как 1, стоимость очень низкая, она работает нормально. Когда я запрашиваю более одной строки с несколькими идентификаторами в выражении IN или выражении OR, оценки индекса совершенно неверны, поэтому используется полное сканирование таблицы. Если я намекаю на индекс с подсказкой, он очень быстрый, полное сканирование таблицы выполняется на порядок медленнее (и намного медленнее в производстве). Так что это проблема оптимизатора.
В качестве теста я продублировал таблицу (в той же схеме + табличное пространство) с точно таким же DDL и точно таким же содержимым. Я воссоздал уникальный индекс в первой таблице для хорошей меры и создал точно такой же индекс в таблице клонов. Я сделал DBMS_STATS.GATHER_SCHEMA_STATS('schemaname',estimate_percent=>100,cascade=>true);
. Вы даже можете видеть, что имена индексов являются последовательными. Таким образом, теперь единственное различие между этими двумя таблицами состоит в том, что первая была загружена в произвольном порядке в течение длительного периода времени, когда на диске были разбросаны блоки (в табличном пространстве вместе с несколькими другими большими таблицами), вторая была загружена как одна пакетная. INSERT-SELECT. Кроме этого, я не могу представить никакой разницы. (Исходная таблица была уменьшена с момента последнего большого удаления, и после этого не было ни одного удаления.)
Вот планы запросов для таблицы больных и клонов (строки под черной кистью одинаковы по всей картинке, а также под серой кистью.):
(В этом примере есть 1867 строк, которые начинаются с идентификатора, выделенного черным цветом. 2-строчный запрос дает мощность 1867 * 2, 3-строчный запрос создает мощность 1867 * 3 и т. Д. По совпадению, Oracle, похоже, не заботится о конце идентификаторов.)
Что может вызвать такое поведение? Очевидно, было бы довольно дорого воссоздать таблицу в производстве.
USER_TABLES: http://i.stack.imgur.com/nDWze.jpg USER_INDEXES: http://i.stack.imgur.com/DG9um.jpg Я изменил только имя схемы и табличного пространства. Вы можете видеть, что имена таблиц и индексов такие же, как на скриншоте плана запросов.
источник
in
), не так ли? Я думаю, что CBO делает предположение о количестве элементов 1, но только в самом простом случае. Я предполагаю, что вы могли бы обойти все это, используя большой,UNION ALL
но могут быть другие причины не делать этого, и JL упоминает другие возможные обходные пути в связанном сообщении в блоге.method_opt=>'for all indexed columns'
?Я нашел решение! Это так красиво, и я действительно узнал много об Oracle.
Одним словом: гистограммы.
Я начал много читать о том, как работает ОО Oracle, и наткнулся на гистограммы. Я не до конца понял, поэтому взглянул на таблицу USER_HISTOGRAMS и вуаля. Там было несколько рядов для больного стола и практически ничего для клонированного стола. Для больной таблицы была одна строка для каждой из 8 различных частей-идентификаторов. И это ключ: они были обрезаны по 32 символа перед знаком @. Как я уже сказал, первая часть клавиш очень повторяющаяся, после знака @ они становятся разными.
Кажется, что гистограммы могут быть более мощными, чем простой факт, что уникальный индекс всегда имеет мощность 0 или 1 для данного значения. Когда я запрашивал 2+ строки, Oracle посмотрел на гистограмму, подумал, что для этой части, идентифицирующей начало, может быть десятки тысяч значений, и это сбило CBO с курса.
Я удалил гистограммы для этого столбца в старой таблице, и проблема исчезла!
Дополнительная информация: https://blogs.oracle.com/optimizer/entry/how_do_i_drop_an_existing_histogram_on_a_column_and_stop_the_auto_stats_gathering_job_from_creating
источник
Я написал по электронной почте Джонатану Льюису об этом и получил очень полезный ответ:
Я настоятельно рекомендовал прочитать сообщения в блоге, на которые он ссылается, они подробно описывают ограничение гистограмм, в которые вы работаете, например:
источник