2013-09-23 3 views
2

У меня есть таблица с 115 М строк. Один из столбцов индексируется (индекс, называемый «my_index» в плане объяснения ниже) и не имеет значения NULL. Более того, этот столбец имеет только одно значение.Отличные значения в индексированном столбце

Когда я

select distinct my_col from my_table; 

, он занимает 230 секунд, что очень долго. Вот план объяснений.

| Id | Operation   | Name  | Rows | Bytes | Cost (%CPU)| Time  | 
| 0 | SELECT STATEMENT |   |  1 |  3 | 22064 (90)| 00:03:23 | 
| 1 | SORT UNIQUE NOSORT|   |  1 |  3 | 22064 (90)| 00:03:23 | 
| 2 | INDEX FULL SCAN | my_index | 115M| 331M| 2363 (2)| 00:00:22 | 

Поскольку столбец имеет только одно отличное значение, почему это так долго? Почему Oracle не просто проверяет записи индекса и быстро обнаруживает, что для этого столбца есть только одно возможное значение? В приведенном выше плане объяснения, сканирование индекса, кажется, занимает 22 с, но что это за «СОРТИРОВАННЫЙ УНИКАЛЬНЫЙ НОСОРТ», который занимает много времени?

Заранее благодарю вас за помощь

+0

Версия Orcale: Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production – Comencau

ответ

0

Подробно ознакомьтесь с планом объяснения.

  1. Она сканирует весь индекс, чтобы знать, что вы пытаетесь принести
  2. Затем применяет определенную функцию (пытаться получить уникальные значения). Хотя вы говорите, что есть только одно уникальное значение, он должен сканировать весь индекс, чтобы получить значения. Oracle не знает, что в индексе есть только одно значение. Вы можете ограничить rownum = 1, чтобы получить быстрый ответ.

Попробуйте это, чтобы получить быстрый ответ

select my_col from my_table where rownum = 1; 

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

+0

Да, я согласен, что наличие только одного значения в этом столбце не имеет смысла. Я бы сказал больше: нет смысла иметь колонку вообще. Но в ближайшем будущем эта колонка будет иметь 3 или 4 разных значения. Я просто задавался вопросом, почему фаза 1 занимает так много времени, тогда как ей дается набор результатов только одного значения дочерней фазой 2. – Comencau

+1

@Comencau - ID 1 в плане не дает результат всего одного стоимость; шаг ID 2 возвращает каждое значение из индекса, это идентификатор шага 1, который сводит его к одному значению. Вы можете видеть, что из столбца 'rows' - ​​идентификатор 2 производит строки длиной 115 м, идентификатор 1 сортирует их и создает 1 строку. Шаг ID 0 не занимает времени - столбец 'time' является кумулятивным. Требуется 22 секунды, чтобы получить значения 115 м из индекса, и 3:01, чтобы отсортировать их для удаления дубликатов. (Возможно, еще немного упростить). –

3

Повторите анализ таблицы.

EXEC dbms_stats.gather_table_stats('owner','table_name',cascade=>true,method_opt=>'FOR ALL INDEXED COLUMNS SIZE '); 

Изменение индекс Тип

одно неоспоримое значение из 115Х строк ?? !! Это то, что называется низкой мощностью, не так хорошо для «нормального» индекса B-Tree. Рассмотрим растровый индекс. (Если у всех вас есть B-дерево)

Реконструкция Запрос

Если вы уверены, что никакие новые значения не будут добавлены в эту колонку, пожалуйста, удалите отчетливое положение и лучше использовать, как сказал Абайджит.

+0

Реконструирующий запрос не является вариантом, так как этот запрос будет иметь смысл, когда будет еще 3 или 4 различных значения. Однако использование растрового изображения определенно является хорошей идеей, когда имеется несколько разных значений. Я попробую. Тем не менее, я до сих пор не понимаю, что это за «СОРТИРОВАННЫЙ УНИКАЛЬНЫЙ НОСОРТ», который занимает более 3 минут, чтобы отсортировать одно значение (в моем понимании, конечно :), я думаю, это делает много чего, но я не понимаю что) – Comencau

+0

план по умолчанию был последовательным полным сканированием индекса, который позволил оптимизатору обойти «порядок сортировки» из-за «уникальной сортировки». – SriniV

+1

+1 Но вообще 'FOR ALL INDEXED' [не рекомендуется] (https://blogs.oracle.com/optimizer/entry/how_does_the_method_opt). –

3

SORT UNIQUE NOSORT не занимает слишком много времени. Вы смотрите на оценки из плохих планов выполнения, которые, вероятно, являются результатом необоснованных параметров оптимизатора. Например, установка параметра OPTIMIZER_INDEX_COST_ADJ в 1 вместо 100 по умолчанию может создать аналогичный план. Скорее всего, ваш запрос выполняется медленно, потому что ваша база данных занята или просто медленна.

Что случилось с опубликованным планом выполнения?

Опубликованный план выполнения кажется необоснованным.Получение данных должно занимать гораздо больше времени, чем просто выкидывать дубликаты. И потребительская операция, SORT UNIQUE NOSORT, может начаться почти в то же время, что и операция производителя, INDEX FULL SCAN. Обычно они должны заканчиваться почти одновременно. В плане выполнения в вопросе отображается оптимизатор оценки. Снимок экрана, приведенный ниже активного отчета, показывает фактические временные рамки для очень похожего запроса. Все шаги начинаются и останавливаются почти одновременно.

enter image description here

установка образца с разумным планом

Ниже приведена очень аналогичные установки, но с очень простой конфигурацией. Такое же количество строк читается (115 миллионов) и возвращается (1), и почти точно такой же размер сегмента (329 МБ против 331 МБ). План показывает почти все время, потраченное на INDEX FULL SCAN.

drop table test1 purge; 
create table test1(a number not null, b number, c number) nologging; 
begin 
    for i in 1 .. 115 loop 
     insert /*+ append */ into test1 select 1, level, level 
     from dual connect by level <= 1000000; 
     commit; 
    end loop; 
end; 
/
create index test1_idx on test1(a); 
begin 
    dbms_stats.gather_table_stats(user, 'TEST1'); 
end; 
/
explain plan for select /*+ index(test1) */ distinct a from test1; 
select * from table(dbms_xplan.display); 

Plan hash value: 77032494 

-------------------------------------------------------------------------------- 
| Id | Operation   | Name  | Rows | Bytes | Cost (%CPU)| Time  | 
-------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |   |  1 |  3 | 244K (4)| 00:48:50 | 
| 1 | SORT UNIQUE NOSORT|   |  1 |  3 | 244K (4)| 00:48:50 | 
| 2 | INDEX FULL SCAN | TEST1_IDX | 115M| 329M| 237K (1)| 00:47:30 | 
-------------------------------------------------------------------------------- 

Воссоздание плохой план

--Set optimizer_index_cost_adj to a ridiculously low value. 
--This changes the INDEX FULL SCAN estimate from 47 minutes to 29 seconds. 
alter session set optimizer_index_cost_adj = 1; 

--Changing the CPUSPEEDNW to 800 will exactly re-create the time estimate 
--for SORT UNIQUE NOSORT. This value is not ridiculous, and it is not 
--something you should normally change. But it does imply your CPUs are 
--slow. My 2+ year-old desktop had an original score of 1720. 
begin 
    dbms_stats.set_system_stats('CPUSPEEDNW', 800); 
end; 
/

explain plan for select /*+ index(test1) */ distinct a from test1; 
select * from table(dbms_xplan.display); 

Plan hash value: 77032494 

-------------------------------------------------------------------------------- 
| Id | Operation   | Name  | Rows | Bytes | Cost (%CPU)| Time  | 
-------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |   |  1 |  3 | 16842 (86)| 00:03:23 | 
| 1 | SORT UNIQUE NOSORT|   |  1 |  3 | 16842 (86)| 00:03:23 | 
| 2 | INDEX FULL SCAN | TEST1_IDX | 115M| 329M| 2389 (2)| 00:00:29 | 
-------------------------------------------------------------------------------- 

Как исследовать

Проверьте параметры.

select name, value from v$parameter where name like 'optimizer_index%' 

NAME      VALUE 
----      ----- 
optimizer_index_cost_adj 1 
optimizer_index_caching  0 

Также проверьте статистику системы.

select * from sys.aux_stats$; 

+---------------+------------+-------+------------------+ 
|  SNAME  | PNAME | PVAL1 |  PVAL2  | 
+---------------+------------+-------+------------------+ 
| SYSSTATS_INFO | STATUS  |  | COMPLETED  | 
| SYSSTATS_INFO | DSTART  |  | 09-23-2013 17:52 | 
| SYSSTATS_INFO | DSTOP  |  | 09-23-2013 17:52 | 
| SYSSTATS_INFO | FLAGS  |  1 |     | 
| SYSSTATS_MAIN | CPUSPEEDNW | 800 |     | 
| SYSSTATS_MAIN | IOSEEKTIM | 10 |     | 
| SYSSTATS_MAIN | IOTFRSPEED | 4096 |     | 
| SYSSTATS_MAIN | SREADTIM |  |     | 
| SYSSTATS_MAIN | MREADTIM |  |     | 
| SYSSTATS_MAIN | CPUSPEED |  |     | 
| SYSSTATS_MAIN | MBRC  |  |     | 
| SYSSTATS_MAIN | MAXTHR  |  |     | 
| SYSSTATS_MAIN | SLAVETHR |  |     | 
+---------------+------------+-------+------------------+ 

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

select dbms_sqltune.report_sql_monitor(sql_id => '5s63uf4au6hcm', 
    type => 'active') from dual; 
+0

Действительно большой отличный ответ! Большое спасибо за такой точный и знающий ответ. Попробуй свои рекомендации завтра на работе и сообщите об этом. – Comencau

1

Если есть только несколько различных значений столбца, попробуйте сжатую индекс:

create index my_index on my_table (my_col) compress; 

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

В качестве бонуса: используйте это, чтобы увидеть реальный план, используемый для запроса:

select /*+ gather_plan_statistics */ distinct my_col from my_table; 
SELECT * FROM table(DBMS_XPLAN.DISPLAY_CURSOR); 

В gather_plan_statistics намекают будет собирать больше данных (это займет больше времени, чтобы выполнить), но она работает и без него тоже. Подробнее см. Документацию DBMS_XPLAN.DISPLAY_CURSOR.

Смежные вопросы