Как разделить большую таблицу в PostgreSQL 11 на несколько частей (партиций)?

Как можно разделить очень большую таблицу размером 5TB на партиции без простоя в бэкенде при записи данных? Я хочу иметь возможность удалить одну партицию размером в 5TB в будущем, вместо того чтобы удалять записи данных по частям в течение долгих дней.
  • 19 октября 2023 г. 19:58
Ответы на вопрос 2
Для разделения большой таблицы на партиции в PostgreSQL 11 вы можете использовать разделение по диапазону или по списку. Ниже приведены шаги по созданию разделенной таблицы без простоя в бэкенде при записи данных:

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

```sql
CREATE TABLE my_table (
    id SERIAL PRIMARY KEY,
    data TEXT,
    created_at TIMESTAMP
);
```

2. Создайте партиции, которые будут хранить фактические данные. Например, для разделения по диапазонам вы можете использовать функцию `CREATE TABLE ... PARTITION BY RANGE`:

```sql
CREATE TABLE my_table_2019 PARTITION OF my_table
    FOR VALUES FROM ('2019-01-01') TO ('2020-01-01');

CREATE TABLE my_table_2020 PARTITION OF my_table
    FOR VALUES FROM ('2020-01-01') TO ('2021-01-01');

-- и так далее для каждого года
```

3. Индексируйте поле, по которому будет производиться разделение данных. В большинстве случаев это поле будет полем даты.

```sql
CREATE INDEX idx_created_at ON my_table (created_at);
```

4. Настройте триггеры, чтобы данные автоматически переносились в соответствующие партиции. Например, для разделения по диапазонам вы можете использовать функцию `FOR EACH ROW ... EXECUTE FUNCTION`.

```sql
CREATE OR REPLACE FUNCTION my_table_insert_trigger()
    RETURNS TRIGGER AS $$
BEGIN
    IF NEW.created_at >= '2019-01-01' AND NEW.created_at < '2020-01-01' THEN
        INSERT INTO my_table_2019 VALUES (NEW.*);
    ELSEIF NEW.created_at >= '2020-01-01' AND NEW.created_at < '2021-01-01' THEN
        INSERT INTO my_table_2020 VALUES (NEW.*);
    -- и так далее для каждой партиции
    END IF;
    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER my_table_insert_trigger
    BEFORE INSERT ON my_table
    FOR EACH ROW EXECUTE FUNCTION my_table_insert_trigger();
```

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

Обратите внимание, что перед размером таблицы в 5TB вам может потребоваться настроить правильное разбиение по диапазону или списку, чтобы обеспечить равномерное распределение данных по партициям. Важно также учитывать производительность и доступность данных при выборе метода разделения и оптимизации запросов.
если clock представляет собой unixtime и мы хотим использовать его для будущего разделения данных: 
начало;
установить локальный таймаут запроса на '100ms';
изменить таблицу history, добавить ограничение partition_key проверки (clock < :N), но пока не действительное;
подтвердить;
проверить таблицу history, ограничение partition_key;
начало;
установить локальный таймаут запроса на '100ms';
переименовать таблицу history в history_old_data;
создать таблицу history (...) с разбиением по диапазону (clock);
прикрепить к таблице history раздел history_old_data для значений от (minvalue) до (:N);
создать таблицу history_part_... как раздел таблицы history от значения (:N) до (...);
подтвердить;
:N - это некая дата в будущем, до которой данные будут записываться в старую таблицу history_old_data в размере 5тб, а после этой даты - в новый раздел. Если не успеете завершить миграцию до выбранной :N, то удалите ограничение проверки, в противном случае запись будет заблокирована.
Похожие вопросы