Секционирование - поможет ли?

Почемух
Дата: 04.12.2007 16:00:39
Есть у нас табличка, в которой хранится всякая история изменений редактирования.

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

В результате таких изменений накопилос 16 млн строк.
Проблема нарисовалась следующая: по этой истории делаются отчеты, которым в основном надо знать сколько и куда организаций было передвинута с шага на шаг.

Вот они-то и начали иногда тормозить, если в период расчета попадают большие обновления организаций (загрузка, обновление скриптами).

что с этим делать - выхода видим пока два.
1) Разделить историю на две таблицы: история перевода организаций и история изменений
2) Сделать табице секционирование, перелить данные в новую разбитую на секции таблицу.

Особенность у нас такая, что если вводить секционирование, то все данные будут лежать на одном физическом носителе. Разносить их по разным дискам - не получится.

Вот и встал вопрос - даст ли что-нибудь секционирование таблиц, для решения вопросов производительности отчетов?
orawish
Дата: 04.12.2007 16:09:07
посмотрите - всё ли из того, что храните равноценно - мож. наиболее востребованные результаты стоит денормо-материализовать
Потомух
Дата: 04.12.2007 16:12:06
1) 16 млн строк - не объем
2) Секционирование разве для этого придумали?
3) Приводите здесь запросы, которые тормозят, их планы, инфу о таблицах/индексах, тогда и будет надежда, что вам здесь помогут, а так - гадание на кофейной гуще
Почемух
Дата: 05.12.2007 12:36:17
Потомух
2) Секционирование разве для этого придумали?
Как я понимаю, и для этого тоже.

Partitioned Tables and Indexes
Oracle9i Database Concepts
Partitioning improves query performance. In many cases, the results of a query can be achieved by accessing a subset of partitions, rather than the entire table. For some queries, this technique (called partition pruning) can provide order-of-magnitude gains in performance.


Потомух
3) Приводите здесь запросы, которые тормозят, их планы, инфу о таблицах/индексах, тогда и будет надежда, что вам здесь помогут, а так - гадание на кофейной гуще


таблица:
CREATE TABLE act_oper_activ_log
    (id                             NUMBER(*,0) NOT NULL,
    oper_id                        NUMBER(*,0),
    oper_login                     VARCHAR2(50),
    event_date                     DATE,
    event_categ                    VARCHAR2(50),
    event_message                  VARCHAR2(600),
    old_state_org_id               NUMBER(*,0),
    new_state_org_id               NUMBER(*,0),
    org_id                         NUMBER(*,0),
    interval                       NUMBER,
    machine                        VARCHAR2(500))
/

-- Indexes for ACT_OPER_ACTIV_LOG
CREATE INDEX idx_act_oper_log_event_date ON act_oper_activ_log
  (    event_date                      ASC  )
/
CREATE INDEX idx_act_oper_act_log_org_id ON act_oper_activ_log
  (    org_id                          ASC  )
/
CREATE INDEX idx_act_oper_act_log_new_st_id ON act_oper_activ_log
  (    new_state_org_id                ASC  )
/
CREATE INDEX idx_act_oper_activ_log_evntcat ON act_oper_activ_log
  (    event_categ                     ASC  )
/
-- Constraints for ACT_OPER_ACTIV_LOG
ALTER TABLE act_oper_activ_log
ADD CONSTRAINT pk_act_oper_activ_log_id PRIMARY KEY (id)
USING INDEX
/
-- Foreign Key
ALTER TABLE act_oper_activ_log
ADD CONSTRAINT fk_act_activ_oper_list_oper_id FOREIGN KEY (oper_id)
REFERENCES oper_list (id)
/
Запрос: план выполнения для 28.11.2007
SQL> SELECT ol.oper_display_name
  2       , st.wrk_mode
  3       , so.status
  4       , st.categ_name
  5       , count(distinct org.id) AS org_cnt
  6       , count(distinct per.id) AS pers_cnt
  7    FROM act_oper_wrk_stat st
  8       , oper_list ol
  9       , act_oper_activ_log lg
 10       , act_stat_org so
 11       , act_orgnztion org
 12       , act_persons per
 13   WHERE st.oper_id = ol.id
 14     AND st.date_beg >= To_Date('28.11.2007', 'DD.MM.YYYY')
 15     AND nvl(st.date_end, sysdate) < To_Date('28.11.2007', 'DD.MM.YYYY') + 1
 16     AND lg.event_date >= st.date_beg
 17     AND lg.event_date <= nvl(st.date_end, sysdate)
 18     AND lg.oper_id = st.oper_id
 19     AND lg.event_categ = 'ChangeStatusOrgAMR'
 20     AND lg.new_state_org_id = so.id
 21     AND lg.org_id = org.id
 22     AND org.id = per.id_org(+)
 23   GROUP BY ol.oper_display_name, so.status, st.wrk_mode, st.categ_name
 24   ORDER BY 1, 4, 2, 3;

160 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=183 Card=1 Bytes=196)
   1    0   SORT (GROUP BY) (Cost=183 Card=1 Bytes=196)
   2    1     NESTED LOOPS (OUTER) (Cost=182 Card=1 Bytes=196)
   3    2       NESTED LOOPS (Cost=179 Card=1 Bytes=184)
   4    3         NESTED LOOPS (Cost=178 Card=1 Bytes=178)
   5    4           NESTED LOOPS (Cost=177 Card=1 Bytes=141)
   6    5             NESTED LOOPS (Cost=12 Card=1 Bytes=104)
   7    6               TABLE ACCESS (BY INDEX ROWID) OF 'ACT_OPER_WRK_STAT' (Cost=11 Card=1 Bytes=69)
   8    7                 INDEX (RANGE SCAN) OF 'IDX_ACT_OPER_WRK_STAT_DATE_BEG' (NON-UNIQUE) (Cost=3 Card=115)
   9    6               TABLE ACCESS (BY INDEX ROWID) OF 'OPER_LIST' (Cost=2 Card=1 Bytes=35)
  10    9                 INDEX (UNIQUE SCAN) OF 'PK_OPER_LIST' (UNIQUE) (Cost=1 Card=1)
  11    5             TABLE ACCESS (BY INDEX ROWID) OF 'ACT_OPER_ACTIV_LOG' (Cost=166 Card=1 Bytes=37)
  12   11               INDEX (RANGE SCAN) OF 'IDX_ACT_OPER_LOG_EVENT_DATE' (NON-UNIQUE) (Cost=162 Card=73)
  13    4           TABLE ACCESS (BY INDEX ROWID) OF 'ACT_STAT_ORG' (Cost=2 Card=1 Bytes=37)
  14   13             INDEX (UNIQUE SCAN) OF 'PK_ACT_STAT_ORG_ID' (UNIQUE) (Cost=1 Card=1)
  15    3         INDEX (UNIQUE SCAN) OF 'PK_ACT_ORGNZTION_ID' (UNIQUE) (Cost=2 Card=1 Bytes=6)
  16    2       TABLE ACCESS (BY INDEX ROWID) OF 'ACT_PERSONS' (Cost=4 Card=1 Bytes=12)
  17   16         INDEX (RANGE SCAN) OF 'IDX_ACT_PERSONS_ID_ORG' (NON-UNIQUE) (Cost=3 Card=1)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      40963  consistent gets
       8787  physical reads
          0  redo size
      10657  bytes sent via SQL*Net to client
        605  bytes received via SQL*Net from client
         24  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
        160  rows processed

SQL>
с датой 29.11.2007
SQL> SELECT ol.oper_display_name
  2       , st.wrk_mode
  3       , so.status
  4       , st.categ_name
  5       , count(distinct org.id) AS org_cnt
  6       , count(distinct per.id) AS pers_cnt
  7    FROM act_oper_wrk_stat st
  8       , oper_list ol
  9       , act_oper_activ_log lg
 10       , act_stat_org so
 11       , act_orgnztion org
 12       , act_persons per
 13   WHERE st.oper_id = ol.id
 14     AND st.date_beg >= To_Date('29.11.2007', 'DD.MM.YYYY')
 15     AND nvl(st.date_end, sysdate) < To_Date('29.11.2007', 'DD.MM.YYYY') + 1
 16     AND lg.event_date >= st.date_beg
 17     AND lg.event_date <= nvl(st.date_end, sysdate)
 18     AND lg.oper_id = st.oper_id
 19     AND lg.event_categ = 'ChangeStatusOrgAMR'
 20     AND lg.new_state_org_id = so.id
 21     AND lg.org_id = org.id
 22     AND org.id = per.id_org(+)
 23   GROUP BY ol.oper_display_name, so.status, st.wrk_mode, st.categ_name
 24   ORDER BY 1, 4, 2, 3;

240 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=183 Card=1 Bytes=196)
   1    0   SORT (GROUP BY) (Cost=183 Card=1 Bytes=196)
   2    1     NESTED LOOPS (OUTER) (Cost=182 Card=1 Bytes=196)
   3    2       NESTED LOOPS (Cost=179 Card=1 Bytes=184)
   4    3         NESTED LOOPS (Cost=178 Card=1 Bytes=178)
   5    4           NESTED LOOPS (Cost=177 Card=1 Bytes=141)
   6    5             NESTED LOOPS (Cost=12 Card=1 Bytes=104)
   7    6               TABLE ACCESS (BY INDEX ROWID) OF 'ACT_OPER_WRK_STAT' (Cost=11 Card=1 Bytes=69)
   8    7                 INDEX (RANGE SCAN) OF 'IDX_ACT_OPER_WRK_STAT_DATE_BEG' (NON-UNIQUE) (Cost=3 Card=115)
   9    6               TABLE ACCESS (BY INDEX ROWID) OF 'OPER_LIST' (Cost=2 Card=1 Bytes=35)
  10    9                 INDEX (UNIQUE SCAN) OF 'PK_OPER_LIST' (UNIQUE) (Cost=1 Card=1)
  11    5             TABLE ACCESS (BY INDEX ROWID) OF 'ACT_OPER_ACTIV_LOG' (Cost=166 Card=1 Bytes=37)
  12   11               INDEX (RANGE SCAN) OF 'IDX_ACT_OPER_LOG_EVENT_DATE' (NON-UNIQUE) (Cost=162 Card=73)
  13    4           TABLE ACCESS (BY INDEX ROWID) OF 'ACT_STAT_ORG' (Cost=2 Card=1 Bytes=37)
  14   13             INDEX (UNIQUE SCAN) OF 'PK_ACT_STAT_ORG_ID' (UNIQUE) (Cost=1 Card=1)
  15    3         INDEX (UNIQUE SCAN) OF 'PK_ACT_ORGNZTION_ID' (UNIQUE) (Cost=2 Card=1 Bytes=6)
  16    2       TABLE ACCESS (BY INDEX ROWID) OF 'ACT_PERSONS' (Cost=4 Card=1 Bytes=12)
  17   16         INDEX (RANGE SCAN) OF 'IDX_ACT_PERSONS_ID_ORG' (NON-UNIQUE) (Cost=3 Card=1)

Statistics
----------------------------------------------------------
          0  recursive calls
          3  db block gets
     641646  consistent gets
      55835  physical reads
          0  redo size
      12862  bytes sent via SQL*Net to client
        660  bytes received via SQL*Net from client
         34  SQL*Net roundtrips to/from client
          0  sorts (memory)
          1  sorts (disk)
        240  rows processed
SQL>
Планы одинаковые - ремя выполнения отличается примерно в 10 раз.

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

Вобще для этой таблицы как правило используется 2 вида запросов:
1) По смене шагов для организаций
2) Вся история по одной организации (к этим запросам претензий по быстродействию нет)

Теперь к вопросу о секционировании.
Как я понимаю, если мы сделаем отдельную партицию для событий 'ChangeStatusOrgAMR', то кол-во блоков при выборке вышеприведенного запроса сократится, потому что в этой партиции других строк просто не будет.
Сам с партициями не работал, поэтому и спрашиваю - насколько реально это поможет и стоит ли овчинка выделки?

2 orawish : за совет с MATVIEW - спасибо, будем думать!