Партиция с существующей таблицы

Есть очень большая таблица. Хочу ускорить селект путем разделения ее на партиции, а точнее существующую таблицу перевести в архивную партицию, а для новых записей создавать свежие партиции. Но проблема в том, что система высоконагруженная и все эти изменения нужно проводить за максимально короткий лок данных. Думал, если партиции разделать по индексному значению это поможет, но практика показала что нет. Может кто в курсе самого оптимального решения?


Ответы (1 шт):

Автор решения: Мелкий

точнее существующую таблицу перевести в архивную партицию, а для новых записей создавать свежие партиции

Такое реально сделать за секундный даунтайм весьма просто. Ключевая хитрость - необходим check constraint, подходящий для условия партицирования.

Предположим, что мы говорим о табличке вида

CREATE TABLE measurement (
     id bigserial NOT NULL primary key,
     created_at timestamp without time zone default now(),
     data bigint NOT NULL
);

Которую хотим партицировать по месяцам по полю created_at.

Для этого нужно:

  1. удостовериться, что поле партицирования исключает значения NULL: (пояснения, пожалуй, не буду перепечатывать из другого ответа)
begin;
set local statement_timeout = '1s';
alter table measurement 
    add constraint measurement_created_at_not_null 
    check (created_at is not null) not valid;
commit;
alter table measurement validate constraint measurement_created_at_not_null;

для версии postgresql начиная с 12 можно поставить штатный set not null. Для более старых версий не надо, это будет долго.

begin;
set local statement_timeout = '1s';
alter table measurement alter column created_at set not null;
alter table measurement 
     drop constraint measurement_created_at_not_null;
commit;
  1. добавить ограничение check на некоторую дату в будущем. До этой даты мы обязуемся завершить миграцию. Либо запись новых данных остановится.
begin;
set local statement_timeout = '1s';
alter table measurement 
    add constraint measurement_old_partition 
    check(created_at < '2022-08-01') not valid;
commit;
alter table measurement validate constraint measurement_old_partition;
  1. в назначенный день где-то до заданной в check ограничении даты делаем фокусы:
begin;
set local statement_timeout to '1s';
alter table measurement rename to measurement_olddata;
CREATE TABLE measurement (
     id bigint NOT NULL,
     created_at timestamp without time zone not null default now(), -- если вы не ставили родной set not null, то и здесь не должно быть not null
     data bigint NOT NULL
) PARTITION BY RANGE (created_at);
ALTER SEQUENCE measurement_id_seq OWNED BY public.measurement.id;
ALTER TABLE ONLY measurement ALTER COLUMN id SET DEFAULT nextval('measurement_id_seq'::regclass);
alter table measurement attach partition measurement_olddata for values from (minvalue) to ('2022-08-01');
commit;

Всё, всем спасибо, все свободны =)

  • если всё сделано верно - операция пройдёт за доли секунды и может выполняться на живом проекте
  • если по таймауту через секунду вылетает поптыка переименования существующей таблицы - вам мешается какая-то транзакция. Смотрите, какие транзакции работают в этой базе и что творческое с ними можно сделать на время замены таблицы
  • если по таймауту через секунду вылетают дальнейшие шаги - что-то сделали неверно. Проверьте, валидны ли check ограничения, не забыли ли про NULL, не перепутали ли даты в check и в attach partition.

Ладно, пояснения по магии выше:

attach partition может использовать валидные check constraint для того, чтобы пропускать проверку данных в таблице во время включения её в дерево партицирования. check constraint, в свою очередь, можно добавить без длительной блокировки, если сперва внести ограничение как not valid, а затем проверить его. validate constraint не блокирует чтение или запись в таблицу.

Поскольку в исходной табличке у нас был bigserial - но это не настоящий тип данных - в определении партицированной таблицы указываем реальный bigint и перепривязываем нижележащий сиквенс корректно к новой таблице.

Само собой, create table головы партицированной таблицы должен совпадать с определение существующей таблицы. pg_dump -s может помочь с получением create table выражения.

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

→ Ссылка