F.31. pg_pathman

Модуль pg_pathman предоставляет оптимизированный механизм секционирования, а также функции для создания и управления секциями.

Это расширение совместимо с Postgres Pro 9.5 и 9.6.

F.31.1. Обзор

Секционирование — это способ разбиения одной большой таблицы на множество меньших по размеру. Каждая строка в такой таблице помещается в отдельную секцию, согласно ключу разбиения. Секционирование в PostgreSQL основано на механизме наследования: каждая секция должна создаваться как дочерняя таблица с ограничением CHECK. Например:

CREATE TABLE test (id SERIAL PRIMARY KEY, title TEXT);
CREATE TABLE test_1 (CHECK ( id >= 100 AND id < 200 )) INHERITS (test);
CREATE TABLE test_2 (CHECK ( id >= 200 AND id < 300 )) INHERITS (test);

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

Модуль pg_pathman предоставляет функции для создания и управления секциями, а также механизм секционирования, оптимизированный с учётом знаний о структуре секций. Он сохраняет конфигурацию разбиения в таблице pathman_config; каждая её строка содержит запись для одной секционированной таблицы (название отношения, столбец разбиения и тип секционирования). На этапе инициализации модуль pg_pathman кеширует некоторую информацию дочерних секций в общей памяти, а затем она может использоваться при построении плана. Начиная выполнять запрос SELECT, pg_pathman проходит по дереву условий в поиске выражений вида:

ПЕРЕМЕННАЯ ОПЕРАТОР КОНСТАНТА

где ПЕРЕМЕННАЯ — это ключ разбиения, ОПЕРАТОР — оператор сравнения (поддерживаются =, <, <=, >, >=), КОНСТАНТА — скалярное значение. Например:

WHERE id = 150

Затем, учитывая стратегию секционирования и оператор условия, pg_pathman ищет соответствующие секции и строит план. В настоящее время pg_pathman поддерживает два типа секционирования:

  • RANGE — строки сопоставляются с секциями по диапазонам разбивающего ключа, назначаемым каждой секции. Оптимизация реализуется благодаря алгоритму двоичного поиска;

  • HASH — строки сопоставляются с секциями с использованием универсальной функции хеширования.

В будущем ожидается появление и других интересных возможностей. Оставайтесь с нами!

F.31.2. Планы развития

  • Реализация схемы секционирования LIST (по списку значений);

  • Оптимизация соединения по хешу (когда обе таблицы секционированы по ключу соединения).

F.31.3. Руководство по установке

Чтобы установить pg_pathman, выполните в каталоге модуля:

make install USE_PGXS=1

Модифицируйте параметр shared_preload_libraries в postgresql.conf:

shared_preload_libraries = 'pg_pathman'

Обязательно перезапустите экземпляр Postgres Pro. Затем выполните в psql следующий запрос:

CREATE EXTENSION pg_pathman;

Готово! Теперь пора перейти к настройке схемы секционирования.

Важно: Не забывайте установить переменную PG_CONFIG в случае, если вы хотите испытать pg_pathman в нестандартной сборке PostgreSQL. Подробнее об этом вы можете прочитать здесь.

F.31.4. Доступные функции

F.31.4.1. Создание секций

create_hash_partitions(relation         REGCLASS,
                       attribute        TEXT,
                       partitions_count INTEGER,
                       partition_name   TEXT DEFAULT NULL,
                       partition_data   BOOLEAN DEFAULT TRUE)

Выполняет HASH-секционирование для таблицы relation по целочисленному ключу attribute. Параметр partitions_count задаёт число создаваемых секций; он не может быть изменён впоследствии. Если параметр partition_data равен true, все данные будут автоматически копироваться из родительской таблицы в секции. Заметьте, что миграция данных может занять некоторое время, и таблица будет заблокирована до завершения транзакции. Чтобы мигрировать данные, не устанавливая блокировки, воспользуйтесь функцией partition_table_concurrently(). Для каждой секции вызывается обработчик создания секции, если он был установлен заранее (см. set_init_callback()).

create_range_partitions(relation       REGCLASS,
                        attribute      TEXT,
                        start_value    ANYELEMENT,
                        interval       ANYELEMENT,
                        count          INTEGER DEFAULT NULL
                        partition_data BOOLEAN DEFAULT TRUE)

create_range_partitions(relation       REGCLASS,
                        attribute      TEXT,
                        start_value    ANYELEMENT,
                        interval       INTERVAL,
                        count          INTEGER DEFAULT NULL,
                        partition_data BOOLEAN DEFAULT TRUE)

Выполняет RANGE-секционирование таблицы relation по ключу разбиения attribute. Аргумент start_value задаёт начальное значение, interval — диапазон значений внутри одной секции, count — число заранее создаваемых секций (если не задано, то pathman пытается определить это количество на основе значений ключа). Для каждой секции вызывается обработчик создания секции, если он был установлен заранее.

create_partitions_from_range(relation       REGCLASS,
                             attribute      TEXT,
                             start_value    ANYELEMENT,
                             end_value      ANYELEMENT,
                             interval       ANYELEMENT,
                             partition_data BOOLEAN DEFAULT TRUE)

create_partitions_from_range(relation       REGCLASS,
                             attribute      TEXT,
                             start_value    ANYELEMENT,
                             end_value      ANYELEMENT,
                             interval       INTERVAL,
                             partition_data BOOLEAN DEFAULT TRUE)

Выполняет RANGE-секционирование для заданного диапазона таблицы relation по ключу разбиения attribute. Для каждой секции вызывается обработчик создания секции, если он был установлен заранее.

F.31.4.2. Миграция данных

partition_table_concurrently(relation REGCLASS)

Запускает фоновый рабочий процесс для переноса данных из родительской таблицы в секции. Этот рабочий процесс копирует данные в коротких транзакциях небольшими блоками (до 10000 строк в транзакции) и поэтому не оказывает значительного влияния на работу пользователей.

stop_concurrent_part_task(relation REGCLASS)

Останавливает фоновый рабочий процесс, выполняющий задачу параллельного секционирования. Замечание: рабочий процесс завершается после того, как заканчивает перемещение текущего блока данных.

F.31.4.3. Триггеры

create_hash_update_trigger(parent REGCLASS)

Создаёт триггер на UPDATE для HASH-секций. По умолчанию триггер на изменение данных не создаётся по причине связанных с этим издержек. Триггер полезен в случаях, когда меняется значение ключевого атрибута.

create_range_update_trigger(parent REGCLASS)

Аналогично предыдущей функции, но для RANGE-секций.

F.31.4.4. Управление секциями после создания

split_range_partition(partition      REGCLASS,
                      value          ANYELEMENT,
                      partition_name TEXT DEFAULT NULL)

Разбивает RANGE-секцию partition на две по значению value. Для создаваемой секции вызывается обработчик создания секции, если он задан.

merge_range_partitions(partition1 REGCLASS, partition2 REGCLASS)

Объединяет две смежные RANGE-секции. Данные из partition2 копируются в partition1, после чего partition2 удаляется.

append_range_partition(p_relation     REGCLASS,
                       partition_name TEXT DEFAULT NULL,
                       tablespace     TEXT DEFAULT NULL)

Добавляет новую RANGE-секцию с диапазоном pathman_config.range_interval в конец списка секций.

prepend_range_partition(p_relation     REGCLASS,
                        partition_name TEXT DEFAULT NULL,
                        tablespace     TEXT DEFAULT NULL)

Добавляет новую RANGE-секцию с диапазоном pathman_config.range_interval в начало списка секций.

add_range_partition(relation       REGCLASS,
                    start_value    ANYELEMENT,
                    end_value      ANYELEMENT,
                    partition_name TEXT DEFAULT NULL,
                    tablespace     TEXT DEFAULT NULL)

Добавляет новую RANGE-секцию для таблицы relation с заданными границами диапазона.

drop_range_partition(partition TEXT, delete_data BOOLEAN DEFAULT TRUE)

Удаляет RANGE-секцию, а также все содержащиеся в ней данные, если установлен флаг delete_data.

attach_range_partition(relation    REGCLASS,
                       partition   REGCLASS,
                       start_value ANYELEMENT,
                       end_value   ANYELEMENT)

Присоединяет секцию к существующему отношению с RANGE-секционированием. Структура присоединяемой таблицы должна в точности повторять структуру родительской, включая удалённые столбцы. Если установлен, вызывается обработчик создания секции (см. pathman_config_params).

detach_range_partition(partition REGCLASS)

Отсоединяет секцию от существующего отношения с RANGE-секционированием.

disable_pathman_for(relation TEXT)

Полностью отключает механизм секционирования pg_pathman для заданной родительской таблицы и удаляет триггер на добавление, если он существует. При этом все секции и данные остаются без изменений.

drop_partitions(parent      REGCLASS,
                delete_data BOOLEAN DEFAULT FALSE)

Удаляет секции таблицы parent (как сторонние, так и локальные). Если параметр delete_data равен false (это значение по умолчанию), данные сначала копируются в родительскую таблицу.

F.31.4.5. Дополнительные параметры

set_enable_parent(relation REGCLASS, value BOOLEAN)

Включает/исключает родительскую таблицу в план запроса. В оригинальном планировщике PostgreSQL родительская таблица всегда включается в план запроса, даже если она пуста, что может повлечь дополнительные издержки. Вы можете исключить родительскую таблицу из рассмотрения, если не собираетесь никогда хранить в ней какие-либо данные. Значение по умолчанию зависит от параметра partition_data, заданного при изначальном создании секций (см. функции create_range_partitions() или create_partitions_from_range()). Если параметр partition_data имел значение true, значит, все данные уже были перенесены в секции, и родительская таблица отключается. В противном случае она включена.

set_auto(relation REGCLASS, value BOOLEAN)

Включает/отключает автоматическое создание секций (только для RANGE-секционирования). По умолчанию этот режим включён.

set_init_callback(relation REGCLASS, callback REGPROC DEFAULT 0)

Устанавливает обработчик создания секции, который будет вызываться для каждой присоединяемой или создаваемой секции (HASH или RANGE). Обработчик должен иметь следующую сигнатуру: part_init_callback(args JSONB) RETURNS VOID. В параметре arg передаётся набор полей, в зависимости от типа секционирования:

/* таблица abc с RANGE-секционированием (потомок abc_4) */
{
    "parent":    "abc",
    "parttype":  "2",
    "partition": "abc_4",
    "range_max": "401",
    "range_min": "301"
}

/* таблица abc с HASH-секционированием (потомок abc_0) */
{
    "parent":    "abc",
    "parttype":  "1",
    "partition": "abc_0"
}

F.31.5. Представления и таблицы

F.31.5.1. pathman_config — основное хранилище конфигурации

CREATE TABLE IF NOT EXISTS pathman_config (
    partrel         REGCLASS NOT NULL PRIMARY KEY,
    attname         TEXT NOT NULL,
    parttype        INTEGER NOT NULL,
    range_interval  TEXT,

    CHECK (parttype IN (1, 2)) /* проверка допустимых типов частей */ );

В этой таблице хранится список секционированных таблиц.

F.31.5.2. pathman_config_params — дополнительные параметры

CREATE TABLE IF NOT EXISTS pathman_config_params (
    partrel        REGCLASS NOT NULL PRIMARY KEY,
    enable_parent  BOOLEAN NOT NULL DEFAULT TRUE,
    auto           BOOLEAN NOT NULL DEFAULT TRUE,
    init_callback  REGPROCEDURE NOT NULL DEFAULT 0);

В этой таблице хранятся дополнительные параметры, переопределяющие стандартное поведение.

F.31.5.3. pathman_concurrent_part_tasks — работающие в данный момент процессы разбиения

-- вспомогательная функция, возвращающая множество
CREATE OR REPLACE FUNCTION show_concurrent_part_tasks()
RETURNS TABLE (
    userid     REGROLE,
    pid        INT,
    dbid       OID,
    relid      REGCLASS,
    processed  INT,
    status     TEXT)
AS 'pg_pathman', 'show_concurrent_part_tasks_internal'
LANGUAGE C STRICT;

CREATE OR REPLACE VIEW pathman_concurrent_part_tasks
AS SELECT * FROM show_concurrent_part_tasks();

В этом представлении показываются все работающие в данный момент задачи параллельного разбиения.

F.31.5.4. pathman_partition_list — список всех существующих секций

-- вспомогательная функция, возвращающая множество
CREATE OR REPLACE FUNCTION show_partition_list()
RETURNS TABLE (
    parent     REGCLASS,
    partition  REGCLASS,
    parttype   INT4,
    partattr   TEXT,
    range_min  TEXT,
    range_max  TEXT)
AS 'pg_pathman', 'show_partition_list_internal'
LANGUAGE C STRICT;

CREATE OR REPLACE VIEW pathman_partition_list
AS SELECT * FROM show_partition_list();

В этом представлении показываются все существующие разделы, а также их родители и границы диапазонов (NULL для HASH-секций).

F.31.6. Дополнительные узлы плана

pg_pathman предоставляет несколько нестандартных узлов плана, позволяющих сократить время выполнения, а именно:

  • RuntimeAppend (переопределяет узел плана Append)

  • RuntimeMergeAppend (переопределяет план узла MergeAppend)

  • PartitionFilter (выполняет роль триггеров INSERT)

PartitionFilter действует как прокси-узел для дочерних узлов INSERT, то есть он может перенаправлять выходные кортежи в соответствующие секции:

EXPLAIN (COSTS OFF)
INSERT INTO partitioned_table
SELECT generate_series(1, 10), random();
               QUERY PLAN
-----------------------------------------
 Insert on partitioned_table
   ->  Custom Scan (PartitionFilter)
         ->  Subquery Scan on "*SELECT*"
               ->  Result
(4 rows)

RuntimeAppend и RuntimeMergeAppend имеют много общего: они оказываются полезными, когда условие WHERE принимает вид:

ПЕРЕМЕННАЯ ОПЕРАТОР ПАРАМЕТР

Подобные выражения уже не могут быть оптимизированы во время планирования, так как значение параметра оказывается неизвестным до стадии выполнения. Решить эту проблему можно, включив процедуру анализа условия WHERE в первоначальный код узла Append, таким образом позволяя ему выбирать только нужные варианты сканирования из всего набора планируемых сканирований. Это по сути сводится к созданию нестандартного узла, способного выполнять такие проверки.

Есть по меньше мере несколько ситуаций, которые демонстрируют полезность таких узлов:

/* создать таблицу, которую мы будем секционировать */
CREATE TABLE partitioned_table(id INT NOT NULL, payload REAL);

/* вставить произвольные данные */
INSERT INTO partitioned_table
SELECT generate_series(1, 1000), random();

/* выполнить секционирование */
SELECT create_hash_partitions('partitioned_table', 'id', 100);

/* создать обычную таблицу */
CREATE TABLE some_table AS SELECT generate_series(1, 100) AS VAL;
    
  • id = (select ... limit 1)

    plpgsql
    EXPLAIN (COSTS OFF, ANALYZE) SELECT * FROM partitioned_table
    WHERE id = (SELECT * FROM some_table LIMIT 1);
                                                 QUERY PLAN
    ----------------------------------------------------------------------------------------------------
     Custom Scan (RuntimeAppend) (actual time=0.030..0.033 rows=1 loops=1)
       InitPlan 1 (returns $0)
         ->  Limit (actual time=0.011..0.011 rows=1 loops=1)
               ->  Seq Scan on some_table (actual time=0.010..0.010 rows=1 loops=1)
       ->  Seq Scan on partitioned_table_70 partitioned_table (actual time=0.004..0.006 rows=1 loops=1)
             Filter: (id = $0)
             Rows Removed by Filter: 9
     Planning time: 1.131 ms
     Execution time: 0.075 ms
    (9 rows)
    
    /* отключить узел RuntimeAppend */
    SET pg_pathman.enable_runtimeappend = f;
    
    EXPLAIN (COSTS OFF, ANALYZE) SELECT * FROM partitioned_table
    WHERE id = (SELECT * FROM some_table LIMIT 1);
                                        QUERY PLAN
    ----------------------------------------------------------------------------------
     Append (actual time=0.196..0.274 rows=1 loops=1)
       InitPlan 1 (returns $0)
         ->  Limit (actual time=0.005..0.005 rows=1 loops=1)
               ->  Seq Scan on some_table (actual time=0.003..0.003 rows=1 loops=1)
       ->  Seq Scan on partitioned_table_0 (actual time=0.014..0.014 rows=0 loops=1)
             Filter: (id = $0)
             Rows Removed by Filter: 6
       ->  Seq Scan on partitioned_table_1 (actual time=0.003..0.003 rows=0 loops=1)
             Filter: (id = $0)
             Rows Removed by Filter: 5
             ... /* more plans follow */
     Planning time: 1.140 ms
     Execution time: 0.855 ms
    (306 rows)
              

  • id = ANY (select ...)

    plpgsql
    EXPLAIN (COSTS OFF, ANALYZE) SELECT * FROM partitioned_table
    WHERE id = any (SELECT * FROM some_table limit 4);
                                                    QUERY PLAN
    -----------------------------------------------------------------------------------------------------------
     Nested Loop (actual time=0.025..0.060 rows=4 loops=1)
       ->  Limit (actual time=0.009..0.011 rows=4 loops=1)
             ->  Seq Scan on some_table (actual time=0.008..0.010 rows=4 loops=1)
       ->  Custom Scan (RuntimeAppend) (actual time=0.002..0.004 rows=1 loops=4)
             ->  Seq Scan on partitioned_table_70 partitioned_table (actual time=0.001..0.001 rows=10 loops=1)
             ->  Seq Scan on partitioned_table_26 partitioned_table (actual time=0.002..0.003 rows=9 loops=1)
             ->  Seq Scan on partitioned_table_27 partitioned_table (actual time=0.001..0.002 rows=20 loops=1)
             ->  Seq Scan on partitioned_table_63 partitioned_table (actual time=0.001..0.002 rows=9 loops=1)
     Planning time: 0.771 ms
     Execution time: 0.101 ms
    (10 rows)
    
    /* отключить узел RuntimeAppend */
    SET pg_pathman.enable_runtimeappend = f;
    
    EXPLAIN (COSTS OFF, ANALYZE) SELECT * FROM partitioned_table
    WHERE id = any (SELECT * FROM some_table limit 4);
                                           QUERY PLAN
    -----------------------------------------------------------------------------------------
     Nested Loop Semi Join (actual time=0.531..1.526 rows=4 loops=1)
       Join Filter: (partitioned_table.id = some_table.val)
       Rows Removed by Join Filter: 3990
       ->  Append (actual time=0.190..0.470 rows=1000 loops=1)
             ->  Seq Scan on partitioned_table (actual time=0.187..0.187 rows=0 loops=1)
             ->  Seq Scan on partitioned_table_0 (actual time=0.002..0.004 rows=6 loops=1)
             ->  Seq Scan on partitioned_table_1 (actual time=0.001..0.001 rows=5 loops=1)
             ->  Seq Scan on partitioned_table_2 (actual time=0.002..0.004 rows=14 loops=1)
    ... /* 96 scans follow */
       ->  Materialize (actual time=0.000..0.000 rows=4 loops=1000)
             ->  Limit (actual time=0.005..0.006 rows=4 loops=1)
                   ->  Seq Scan on some_table (actual time=0.003..0.004 rows=4 loops=1)
     Planning time: 2.169 ms
     Execution time: 2.059 ms
    (110 rows)
              

  • NestLoop (вложенный цикл) с секционированной таблицей, которая опущена здесь, так как была показана выше.

Если вам это интересно, вы можете узнать больше о нестандартных узлах плана в блоге Александра Короткова.

F.31.7. Примеры

F.31.7.1. Общие рекомендации

  • Вы можете легко получить столбец partition, содержащий имена нижележащих секций, воспользовавшись системным атрибутом tableoid:

                SELECT tableoid::regclass AS partition, * FROM partitioned_table;
              
  • Несмотря на то, что индексы в родительской таблице не очень полезны (так как предполагается, что она пуста), они выполняют роль прототипов для создания индексов в секциях. Для каждого индекса в родительской таблице pg_pathman будет создан подобный индекс в каждой секции.

  • Получить список всех текущих задач параллельного разбиения можно в представлении pathman_concurrent_part_tasks:

                SELECT * FROM pathman_concurrent_part_tasks;
                userid  | pid  | dbid  | relid | processed | status  
                --------+------+-------+-------+-----------+---------
                dmitry  | 7367 | 16384 | test  |    472000 | working
                (1 row)
              
  • Функция pathman_partition_list в сочетании drop_range_partition() может использоваться для удаления RANGE-секций более гибким образом по сравнению со старым добрым DROP TABLE:

                SELECT drop_range_partition(partition, false) /* move data to parent */
                FROM pathman_partition_list
                WHERE parent = 'part_test'::regclass AND range_min::int < 500;
                NOTICE:  1 rows copied from part_test_11
                NOTICE:  100 rows copied from part_test_1
                NOTICE:  100 rows copied from part_test_2
                drop_range_partition 
                ----------------------
                dummy_test_11
                dummy_test_1
                dummy_test_2
                (3 rows)
              

F.31.7.2. Секционирование по хешу

Рассмотрим пример HASH-секционирования таблицы. Для начала создадим таблицу с целочисленным столбцом.

        CREATE TABLE items (
        id       SERIAL PRIMARY KEY,
        name     TEXT,
        code     BIGINT);

        INSERT INTO items (id, name, code)
        SELECT g, md5(g::text), random() * 100000
        FROM generate_series(1, 100000) as g;
      

Теперь выполним функцию create_hash_partitions() с подходящими аргументами:

        SELECT create_hash_partitions('items', 'id', 100);
      

Этот запрос создаст новые секции и переместит в них данные из родительской таблицы.

Пример запроса, выполняющего фильтрацию по ключу разбиения:

        SELECT * FROM items WHERE id = 1234;
        id  |               name               | code
        ------+----------------------------------+------
        1234 | 81dc9bdb52d04dc20036dbd8313ed055 | 1855
        (1 row)

        EXPLAIN SELECT * FROM items WHERE id = 1234;
        QUERY PLAN
        ------------------------------------------------------------------------------------
        Append  (cost=0.28..8.29 rows=0 width=0)
        ->  Index Scan using items_34_pkey on items_34  (cost=0.28..8.29 rows=0 width=0)
        Index Cond: (id = 1234)
      

Заметьте, узел Append содержит только одно дочернее сканирование, соответствующее предложению WHERE.

Важно: обратите внимание на тот факт, что pg_pathman исключает родительскую таблицу из плана запроса.

Чтобы обратиться к родительской таблице, используйте модификатор ONLY:

        EXPLAIN SELECT * FROM ONLY items;
        QUERY PLAN
        ------------------------------------------------------
        Seq Scan on items  (cost=0.00..0.00 rows=1 width=45)
      

F.31.7.3. Секционирование по диапазонам

Рассмотрим пример RANGE-секционирования. Давайте создадим таблицу, содержащую сообщения журнала:

        CREATE TABLE journal (
        id      SERIAL,
        dt      TIMESTAMP NOT NULL,
        level   INTEGER,
        msg     TEXT);

        -- подобный индекс будет также создан для каждой секции
        CREATE INDEX ON journal(dt);

        -- сгенерировать некоторые данные
        INSERT INTO journal (dt, level, msg)
        SELECT g, random() * 6, md5(g::text)
        FROM generate_series('2015-01-01'::date, '2015-12-31'::date, '1 minute') as g;
      

Выполним функцию create_range_partitions(), чтобы создать секции, которые будут содержать данные за один день:

        SELECT create_range_partitions('journal', 'dt', '2015-01-01'::date, '1 day'::interval);
      

Этот запрос создаст 365 секций и переместит в них данные из родительской таблицы.

Новые секции добавляются автоматически триггером INSERT, но это можно сделать и вручную, с помощью следующих функций:

        -- добавить новую секцию с заданным диапазоном
        SELECT add_range_partition('journal', '2016-01-01'::date, '2016-01-07'::date);

        -- добавить новую секцию с диапазоном по умолчанию
        SELECT append_range_partition('journal');
      

Первая создаёт новую секцию с заданным диапазоном. Вторая создаёт новую секцию с диапазоном, заданным при первоначальном разбиении, и добавляет её в конец списка секций. Также можно присоединить существующую таблицу в качестве секции. Например, мы можем подключить таблицу архива (это может быть даже сторонняя таблица с другого сервера) с историческими данными:

        CREATE FOREIGN TABLE journal_archive (
        id      INTEGER NOT NULL,
        dt      TIMESTAMP NOT NULL,
        level   INTEGER,
        msg     TEXT)
        SERVER archive_server;

        SELECT attach_range_partition('journal', 'journal_archive', '2014-01-01'::date, '2015-01-01'::date);
      

Важно: определение присоединённой таблицы должно соответствовать определению секционированной таблицы, включая удалённые столбцы.

Для слияния соседних секций используйте функцию merge_range_partitions():

        SELECT merge_range_partitions('journal_archive', 'journal_1');
      

Чтобы разделить секцию по значению, воспользуйтесь функцией split_range_partition():

        SELECT split_range_partition('journal_366', '2016-01-03'::date);
      

Чтобы отсоединить секцию, воспользуйтесь функцией detach_range_partition():

        SELECT detach_range_partition('journal_archive');
      

Пример запроса, выполняющего фильтрацию по ключу разбиения:

        SELECT * FROM journal WHERE dt >= '2015-06-01' AND dt < '2015-06-03';
        id      |         dt          | level |               msg
        --------+---------------------+-------+----------------------------------
        217441  | 2015-06-01 00:00:00 |     2 | 15053892d993ce19f580a128f87e3dbf
        217442  | 2015-06-01 00:01:00 |     1 | 3a7c46f18a952d62ce5418ac2056010c
        217443  | 2015-06-01 00:02:00 |     0 | 92c8de8f82faf0b139a3d99f2792311d
        ...
        (2880 rows)

        EXPLAIN SELECT * FROM journal WHERE dt >= '2015-06-01' AND dt < '2015-06-03';
        QUERY PLAN
        ------------------------------------------------------------------
        Append  (cost=0.00..58.80 rows=0 width=0)
        ->  Seq Scan on journal_152  (cost=0.00..29.40 rows=0 width=0)
        ->  Seq Scan on journal_153  (cost=0.00..29.40 rows=0 width=0)
        (3 rows)
      

F.31.7.4. Отключение pg_pathman

Для включения/отключения всего модуля и отдельных узлов плана предназначены несколько переменных GUC:

  • pg_pathman.enable — отключить (или включить) pg_pathman полностью

  • pg_pathman.enable_runtimeappend — включить дополнительный узел RuntimeAppend

  • pg_pathman.enable_runtimemergeappend — включить дополнительный узел RuntimeMergeAppend

  • pg_pathman.enable_partitionfilter — включить дополнительный узел PartitionFilter

  • pg_pathman.enable_auto_partition — включить автоматическое создание разделов (по сеансам)

  • pg_pathman.insert_into_fdw — разрешить INSERT в различных обёртках сторонних данных (disabled | postgres | any_fdw)

  • pg_pathman.override_copy — включение перехвата оператора COPY

Чтобы полностью отключить pg_pathman для ранее секционированной таблицы, воспользуйтесь функцией disable_pathman_for():

        SELECT disable_pathman_for('range_rel');
      

Все секции и данные останутся неизменными и будут обрабатываться стандартным механизмом секционирования PostgreSQL.

F.31.8. Обратная связь

Вы можете свободно сообщить о проблемах, задать свои вопросы или поделиться идеями на странице проблем проекта.

F.31.9. Авторы

Ильдар Мусин  ООО «Постгрес Профессиональный», Россия
      Александр Коротков  ООО «Постгрес Профессиональный», Россия
      Дмитрий Иванов  ООО «Постгрес Профессиональный», Россия