вторник, 26 мая 2015 г.

Хеш секционирование


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

Oracle рекомендует в качестве n значения использовать степень двойки (2, 4, 6, 8, 16 ...)
чтобы данные распределялись между секциями более равномерно.

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


Создадим табличные пространства:

drop tablespace tsp1;
drop tablespace tsp2;
drop tablespace tsp3;
drop tablespace tsp4;
drop tablespace tsp5;
drop tablespace tsp6;
drop tablespace tsp7;
drop tablespace tsp8;
create tablespace tsp1 datafile '+DATA' size 500m reuse
autoextend on next 100m extent management local uniform size 100m;
create tablespace tsp2 datafile '+DATA' size 500m reuse
autoextend on next 100m extent management local autoallocate;
create tablespace tsp3 datafile '+DATA' size 500m reuse
autoextend on next 100m extent management local uniform size 100m;
create tablespace tsp4 datafile '+DATA' size 500m reuse
autoextend on next 100m extent management local autoallocate;
create tablespace tsp5 datafile '+DATA' size 500m reuse
autoextend on next 100m extent management local uniform size 100m;
create tablespace tsp6 datafile '+DATA' size 500m reuse
autoextend on next 100m extent management local autoallocate;
create tablespace tsp7 datafile '+DATA' size 500m reuse
autoextend on next 100m extent management local uniform size 100m;
create tablespace tsp8 datafile '+DATA' size 500m reuse
autoextend on next 100m extent management local autoallocate;


Создадим таблицу t с ключом секционирования по столбцу object_id:

drop table t;
create table t (object_id, owner, object_type, object_name, created)
partition by hash(object_id)
(
partition part_1 tablespace tsp1,
partition part_2 tablespace tsp2,
partition part_3 tablespace tsp3,
partition part_4 tablespace tsp4,
partition part_5 tablespace tsp5,
partition part_6 tablespace tsp6,
partition part_7 tablespace tsp7,
partition part_8 tablespace tsp8
)
as
select object_id, owner, object_type, object_name, created
from all_objects,
         (select rownum as r from dual connect by rownum <=100);



-- секции таблицы t
select partition_name from user_tab_partitions where table_name = 'T';

PART_1
PART_2
PART_3
PART_4
PART_5
PART_6
PART_7
PART_8


-- табличные пространства в которых расположены секции таблицы t
select tablespace_name from user_segments where segment_type = 'TABLE PARTITION' and segment_name = 'T' ;

TSP1
TSP2
TSP3
TSP4
TSP5
TSP6
TSP7
TSP8

-- сколько выделено и сколько свободного пространства в мегабайтах в этих табличных пространствах
select b.tablespace_name,  mbytes_alloc,  mbytes_free
     from ( select round(sum(bytes)/1024/1024) mbytes_free,  tablespace_name
                  from dba_free_space
                group by tablespace_name ) a,
              ( select round(sum(bytes)/1024/1024) mbytes_alloc, tablespace_name
                  from dba_data_files
                group by tablespace_name ) b
 where a.tablespace_name (+) = b.tablespace_name
 and b.tablespace_name in (select tablespace_name from user_segments
                                        where segment_type = 'TABLE PARTITION' and segment_name = 'T');



TSP4    500    427
TSP7    500    300
TSP1    500    300
TSP2    500    427
TSP3    500    300
TSP6    500    427
TSP5    500    300
TSP8    500    427


Секционированные объекты более удобны в обслуживании.
Например вместо того, чтобы перестроить и реорганизовать всю таблицу:

alter table t move;

Можно перестроить каждую секцию по отдельности:

alter table t move partition part_1;
alter table t move partition part_2;
alter table t move partition part_3;
alter table t move partition part_4;
alter table t move partition part_5;
alter table t move partition part_6;
alter table t move partition part_7;
alter table t move partition part_8;


-- или так:

begin
    for x in ( select partition_name  from user_tab_partitions  where table_name = 'T' )
    loop
         execute immediate
         'alter table t move partition ' ||  x.partition_name;
    end loop;
end;
/



Запросы по отдельным секциям можно делать так:

select * from t partition(part_1);

select count(*) from t partition(part_1);


Используя хеш - секционирование, мы тем самым просим Oracle случайно распределять данные,
надеясь при этом, что распределение между секциями будет равномерным:

Посмотрим какое количество строк попало в каждую секцию:

DECLARE
    CURSOR cur IS
    SELECT table_name, partition_name FROM all_tab_partitions WHERE table_name = 'T';
    v_count number:=0;
BEGIN
    FOR rec IN cur
    LOOP
        execute immediate 'select count(*) from '||rec.table_name||' partition('||rec.partition_name||')' into v_count;
        dbms_output.put_line('The Partition '||rec.partition_name||' of table '||rec.table_name||' has '||v_count||' rows');
    END LOOP;
END;


The Partition PART_1 of table T has 1119500 rows
The Partition PART_2 of table T has 1118400 rows
The Partition PART_3 of table T has 1120800 rows
The Partition PART_4 of table T has 1109900 rows
The Partition PART_5 of table T has 1098900 rows
The Partition PART_6 of table T has 1097300 rows
The Partition PART_7 of table T has 1122100 rows
The Partition PART_8 of table T has 1102400 rows



Секционирование также может повысить доступность данных.
Например если произойдет повреждения диска, который содержал данные одной из секций,
(симитировать можно так alter tablespace  p1 offline; )
то вся база будет доступна и в рабочем состоянии не будет доступна только одна секция.

Причем одну секцию можно восстановить намного быстрее, чем всю таблицу.

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