V Postgres 10 bylo představeno „Deklarativní rozdělení“, které vás může zbavit spousty práce, jako je generování spouštěčů nebo pravidel s obrovskými příkazy if/else přesměrováním na správnou tabulku. Postgres to nyní umí automaticky. Začněme s migrací:
-
Přejmenujte starou tabulku a vytvořte novou rozdělenou tabulku
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);
To by snad nemělo vyžadovat žádné vysvětlení. Stará tabulka je přejmenována (po migraci dat ji smažeme) a získáme hlavní tabulku pro náš oddíl, která je v podstatě stejná jako naše původní tabulka, ale bez indexů)
-
Vytvořte funkci, která dokáže generovat nové oddíly, jak je potřebujeme:
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;
To se bude hodit později.
-
Vytvořte pohled, který v podstatě pouze deleguje naši hlavní tabulku:
create or replace view myTable as select * from myTable_master;
-
Vytvořte pravidlo, abychom po vložení do pravidla nejen aktualizovali rozdělenou tabulku, ale v případě potřeby také vytvořili nový oddíl:
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) );
Samozřejmě, pokud potřebujete také update
a delete
, potřebujete také pravidlo pro ty, které by měly být přímočaré.
-
Ve skutečnosti migrujte starou tabulku:
-- 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;
Nyní je migrace tabulky kompletní, aniž by bylo potřeba vědět, kolik oddílů je potřeba, a také pohled myTable
bude naprosto transparentní. Můžete jednoduše vkládat a vybírat z této tabulky jako dříve, ale můžete získat výhodu výkonu z rozdělení.
Všimněte si, že je potřeba pouze zobrazení, protože dělená tabulka nemůže mít spouštěče řádků. Pokud si vystačíte s voláním createPartitionIfNotExists
ručně kdykoli je to potřeba z vašeho kódu, nepotřebujete zobrazení a všechna jeho pravidla. V tomto případě musíte během migrace přidat oddíly také ručně:
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
$$;