pg_pathman — это расширение Postgres Pro, реализующее оптимизированное решение для секционирования больших и распределённых баз данных. Используя pg_pathman, вы можете:
Секционировать большие базы данных, не прерывая их работу.
Ускорять выполнение запросов с секционированными таблицами.
Управлять существующими и добавлять новые секции на лету.
Добавлять в качестве секций сторонние таблицы.
Соединять секционированные таблицы для операций чтения и записи.
Это расширение совместимо с Postgres Pro 9.5 и новее.
Расширение pg_pathman включено в состав Postgres Pro. Установив Postgres Pro, выполните следующие действия, чтобы подготовить pg_pathman к работе:
Добавьте pg_pathman в переменную shared_preload_libraries в файле postgresql.conf:
shared_preload_libraries = 'pg_pathman'
pg_pathman может конфликтовать с другими расширениями, использующими те же функции для перехвата управления. Например, возможен конфликт pg_pathman с pg_stat_statements, так как оба эти расширения используют функцию ProcessUtility_hook. Если вы столкнулись с проблемой несовместимости, попробуйте поменять порядок библиотек, задаваемых в этом параметре: shared_preload_libraries = 'pg_pathman, pg_stat_statements'
Перезапустите Postgres Pro, чтобы изменения вступили в силу.
Создайте расширение pg_pathman, выполнив следующий запрос:
CREATE EXTENSION pg_pathman;
После того как расширение pg_pathman будет создано, вы можете приступить к секционированию таблиц.
Вы также можете скомпилировать pg_pathman из исходного кода, выполнив следующую команду в каталоге pg_pathman:
make install USE_PGXS=1
Завершив эту операцию, выполните следующие действия для окончания установки.
Также не забудьте дополнительно установить переменную PG_CONFIG, если вы хотите испытать pg_pathman в нестандартной сборке Postgres Pro. Подробнее об этом вы можете прочитать здесь.
Включать/отключать pg_pathman или его определённые узлы можно с помощью переменных GUC. За подробностями обратитесь к Подразделу F.35.5.1.
Если вы хотите полностью отключить pg_pathman для ранее секционированной таблицы, воспользуйтесь функцией disable_pathman_for():
SELECT disable_pathman_for('range_rel');Все секции и данные останутся неизменными и будут обрабатываться стандартным механизмом наследования Postgres Pro.
Если у вас уже была установлена предыдущая версия pg_pathman, выполните следующие действия для установки новой версии:
Установите Postgres Pro.
Перезагрузите ваш сервер Postgres Pro.
Выполните следующие запросы:
ALTER EXTENSION pg_pathman UPDATE TO версия;
SET pg_pathman.enable = t;Здесь версия — это номер версии pg_pathman, например, 1.3.
По мере увеличения базы данных механизмы индексирования могут становиться неэффективными и запросы могут выполняться намного медленнее. Для повышения производительности, улучшения масштабируемости и оптимизации процессов администрирования вы можете использовать секционирование — разделение большой таблицы на множество меньших по размеру, когда все строки размещаются в секциях согласно ключу разбиения. Секционирование в Postgres Pro основано на механизме наследования: каждая секция должна создаваться как дочерняя таблица с ограничением 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 решает эту проблему, применяя оптимизированные алгоритмы планирования и функции секционирования, учитывающие внутреннюю структуру секционированных таблиц. Более подробно детали реализации pg_pathman описаны в Подразделе F.35.4.
Расширение pg_pathman поддерживает следующие стратегии секционирования:
По хешу — строки сопоставляются с секциями с использованием универсальной функции хеширования. Выберите эту стратегию, если в большинстве запросов будет выполняться поиск точного соответствия.
По диапазонам — строки сопоставляются с секциями по диапазонам разбивающего ключа, назначаемым каждой секции. Выберите эту стратегию, если ваша база данных содержит числовые данные, которые, скорее всего, будут представлять интерес как значения в интервалах. Например, вас могут интересовать исторические данные по годам или результаты экспериментов в определённых числовых диапазонах. Для получения выигрыша в производительности pg_pathman использует алгоритм бинарного поиска.
По умолчанию pg_pathman переносит все данные из родительской таблицы в создаваемые секции сразу (производится блокирующее секционирование). При таком подходе вы можете изменить структуру таблицы в одной транзакции, но если объём данных велик, это может привести к приостановке работы. Если важно, чтобы работа не прерывалась, вы можете выполнить параллельное секционирование. В этом случае pg_pathman записывает все новые данные в созданные секции, но сохраняет исходные данные в родительской таблице, пока вы явно не перенесёте их. Это позволяет секционировать большие базы данных, не прерывая работу, так как вы можете выбрать удобное время для переноса данных и переносить их небольшими порциями, не блокируя другие транзакции. Подробнее параллельное секционирование описано в Подразделе F.35.2.2.
Чтобы выполнить секционирование по хешу с применением pg_pathman, воспользуйтесь функцией create_hash_partitions():
create_hash_partitions(relation REGCLASS,
attribute TEXT,
partitions_count INTEGER,
partition_data BOOLEAN DEFAULT TRUE,
partition_names TEXT[] DEFAULT NULL,
tablespaces TEXT[] DEFAULT NULL)Модуль pg_pathman создаёт указанное число секций, используя хеш-функцию. Вы можете также указать имена секций и табличных пространств, задав параметры partition_names и tablespaces, соответственно.
После того, как таблица разделена на секции, удалять или добавлять секции в ней нельзя. Однако при необходимости определённую секцию можно заменить другой таблицей:
replace_hash_partition(old_partition REGCLASS,
new_partition REGCLASS,
lock_parent BOOL DEFAULT TRUE);Если параметр lock_parent равен true, никакие запросы INSERT/UPDATE/ALTER TABLE в родительской таблице не разрешаются.
Если вы опустите необязательный параметр partition_data или зададите для него значение true, все данные из родительской таблицы будут перенесены в секции. Модуль pg_pathman заблокирует эту таблицу для других транзакций до завершения переноса данных. Чтобы избежать приостановки работы, вы можете передать в параметре partition_data значение false и затем вызвать функцию partition_table_concurrently() для переноса данных без блокирования других запросов. За подробностями обратитесь к Подразделу F.35.2.2.
Модуль pg_pathman предоставляет следующие функции для секционирования по диапазонам:
create_range_partitions() — создаёт секции с заданным начальным ключом разбиения и интервалом. Новые секции будут создаваться автоматически при добавлении данных, не попадающих в начальный интервал.
create_partitions_from_range() — создаёт секции для заданного диапазона. Если требуется, вы можете добавлять новые секции вручную, применяя функции управления секциями. За подробностями обратитесь к Подразделу F.35.5.3.4.
Если вы намерены добавлять данные вне существующего диапазона, воспользуйтесь функцией create_range_partitions():
create_range_partitions(relation REGCLASS,
attribute TEXT,
start_value ANYELEMENT,
p_interval ANYELEMENT | INTERVAL,
p_count INTEGER DEFAULT NULL,
partition_data BOOLEAN DEFAULT TRUE)Модуль pg_pathman создаёт секции в зависимости от переданных параметров. Если необязательный параметр p_count опущен, pg_pathman вычисляет требуемое число секций, исходя из заданного начального значения и интервала. Если затем будут добавляться данные вне ранее заданного диапазона, pg_pathman создаст новые секции автоматически, сохраняя указанный интервал. При таком подходе все секции оказываются одного размера, что может способствовать ускорению запросов и упрощению управления данными.
Если текущий диапазон данных, скорее всего, не изменится в будущем, воспользуйтесь функцией create_partitions_from_range():
create_partitions_from_range(relation REGCLASS,
attribute TEXT,
start_value ANYELEMENT,
end_value ANYELEMENT,
p_interval ANYELEMENT | INTERVAL,
partition_data BOOLEAN DEFAULT TRUE)В этом случае pg_pathman не создаёт новые секции автоматически, но вы можете использовать функции управления секциями для добавления секций вручную, если это требуется.
По умолчанию все данные из родительской таблицы будут перенесены в указанное количество секций. Модуль pg_pathman заблокирует эту таблицу для других транзакций до завершения переноса данных. Чтобы избежать приостановки работы, вы можете передать в параметре partition_data значение false и затем вызвать функцию partition_table_concurrently() для переноса данных без блокирования других запросов. За подробностями обратитесь к Подразделу F.35.2.2.
Если важно не допустить прерывания работы, вы можете произвести секционирование в параллельном режиме, установив для параметра partition_data значение false. В этом случае pg_pathman создаст пустые секции и оставит все исходные данные в родительской таблице. При этом все новые записи будут попадать в созданные секции. Позднее вы сможете переместить все начальные данные в соответствующие секции, не блокируя другие запросы, воспользовавшись функцией partition_table_concurrently():
partition_table_concurrently(relation REGCLASS,
batch_size INTEGER DEFAULT 1000,
sleep_time FLOAT8 DEFAULT 1.0)Здесь:
relation — родительская таблица.
batch_size — количество строк, которое должно копироваться из родительской таблицы в секции за один раз. Этот параметр может принимать любое целое значение от 1 до 10000.
sleep_time — интервал времени между попытками переноса данных, в секундах.
Модуль pg_pathman запускает фоновый рабочий процесс для переноса данных из родительской таблицы в секции маленькими порциями, размер которых задаётся параметром batch_size. Если одна или несколько строк в порции оказались заблокированы другими запросами, pg_pathman ждёт заданное время (sleep_time) и повторяет попытку (до 60 раз). За процессом переноса данных можно наблюдать в представлении pathman_concurrent_part_tasks, показывающем количество строк, обработанных на данный момент:
[user]postgres: select * from pathman_concurrent_part_tasks ; userid | pid | dbid | relid | processed | status --------+-------+-------+-------+-----------+--------- user | 20012 | 12413 | test | 334000 | working (1 row)
Если потребуется остановить перенос данных, вы можете в любое время выполнить функцию stop_concurrent_part_task():
SELECT stop_concurrent_part_task(relation REGCLASS);
pg_pathman завершит перенос текущей порции и прекратит процесс переноса.
Когда pg_pathman перенесёт все данные из родительской таблицы, вы можете исключить её из плана запроса. За подробностями обратитесь к описанию функции set_enable_parent().
pg_pathman предоставляет набор функций для простого управления секциями. За подробностями обратитесь к Подразделу F.35.5.3.4.
Так можно получить столбец 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 --------+------+-------+-------+-----------+--------- user | 7367 | 16384 | test | 472000 | working (1 row)
Представление pathman_partition_list в сочетании с drop_range_partition() может использоваться для удаления диапазонных секций более гибким образом по сравнению с обычным 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)
Рассмотрим пример секционирования таблицы по хешу. Для начала создадим таблицу с целочисленным столбцом:
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)
Рассмотрим пример секционирования по диапазонам. Давайте создадим таблицу, содержащую сообщения журнала:
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);Этот запрос создаст 364 секции и переместит в них данные из родительской таблицы.
Новые секции добавляются автоматически триггером 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);Присоединённая таблица должна содержать такие же столбцы, что и секционируемая, за исключением удалённых. Эти столбцы должны иметь те же типы, правила сортировки и характеристики NOT NULL, что и исходные.
Для слияния соседних секций используйте функцию 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)
Расширение pg_pathman сохраняет конфигурацию разбиения в таблице pathman_config; каждая её строка содержит запись для одной секционированной таблицы (название отношения, столбец разбиения и тип секционирования). На этапе инициализации модуль pg_pathman кеширует некоторую информацию дочерних секций в общей памяти, а затем она может использоваться при построении плана. Когда начинает выполняться запрос SELECT, pg_pathman проходит по дереву условий в поиске выражений вида:
ПЕРЕМЕННАЯ ОПЕРАТОР КОНСТАНТА
где ПЕРЕМЕННАЯ — это ключ разбиения, ОПЕРАТОР — оператор сравнения (поддерживаются =, <, <=, >, >=), КОНСТАНТА — скалярное значение. Например:
WHERE id = 150
Затем, учитывая стратегию секционирования и оператор условия, pg_pathman ищет соответствующие секции и строит план.
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)
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 ...)
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 (вложенный цикл) с секционированной таблицей, которая опущена здесь, так как была показана выше.
Узнать больше о нестандартных узлах плана вы можете в блоге Александра Короткова.
Для включения/отключения модуля pg_pathman и отдельных узлов плана предназначены несколько переменных GUC:
pg_pathman.enable — включает (отключает) модуль pg_pathman.
По умолчанию: on (вкл.)
pg_pathman.enable_runtimeappend — включает нестандартный узел RuntimeAppend.
По умолчанию: on (вкл.)
pg_pathman.enable_runtimemergeappend — включает нестандартный узел RuntimeMergeAppend.
По умолчанию: on (вкл.)
pg_pathman.enable_partitionfilter — включает нестандартный узел PartitionFilter.
По умолчанию: on (вкл.)
pg_pathman.enable_auto_partition — включает автоматическое создание секций (в рамках сеанса).
По умолчанию: on (вкл.)
pg_pathman.insert_into_fdw — разрешает использовать операции INSERT с различными обёртками сторонних данных. Возможные значения: disabled (такое использование запрещено), postgres (разрешено для обёртки postgres) и any_fdw (разрешено для любых обёрток).
По умолчанию: postgres
pg_pathman.override_copy — включает/отключает перехват оператора COPY.
По умолчанию: on (вкл.)
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);pathman_config_paramsВ этой таблице хранятся дополнительные параметры, переопределяющие стандартное поведение pg_pathman.
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,
spawn_using_bgw BOOLEAN NOT NULL DEFAULT FALSE);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();pathman_partition_listВ этом представлении показываются все существующие разделы, а также их родители и границы диапазонов (NULL для хеш-секций).
-- вспомогательная функция, возвращающая множество
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();create_hash_partitions(relation REGCLASS,
attribute TEXT,
partitions_count INTEGER,
partition_data BOOLEAN DEFAULT TRUE,
partition_names TEXT[] DEFAULT NULL,
tablespaces TEXT[] DEFAULT NULL)Выполняет секционирование по хешу для таблицы relation по целочисленному ключу attribute. Параметр partitions_count задаёт число создаваемых секций; он не может быть изменён впоследствии. Если параметр partition_data равен true, все данные будут автоматически переноситься из родительской таблицы в секции. Заметьте, что перенос данных может занять некоторое время, и таблица будет заблокирована до завершения транзакции. Если вам нужно перенести данные без блокировки, воспользуйтесь функцией partition_table_concurrently(). Для каждой секции вызывается обработчик создания секции, если он был установлен заранее (см. set_init_callback()).
create_range_partitions(relation REGCLASS,
attribute TEXT,
start_value ANYELEMENT,
p_interval ANYELEMENT,
p_count INTEGER DEFAULT NULL,
partition_data BOOLEAN DEFAULT TRUE)
create_range_partitions(relation REGCLASS,
attribute TEXT,
start_value ANYELEMENT,
p_interval INTERVAL,
p_count INTEGER DEFAULT NULL,
partition_data BOOLEAN DEFAULT TRUE)Выполняет секционирование по диапазонам таблицы relation по ключу разбиения attribute. Аргумент start_value задаёт начальное значение, p_interval — диапазон по умолчанию для автоматически создаваемых секций или секций, создаваемых функциями append_range_partition()/prepend_range_partition() (если в p_interval передаётся NULL, секции не будут создаваться автоматически). В p_count задаётся число заранее создаваемых секций (если оно не задано, то pg_pathman пытается определить это количество по значению attribute). Для каждой секции вызывается обработчик создания секции, если он был установлен заранее.
create_partitions_from_range(relation REGCLASS,
attribute TEXT,
start_value ANYELEMENT,
end_value ANYELEMENT,
p_interval ANYELEMENT,
partition_data BOOLEAN DEFAULT TRUE)
create_partitions_from_range(relation REGCLASS,
attribute TEXT,
start_value ANYELEMENT,
end_value ANYELEMENT,
p_interval INTERVAL,
partition_data BOOLEAN DEFAULT TRUE)Выполняет диапазонное секционирование для заданного диапазона таблицы relation по ключу разбиения attribute. Для каждой секции вызывается обработчик создания секции, если он был установлен заранее.
partition_table_concurrently(relation REGCLASS)
Запускает фоновый рабочий процесс для переноса данных из родительской таблицы в секции. Этот рабочий процесс копирует данные в коротких транзакциях небольшими блоками (до 10000 строк в транзакции) и поэтому не оказывает значительного влияния на работу пользователей.
stop_concurrent_part_task(relation REGCLASS)
Останавливает фоновый рабочий процесс, выполняющий задачу параллельного секционирования. Замечание: рабочий процесс завершается после того, как заканчивает перемещение текущего блока данных.
create_hash_update_trigger(parent REGCLASS)
Создаёт триггер на UPDATE для секций с разбиением по хешу. По умолчанию триггер на UPDATE не создаётся по причине связанных с этим издержек. Но он может быть полезен, когда значение ключевого атрибута меняется.
create_range_update_trigger(parent REGCLASS)
Аналогично предыдущей функции, но для секционирования по хешу.
replace_hash_partition(old_partition REGCLASS,
new_partition REGCLASS,
lock_parent BOOL DEFAULT TRUE)Заменяет заданную секцию таблицы, секционированной по хешу, другой таблицей. Если lock_parent имеет значение true, операции INSERT/UPDATE/ALTER TABLE в родительской таблице не допускаются.
split_range_partition(partition REGCLASS,
value ANYELEMENT,
partition_name TEXT DEFAULT NULL)Разбивает диапазонную секцию partition на две по значению value. Для создаваемой секции вызывается обработчик создания секции, если он задан.
merge_range_partitions(partition1 REGCLASS, partition2 REGCLASS)
Объединяет две смежные диапазонные секции. Данные из partition2 копируются в partition1, после чего partition2 удаляется.
merge_range_partitions(partitions REGCLASS[])
Объединяет несколько смежных диапазонных секций (они должны быть заданы в порядке возрастания или убывания). Все данные будут собраны в первой секции.
append_range_partition(p_relation REGCLASS,
partition_name TEXT DEFAULT NULL,
tablespace TEXT DEFAULT NULL)Добавляет новую диапазонную секцию с интервалом pathman_config.range_interval в конец списка секций.
prepend_range_partition(p_relation REGCLASS,
partition_name TEXT DEFAULT NULL,
tablespace TEXT DEFAULT NULL)Добавляет новую диапазонную секцию с интервалом pathman_config.range_interval в начало списка секций.
add_range_partition(relation REGCLASS,
start_value ANYELEMENT,
end_value ANYELEMENT,
partition_name TEXT DEFAULT NULL,
tablespace TEXT DEFAULT NULL)Добавляет новую диапазонную секцию для таблицы relation с заданными границами диапазона. Если в качестве start_value или end_value передаётся NULL, соответствующая граница диапазона будет бесконечной.
drop_range_partition(partition TEXT, delete_data BOOLEAN DEFAULT TRUE)
Удаляет диапазонную секцию, а также все содержащиеся в ней данные, если установлен флаг delete_data.
attach_range_partition(relation REGCLASS,
partition REGCLASS,
start_value ANYELEMENT,
end_value ANYELEMENT)Присоединяет секцию к существующему отношению с секционированием по диапазонам. Структура присоединяемой таблицы должна в точности повторять структуру родительской, включая удалённые столбцы. Если установлен, вызывается обработчик создания секции (см. pathman_config_params).
detach_range_partition(partition REGCLASS)
Отсоединяет секцию от существующего отношения с секционированием по диапазонам.
disable_pathman_for(relation TEXT)
Полностью отключает механизм секционирования pg_pathman для заданной родительской таблицы и удаляет триггер на добавление, если он существует. При этом все секции и данные остаются без изменений.
drop_partitions(parent REGCLASS,
delete_data BOOLEAN DEFAULT FALSE)Удаляет секции таблицы parent (как сторонние, так и локальные). Если параметр delete_data равен false (это значение по умолчанию), данные сначала копируются в родительскую таблицу.
set_interval(relation REGCLASS, value ANYELEMENT)
Изменяет интервал для таблицы, секционированной по диапазонам. Заметьте, что этот интервал должен быть неотрицательными и он не должен быть пустым, то есть его значение должно быть больше нуля для числовых типов, не меньше 1 микросекунды для типа timestamp и не меньше 1 дня для типа date.
set_enable_parent(relation REGCLASS, value BOOLEAN)
Включает/исключает родительскую таблицу в план запроса. В оригинальном планировщике Postgres Pro родительская таблица всегда включается в план запроса, даже если она пуста, что может повлечь дополнительные издержки. Вы можете исключить родительскую таблицу из рассмотрения, если не собираетесь никогда хранить в ней какие-либо данные. Значение по умолчанию зависит от параметра partition_data, заданного при изначальном создании секций (см. функции create_range_partitions() или create_partitions_from_range()). Если параметр partition_data имел значение true, значит все данные уже были перенесены в секции, и родительская таблица отключается. В противном случае она включена.
set_auto(relation REGCLASS, value BOOLEAN)
Включает/отключает автоматическое создание секций (только для секционирования по диапазонам). По умолчанию этот режим включён.
set_init_callback(relation REGCLASS, callback REGPROC DEFAULT 0)
Устанавливает обработчик создания секции, который будет вызываться для каждой присоединяемой или создаваемой секции (по диапазонам или по хешу). Обработчик должен иметь следующую сигнатуру: 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"
}
set_spawn_using_bgw(relation REGCLASS, value BOOLEAN)
Включает использование SpawnPartitionsWorker для создания новых секций в отдельной транзакции в случае добавления данных за пределами диапазона разбиения.
Ильдар Мусин <i.musin@postgrespro.ru>, Postgres Professional, Россия
Александр Коротков <a.korotkov@postgrespro.ru>, Postgres Professional, Россия
Дмитрий Иванов <d.ivanov@postgrespro.ru>, Postgres Professional, Россия