W Postgresie 10 wprowadzono „Deklaratywne partycjonowanie”, które może odciążyć Cię od wielu prac, takich jak generowanie wyzwalaczy lub reguł z ogromnymi instrukcjami if/else przekierowującymi do właściwej tabeli. Postgres może to teraz zrobić automatycznie. Zacznijmy od migracji:
-
Zmień nazwę starej tabeli i utwórz nową tabelę podzieloną na partycje
alter table myTable rename to myTable_old; create table myTable_master( forDate date not null, key2 int not null, value int not null ) partition by range (forDate);
Nie powinno to wymagać żadnego wyjaśnienia. Nazwa starej tabeli zostaje zmieniona (po migracji danych usuniemy ją) i otrzymujemy główną tabelę dla naszej partycji, która jest zasadniczo taka sama jak nasza oryginalna tabela, ale bez indeksów)
-
Utwórz funkcję, która może generować nowe partycje, gdy ich potrzebujemy:
create function createPartitionIfNotExists(forDate date) returns void as $body$ declare monthStart date := date_trunc('month', forDate); declare monthEndExclusive date := monthStart + interval '1 month'; -- We infer the name of the table from the date that it should contain -- E.g. a date in June 2005 should be int the table mytable_200506: declare tableName text := 'mytable_' || to_char(forDate, 'YYYYmm'); begin -- Check if the table we need for the supplied date exists. -- If it does not exist...: if to_regclass(tableName) is null then -- Generate a new table that acts as a partition for mytable: execute format('create table %I partition of myTable_master for values from (%L) to (%L)', tableName, monthStart, monthEndExclusive); -- Unfortunatelly Postgres forces us to define index for each table individually: execute format('create unique index on %I (forDate, key2)', tableName); end if; end; $body$ language plpgsql;
Przyda się to później.
-
Utwórz widok, który po prostu deleguje do naszej tabeli głównej:
create or replace view myTable as select * from myTable_master;
-
Utwórz regułę, aby po wstawieniu do reguły nie tylko aktualizować partycjonowaną tabelę, ale także tworzyć nową partycję, jeśli zajdzie taka potrzeba:
create or replace rule autoCall_createPartitionIfNotExists as on insert to myTable do instead ( select createPartitionIfNotExists(NEW.forDate); insert into myTable_master (forDate, key2, value) values (NEW.forDate, NEW.key2, NEW.value) );
Oczywiście, jeśli potrzebujesz także update
i delete
, potrzebujesz również reguły dla tych, które powinny być proste.
-
Właściwie migruj starą tabelę:
-- Finally copy the data to our new partitioned table insert into myTable (forDate, key2, value) select * from myTable_old; -- And get rid of the old table drop table myTable_old;
Teraz migracja tabeli jest kompletna bez potrzeby znajomości liczby potrzebnych partycji oraz widoku myTable
będzie absolutnie przejrzysta. Możesz po prostu wstawiać i wybierać z tej tabeli, jak poprzednio, ale możesz uzyskać korzyści w zakresie wydajności dzięki partycjonowaniu.
Należy zauważyć, że widok jest potrzebny tylko, ponieważ tabela podzielona na partycje nie może mieć wyzwalaczy wierszy. Jeśli możesz się dogadać z wywołaniem createPartitionIfNotExists
ręcznie w dowolnym momencie z kodu, nie potrzebujesz widoku i wszystkich jego reguł. W takim przypadku musisz dodać partycje również ręcznie podczas migracji:
do
$$
declare rec record;
begin
-- Loop through all months that exist so far...
for rec in select distinct date_trunc('month', forDate)::date yearmonth from myTable_old loop
-- ... and create a partition for them
perform createPartitionIfNotExists(rec.yearmonth);
end loop;
end
$$;