выбор целых разделов использует индекс - почему? - PullRequest
3 голосов
/ 31 мая 2019

Я выбираю все данные из некоторых разделов многораздельной таблицы (Oracle 11g - фактически обновляется в реальном случае, но для примера select показывает то же поведение, которое я хотел бы понять). Можете ли вы объяснить мне, почему Oracle решает использовать индекс вместо полного сканирования? В моем понимании полное сканирование было бы более разумным методом доступа. Почему Oracle считает, что сканирование диапазона индекса + доступ к таблице по пакетному идентификатору строки более разумно, чем полное сканирование разделов?

Тестовый стол:

CREATE TABLE t_test
    (ID                NUMBER                 NOT NULL ENABLE, 
    PARTITION_NUMBER   NUMBER                 NOT NULL ENABLE, 
    CREATION_TIMESTAMP DATE   DEFAULT SYSDATE NOT NULL ENABLE, 
    CONSTRAINT PK_t_test PRIMARY KEY (PARTITION_NUMBER, ID) USING INDEX LOCAL
    ) 
PARTITION BY LIST (PARTITION_NUMBER) 
    (
    PARTITION P1  VALUES (1) SEGMENT CREATION IMMEDIATE,
    PARTITION P2  VALUES (2) SEGMENT CREATION IMMEDIATE,
    PARTITION P3  VALUES (3) SEGMENT CREATION IMMEDIATE,
    PARTITION P4  VALUES (4) SEGMENT CREATION IMMEDIATE,
    PARTITION P5  VALUES (5) SEGMENT CREATION IMMEDIATE,
    PARTITION P6  VALUES (6) SEGMENT CREATION IMMEDIATE,
    PARTITION P7  VALUES (7) SEGMENT CREATION IMMEDIATE,
    PARTITION P8  VALUES (8) SEGMENT CREATION IMMEDIATE,
    PARTITION P9  VALUES (9) SEGMENT CREATION IMMEDIATE
    );

Выбор данных из некоторых разделов (для этого примера ничего не было вставлено, но поведение аналогично данным в разделах):

explain plan for select * from t_test where PARTITION_NUMBER in (2,3,4,5,6,7);
SELECT * FROM TABLE(dbms_xplan.display);

Plan hash value: 3284178661

-------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                            |           |     1 |    35 |     1   (0)| 00:00:01 |       |       |
|   1 |  INLIST ITERATOR                            |           |       |       |            |          |       |       |
|   2 |   PARTITION LIST ITERATOR                   |           |     1 |    35 |     1   (0)| 00:00:01 |KEY(I) |KEY(I) |
|   3 |    TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| T_TEST    |     1 |    35 |     1   (0)| 00:00:01 |KEY(I) |KEY(I) |
|*  4 |     INDEX RANGE SCAN                        | PK_T_TEST |     1 |       |     2   (0)| 00:00:01 |KEY(I) |KEY(I) |
-------------------------------------------------------------------------------------------------------------------------

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

   4 - access("PARTITION_NUMBER"=2 OR "PARTITION_NUMBER"=3 OR "PARTITION_NUMBER"=4 OR "PARTITION_NUMBER"=5 OR 
              "PARTITION_NUMBER"=6 OR "PARTITION_NUMBER"=7)

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)

Я не понимаю, почему он сканирует диапазон ... Почему не просто так (тот же запрос, но с подсказкой FULL)? Почему это когда-либо думает, что использование индекса - лучший подход здесь? (Даже подсказка ALL_ROWS не меняет поведение):

explain plan for select /*+ full(t_test) */ * from t_test where PARTITION_NUMBER in (2,3,4,5,6,7);
SELECT * FROM TABLE(dbms_xplan.display);

Plan hash value: 3335595461

------------------------------------------------------------------------------------------------
| Id  | Operation             | Name   | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |        |     1 |    35 |     2   (0)| 00:00:01 |       |       |
|   1 |  PARTITION LIST INLIST|        |     1 |    35 |     2   (0)| 00:00:01 |KEY(I) |KEY(I) |
|   2 |   TABLE ACCESS FULL   | T_TEST |     1 |    35 |     2   (0)| 00:00:01 |KEY(I) |KEY(I) |
------------------------------------------------------------------------------------------------

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)

Ответы [ 2 ]

2 голосов
/ 01 июня 2019

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

Когда я запускаю ваш пример кода, пустая таблица содержит 72 мегабайта, но пустой индекс содержит 0,5 мегабайта:

select segment_name, sum(bytes)/1024/1024 mb
from user_segments
where segment_name in ('T_TEST', 'PK_T_TEST')
group by segment_name
order by 1 desc;

SEGMENT_NAME   MB
------------   -------
T_TEST         72
PK_T_TEST       0.5625

Полные сканы таблицы должны читать весь сегмент. Сканирование диапазона индекса все еще должно считывать данные из таблицы, но это можно сделать с помощью идентификаторов ROWID и, следовательно, считывать меньше данных с диска.

Автоматическое распределение пространства сегментов в Oracle почти всегда лучше, чем его настройка вручную. Но с тривиальным объемом данных может иметь смысл оптимизировать сегменты. Если вы измените каждый из 9 SEGMENT CREATION IMMEDIATE на STORAGE (INITIAL 64K NEXT 64K), тогда разделы таблицы будут меньше, и план выполнения будет использовать полное сканирование таблицы. Но вы, вероятно, не хотите этого делать. Эта проблема, вероятно, связана только с использованием нереально небольшого выборочного набора данных.

Пространственные алгоритмы Oracle по понятным причинам оптимизированы для хранения больших объемов данных в разделах.


К сожалению, это, вероятно, не поможет вам в вашей реальной проблеме. Вы упомянули таблицу с 300 миллионами строк, и такой размер должен лучше работать с секционированием.

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

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

0 голосов
/ 31 мая 2019

Поскольку индекс сообщает oracle, в каких разделах находятся ваши данные. Когда вы говорите «полное сканирование», вы имеете в виду полное сканирование таблицы или чтение всех строк в указанных разделах?Строка «TABLE ACCESS FULL» во втором плане объяснения имеет KEY(I) в столбцах Pstart и Pstop, поэтому Oracle будет сканировать только все строки в этих разделах, а не всю таблицу.

Таким образом, разделение ограничивает чтение только разделов в предложении where, и это чтение всех строк в определенных разделах и использование индекса, чтобы выяснить, какие разделы читать.Он использует сканирование диапазона для поиска ведущего значения столбца в вашем первичном ключе.В любом плане объяснения он использует индекс для просмотра только тех разделов, которые содержат указанный PARTITION_NUMBER - в первом плане объяснения вы видите 4 = access, что означает, что он использует индекс.

...