Подскажите как правильно организовать партиционирование таблицы и индексов

VikingCDC
Дата: 11.12.2007 20:57:17
структура таблицы
CREATE TABLE T1
(
  ID        INTEGER                             NOT NULL,
  IdUser    INT,
  TIME      DATE                                NOT NULL,
  ADRESS    VARCHAR2(15 BYTE),
  .
  .
  .
)

В таблицу за месяц попадает примерно 300млн записей

Выборки из нее осуществяются по Time и IdUser
т.е. запросы вида select IdUser, Sum(XXX) from T1 where Trunc(Time1,'HH') between XXXX and YYYY group by IdUser, Trunc(Time1,'HH')

Партиционировал таблицу след образом


PARTITION BY RANGE (TIME) 
(  
  PARTITION T1_200612 VALUES LESS THAN (TO_DATE(' 2007-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
    NOLOGGING
    NOCOMPRESS
    TABLESPACE T1_200612
    PCTFREE    10
    INITRANS   1
    MAXTRANS   255
    STORAGE    (
                INITIAL          64K
                MINEXTENTS       1
                MAXEXTENTS       2147483645
                BUFFER_POOL      DEFAULT
               ),  
  PARTITION T1_200701 VALUES LESS THAN (TO_DATE(' 2007-02-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
    NOLOGGING
    NOCOMPRESS
    TABLESPACE T1_200701
    PCTFREE    10
    INITRANS   1
    MAXTRANS   255
    STORAGE    (
                INITIAL          64K
                MINEXTENTS       1
                MAXEXTENTS       2147483645
                BUFFER_POOL      DEFAULT
               ),
 и т.д.

Создал партиционированный индекс
CREATE INDEX T1$TIME ON T1
(TRUNC("TIME",'fmhh'))
  INITRANS   2
  MAXTRANS   255
LOCAL (  
  PARTITION IDX_T1_200612
    NOLOGGING
    NOCOMPRESS
    TABLESPACE IDX_T1_200612
    PCTFREE    10
    INITRANS   2
    MAXTRANS   255
    STORAGE    (
                INITIAL          64K
                MINEXTENTS       1
                MAXEXTENTS       2147483645
                BUFFER_POOL      DEFAULT
               ),  
  PARTITION IDX_T1_200701
    NOLOGGING
    NOCOMPRESS
    TABLESPACE IDX_T1_200701
    PCTFREE    10
    INITRANS   2
    MAXTRANS   255
    STORAGE    (
                INITIAL          64K
                MINEXTENTS       1
                MAXEXTENTS       2147483645
                BUFFER_POOL      DEFAULT
               ),
и т.д.
но к сожалению элементарные запросы выполняются очень долго,
причем запрос вида
select time from T1 partition(T1_200712) where Trunc(Time,'HH')>='01.12.2007'

вроде идет по индексу - но результат все тотже - ужасно медленно (минут 20) выполняется

Plan
SELECT STATEMENT  ALL_ROWSCost: 2 M  Bytes: 391 M  Cardinality: 5 M  			
	3 PARTITION RANGE SINGLE  Cost: 2 M  Bytes: 391 M  Cardinality: 5 M  Partition #: 1  Partitions accessed #13		
		2 TABLE ACCESS BY LOCAL INDEX ROWID TABLE T1 Cost: 2 M  Bytes: 391 M  Cardinality: 5 M  Partition #: 2  Partitions accessed #13	
			1 INDEX FULL SCAN INDEX T1$TIME Cost: 261 K  Cardinality: 34 M  Partition #: 3  Partitions accessed #13
kapelan
Дата: 11.12.2007 21:02:36
CREATE INDEX T1$TIME ON T1
(TRUNC("TIME",'fmhh'))
select time from T1 partition(T1_200712) where Trunc(Time,'HH')>='01.12.2007'
TRUNC("TIME",'fmhh')) !=Trunc(Time,'HH')
VikingCDC
Дата: 11.12.2007 21:18:46
Хорошо
select time from T1 partition(T1_200712) where Trunc(Time,'fmHH')>='11.12.2007'

время выполнения и план аналогичный!!!
kapelan
Дата: 11.12.2007 21:58:10
SQL> alter session set nls_date_format='YYYY-MM-DD hh24:mi:ss';

Session altered.

SQL> select Trunc(sysdate,'fmHH') from dual;

TRUNC(SYSDATE,'FMHH
-------------------
2007-12-11 13:00:00

SQL> 

а так?
CREATE INDEX T1$TIME ON T1
(TIME)

select time from T1 partition(T1_200712) where Time>=TO_DATE('01.12.2007','DD.MM.YYYY')
kapelan
Дата: 11.12.2007 22:25:36
не заметил что у вас 2 индекса по одному и тому-же полю
второй индекс не нужен
andrey_anonymous
Дата: 11.12.2007 23:36:13
автор
запросы вида select IdUser, Sum(XXX) from T1 where Trunc(Time1,'HH') between XXXX and YYYY group by IdUser, Trunc(Time1,'HH')

1) Рекомендовал бы переписать условия:
where Trunc(Time1,'HH') between XXXX and YYYY
заменить на
where Time between XXXX and trunc(YYYY+1)-1/86400
В зависимости от размера окна можете получить вполне пристойный IRS.

Это довольно общая рекомендация - по мере сил не использовать функции от полей, по которым осуществляется поиск.

2) Имеет смысл приводить планы в формате dbms_xplan - их легче читать.
3) Вероятно, Вы нарезали слишком широкие секции - 20 минут отбора по индексу при актуальной(?) статистике - это много. Возможно, имело бы смысл сделать секции помельче и применять FTS.
4) Вероятно, стоит попробовать hash subpartition по IdUser, разместить подсекции на разных шпинделях и организовать отбор данных in parallel
5) Мне кажется, с FBI Вы погорячились (см. п.1), достаточно обычного локального индекса
...
N) Возможно, для особенно тяжелых запросов имеет смысл рассмотреть mview
G.M.
Дата: 12.12.2007 10:44:58
VikingCDC

причем запрос вида
select time from T1 partition(T1_200712) where Trunc(Time,'HH')>='01.12.2007'

вроде идет по индексу - но результат все тотже - ужасно медленно (минут 20) выполняется

А не по индексу он сколько идет?
Просто впечатление такое, что этот индекс только мешает и что лучше ориентироваться на full table scan
SQL*Plus
Дата: 12.12.2007 12:45:01
andrey_anonymous
автор
запросы вида select IdUser, Sum(XXX) from T1 where Trunc(Time1,'HH') between XXXX and YYYY group by IdUser, Trunc(Time1,'HH')

1) Рекомендовал бы переписать условия:
where Trunc(Time1,'HH') between XXXX and YYYY
заменить на
where Time between XXXX and trunc(YYYY+1)-1/86400
В зависимости от размера окна можете получить вполне пристойный IRS.

Это довольно общая рекомендация - по мере сил не использовать функции от полей, по которым осуществляется поиск.
2) Имеет смысл приводить планы в формате dbms_xplan - их легче читать.
3) Вероятно, Вы нарезали слишком широкие секции - 20 минут отбора по индексу при актуальной(?) статистике - это много. Возможно, имело бы смысл сделать секции помельче и применять FTS.
4) Вероятно, стоит попробовать hash subpartition по IdUser, разместить подсекции на разных шпинделях и организовать отбор данных in parallel
5) Мне кажется, с FBI Вы погорячились (см. п.1), достаточно обычного локального индекса
...
N) Возможно, для особенно тяжелых запросов имеет смысл рассмотреть mview
С рекомендацией 1) совершенно согласен. Не нужно так использовать функции во фразе WHERE
3) Возможно, что это так. Ориентируйтесь по приемлемому времени полного просмотра одной секции
4) не согласен. Для приведенного запроса хеш-подсекции только замедлит выборку, хотя, возможно, и несущественно,
так как вместо одной секции потребуется просмотр всех 2-х/4-х/8-ми/16-ти/32-х/... подсекций
5) Да, Function Based Index здесь совершенно лишний. Считаю, что индекс по полю TIME вообще не нужен

Считаю, что может пригодиться индекс (LOCAL) по полю IDUSER.
Он позволит быстро выбирать данные по ограниченному небольшому набору IDUSER за заданный период времени.
VikingCDC
Дата: 12.12.2007 14:28:53
G.M.
VikingCDC

причем запрос вида
select time from T1 partition(T1_200712) where Trunc(Time,'HH')>='01.12.2007'

вроде идет по индексу - но результат все тотже - ужасно медленно (минут 20) выполняется

А не по индексу он сколько идет?
Просто впечатление такое, что этот индекс только мешает и что лучше ориентироваться на full table scan


select * 
from T1 partition(T1_200712) where Time between '11.12.2007' and trunc(sysdate+1)-1/86400

Да быстрее - 15мин, но все равно не то что нужно!!!

andrey_anonymous, SQL*Plus - спасибо. Пока пытаюсь переварить ваши рекомендации (к сожалению не настолько близок к оптимизации как Вы).

Пока возникают следующие вопросы:
1. А на сколько большими вы порекомендуете делать партиции таблицы (сейчас это 200-300 млн на партицию - по месяцам побито).
2. Если бить по дням - то соответственно за год это будет 365 партиций, не повлияет ли такое кол-во как-то на производительность.
Нужно ли каждую партицию в отдельный tablespace класть или tablespace по месяцам создавать (в будующем планируется отключать их и скидывать в другое место).
Да, на разные винты раскинуть не могу, поскольку все хранится на внешнем хранилище SAN.
3. Я так и не пойму почему индекс по time (согласен что с функцией был не прав) не нужен - ведь в данном случае я именно по нему и делаю выборку, почему full scan работает быстрее? (статистику вообще поставил собирать раз в час)
andrey_anonymous
Дата: 12.12.2007 14:52:05
SQL*Plus
andrey_anonymous
автор
запросы вида select IdUser, Sum(XXX) from T1 where Trunc(Time1,'HH') between XXXX and YYYY group by IdUser, Trunc(Time1,'HH')

4) Вероятно, стоит попробовать hash subpartition по IdUser, разместить подсекции на разных шпинделях и организовать отбор данных in parallel
4) не согласен. Для приведенного запроса хеш-подсекции только замедлит выборку, хотя, возможно, и несущественно, так как вместо одной секции потребуется просмотр всех 2-х/4-х/8-ми/16-ти/32-х/... подсекций...
Считаю, что может пригодиться индекс (LOCAL) по полю IDUSER.
Он позволит быстро выбирать данные по ограниченному небольшому набору IDUSER за заданный период времени.

4) Не согласен с мотивами Вашего несогласия. Сам по себе итератор секций не оказывает заметного влияния на время выборки из больших секций. Зато неплохо относится к parallel, а уж для parallel dml - the only way...
Неактуально, в основном, ввиду того, что у автора один LUN
Вторым аргументом в пользу такого subpartition являлись бы запросы с ограничениям на IdUser вида "=", "in(...)"
Замедлится же не выборка, но скорее вставка. Впрочем, скорее всего незначительно.

Про индекс по IdUser: его целесообразность зависит от кардинальности IdUser и, если не подводит мой хрустальный шар, шансов в данной задаче у такого индекса маловато - при низкой кардинальности скорее стоит смотреть в сторону subpartitioning.
Впрочем, it depends.

VikingCDC
1. А на сколько большими вы порекомендуете делать партиции таблицы (сейчас это 200-300 млн на партицию - по месяцам побито).
Как уже отметил SQL*Plus, ориентироваться следует на приемлемое время сканирования одной секции.
VikingCDC

2. Если бить по дням - то соответственно за год это будет 365 партиций, не повлияет ли такое кол-во как-то на производительность.
На окнах в один-два дня повлияет положительно. На "месячных" особо не повлияет пока границы окна в пределах одного месяца и может дать близкий к двукратному выигрыш при пересечении окном границ месяца
VikingCDC

Нужно ли каждую партицию в отдельный tablespace класть или tablespace по месяцам создавать (в будующем планируется отключать их и скидывать в другое место).
Это вам решать. Но если все лежит на одном LUN, то я бы не слишком заморачивался - сделал бы 2-3 пространства и пользовал бы циклически.
VikingCDC
3. Я так и не пойму почему индекс по time (согласен что с функцией был не прав) не нужен - ведь в данном случае я именно по нему и делаю выборку, почему full scan работает быстрее? (статистику вообще поставил собирать раз в час)

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