No Postgres 10, o "Particionamento Declarativo" foi introduzido, o que pode aliviar muito o trabalho, como gerar gatilhos ou regras com enormes instruções if/else redirecionando para a tabela correta. O Postgres pode fazer isso automaticamente agora. Vamos começar com a migração:
-
Renomeie a tabela antiga e crie uma nova tabela particionada
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);
Isso dificilmente deve exigir qualquer explicação. A tabela antiga é renomeada (após a migração de dados vamos excluí-la) e obtemos uma tabela mestre para nossa partição que é basicamente a mesma da nossa tabela original, mas sem índices)
-
Crie uma função que possa gerar novas partições conforme precisamos delas:
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;
Isso será útil mais tarde.
-
Crie uma visão que basicamente delegue para nossa tabela mestra:
create or replace view myTable as select * from myTable_master;
-
Crie uma regra para que, quando inserirmos na regra, não apenas atualizemos a tabela particionada, mas também criemos uma nova partição, se necessário:
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) );
Claro, se você também precisar de
update
e delete
, você também precisa de uma regra para aqueles que devem ser diretos. -
Na verdade, migre a tabela antiga:
-- 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;
Agora a migração da tabela está completa sem que houvesse a necessidade de saber quantas partições são necessárias e também a visão
myTable
será absolutamente transparente. Você pode simplesmente inserir e selecionar a partir dessa tabela como antes, mas pode obter o benefício de desempenho do particionamento. Observe que a exibição só é necessária, pois uma tabela particionada não pode ter gatilhos de linha. Se você conseguir chamar
createPartitionIfNotExists
manualmente sempre que necessário do seu código, você não precisa da visualização e todas as suas regras. Nesse caso, você precisa adicionar as partições também manualmente durante a migração: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
$$;