Содержание

Расширение диспетчера разделов PostgreSQL (`pg_partman`)

PostgreSQL Partition Manager — это расширение, упрощающее управление секционированием таблиц по времени или серийным номерам. У него много опций, но обычно нужны лишь некоторые, поэтому его гораздо проще использовать, чем может показаться (и определенно проще, чем реализовать самому). В настоящее время триггерные функции обрабатывают только вставки в родительскую таблицу. Обновления, которые перемещают значение из одного раздела в другой, пока не поддерживаются. Некоторые возможности этого расширения были расширены в блоге автора - http://www.keithf4.com/tag/pg_partman

Если вы попытаетесь вставить данные в набор разделов, содержащий данные для несуществующего раздела, эти данные будут помещены в родительскую таблицу набора. Это предпочтительнее автоматического создания новых разделов для соответствия этим данным, поскольку ошибка, приводящая к вставке неразделенных данных, может привести к созданию множества нежелательных дочерних таблиц. Функция check_parent() обеспечивает мониторинг любых данных, вставляемых в родительские элементы, а набор функций partition_data_* может легко разбить эти данные для вас, если они являются допустимыми. Это намного проще, чем очищать потенциально сотни или тысячи ненужных разделов. А также лучше, чем выкидывать ошибку и терять данные!

Обратите внимание, что будущее создание дочерней таблицы основано на данных, которые в настоящее время находятся в разделе, установленном как для временного, так и для последовательного разделения. Это означает, что если вы поместите «будущие» данные в набор разделов, вновь создаваемые таблицы будут основываться на этом значении. Это может привести к тому, что промежуточные данные перейдут к родительской таблице, как указано выше, если дочерней таблицы не существует. Рекомендуется установить достаточно высокое значение premake, чтобы охватить ожидаемый диапазон вставляемых данных, и значение optimise_trigger, чтобы эффективно обрабатывать наиболее частый диапазон данных. Дополнительные пояснения по этим значениям конфигурации см. ниже.

Если у вас есть существующий набор разделов, и вы хотите перенести его на pg_partman, см. файл migration.md в папке doc.

Наследование свойства дочерней таблицы

Для этого расширения большинство атрибутов дочерних разделов получаются от исходного родителя. Сюда входят значения по умолчанию, индексы (первичные ключи, уникальные, кластеризация и т. д.), внешние ключи (необязательно), табличное пространство, ограничения, привилегии и владение. Это также включает свойства таблицы OID и UNLOGGED. Для управления привилегиями всякий раз, когда создается новый раздел, он будет получать информацию о своих привилегиях и правах собственности из того, что есть у родителя в это время. Привилегии предыдущего раздела не изменяются. Если предыдущие разделы требуют обновления своих привилегий, доступна отдельная функция. Это сохраняется как отдельный процесс из-за того, что это дорогостоящая операция, когда набор разделов становится больше. Значения по умолчанию, индексы, табличное пространство и ограничения для родителя применяются только к вновь созданным разделам и не устанавливаются задним числом для уже существующих разделов. Хотя обычно вы не создаете индексы для родителя набора разделов, в данном случае это значительно упрощает управление. В родительской таблице не будет данных (если все работает правильно), поэтому они не будут занимать место и не будут влиять на производительность системы. Использование родительской таблицы в качестве элемента управления данными дочерних таблиц, как это, дает лучшее место для управления вещами, которые немного более естественны, чем таблица конфигурации или использование функций настройки.

Разбиение на секции

Разбиение на секции с несколькими уровнями поддерживается. Вы можете сделать время-время, id-id, time-id и id-time. Не существует установленного ограничения на уровень разбиения, которое вы можете сделать, но будьте благоразумны и помните о соображениях производительности при управлении многими таблицами в одном наборе наследования. Кроме того, если количество таблиц в одном наборе разделов становится очень большим, вам, возможно, придется настроить параметр max_locks_per_transaction postgresql.conf выше значения по умолчанию, равного 64. В противном случае вы можете столкнуться с проблемами с общей памятью или даже привести к сбою кластера. По умолчанию все наборы подразделов требуют run_maintenance() для создания новых разделов. Одноуровневые наборы разделов, основанные на времени, уже делают это, но одноуровневые последовательные наборы этого не делают. Если у вас возникают конфликты, когда run_maintenance() вызывается для общего обслуживания всех наборов разделов, вы можете установить для столбца use_run_maintenance в таблице part_config значение false, если вы не хотите, чтобы этот общий вызов управлял вашим набором подразделов. Но затем вы должны вызывать run_maintenance(parent_table) напрямую и достаточно часто, чтобы создавать разделы в будущем. Дополнительные сведения см. в функциях create_parent_sub() и run_maintenance() ниже.

Ограничения при секционировании

Наименьший поддерживаемый интервал составляет 1 секунду, а верхний предел ограничен минимальным и максимальным значениями метки времени, поддерживаемыми PostgreSQL (http://www.postgresql.org/docs/current/static/datatype-datetime.html).

При первом запуске create_parent() для создания набора разделов интервалы менее дня округляются в меньшую сторону при определении того, какой раздел будет создан первым. Интервалы менее 24 часов, но более 1 минуты используют ближайший час с округлением в меньшую сторону. Интервалы менее 1 минуты округляются до ближайшей минуты. Однако будет создано достаточно разделов, чтобы поддерживать реальное текущее время. Это означает, что при запуске create_parent() может быть создано больше предыдущих разделов, чем ожидалось, и некоторые будущие разделы не будут созданы. Первый запуск run_maintenance() исправит этот недостаток. Это происходит из-за возможности поддержки настраиваемых интервалов времени. Любые интервалы, превышающие или равные 24 часам, должны привести к ожидаемому результату.

Имейте в виду, что для интервалов, равных или превышающих 100 лет, расширение будет использовать реальное начало века или тысячелетия для определения имени раздела и правил ограничений. Например, 21 век и 3 тысячелетие начались 1 января 2001 года (а не 2000 года). Это также означает, что нет года «0». Слишком сложно попытаться обойти это и сделать хорошие «ровные» имена разделов и правила для обработки всех возможных периодов времени, которые могут понадобиться людям. Во всем виноваты создатели григорианского календаря.

Ограничения длины имени

PostgreSQL имеет ограничение длины имени объекта в 63 символа. Если вы попытаетесь создать объект с более длинным именем, он обрежет все символы в конце, чтобы соответствовать этому ограничению. Это может вызвать очевидные проблемы с именами разделов, которые зависят от наличия определенного суффикса имени. PG Partman автоматически обрабатывает это для всех дочерних таблиц, триггерных функций и триггеров. Он урежет существующее имя родительской таблицы, чтобы оно соответствовало требуемому суффиксу. Имейте в виду, что если у вас есть таблицы с очень длинными похожими именами, вы можете столкнуться с конфликтами имен, если они являются частью отдельных наборов разделов. Имейте в виду, что при последовательном секционировании со временем имя таблицы будет усекаться все больше и больше, чтобы соответствовать более длинному суффиксу раздела. Поэтому, хотя расширение попытается обработать этот пограничный случай за вас, рекомендуется, чтобы имена таблиц, которые будут секционированы, были как можно короче.

Уникальные ограничения и Upsert

Наследование таблиц в PostgreSQL не позволяет применять первичный ключ или уникальный индекс/ограничение родительской таблицы ко всем дочерним таблицам. Ограничение применяется к каждой отдельной таблице, но не ко всему набору разделов в целом. Например, это означает, что неосторожное приложение может привести к дублированию значения первичного ключа в наборе разделов. Это одна из «больших проблем», вызывающая проблемы с производительностью при разбиении на разделы в других системах баз данных, и одна из причин задержки внедрения разбиения на разделы, встроенного в PostgreSQL. В то же время в pg_partman включен скрипт Python, который может обеспечить мониторинг, чтобы гарантировать, что отсутствие этой функции не причинит долгосрочного вреда. См. check_unique_constraint.py в разделе «Скрипты».

INSERT … ON CONFLICT (upsert) поддерживается в триггере разделения, но очень ограничено. Основные ограничения заключаются в том, что нарушения ограничений, вызывающие срабатывание предложения ON CONFLICT, происходят только в отдельных дочерних таблицах, которые фактически содержат данные, по причинам, описанным выше. Более серьезной проблемой, чем дублирование данных, является предложение ON CONFLICT DO UPDATE, которое может не срабатывать и приводить к крайне несогласованным данным, если его не учитывать. Неясно, удастся ли преодолеть это ограничение, если разделение основано на наследовании и триггерах. В ситуациях, когда вставляются только новые данные, upsert может значительно повысить производительность. Однако, если вы полагаетесь на данные в старых разделах, чтобы вызвать нарушение ограничений, которое обычно обрабатывает upsert, это, скорее всего, не сработает. Кроме того, если результирующее UPDATE в конечном итоге нарушит ограничение разделения этой таблицы chld, оно завершится ошибкой. В настоящее время pg_partman не поддерживает ОБНОВЛЕНИЯ, требующие перемещения строки из одной дочерней таблицы в другую.

Upsert является необязательным, отключенным по умолчанию, и рекомендуется тщательно протестировать его перед внедрением в производство и внимательно следить за ним. См. https://www.postgresql.org/docs/9.5/static/sql-insert.html.

Logging/Monitoring

Расширение PG Jobmon (https://github.com/omniti-labs/pg_jobmon) является необязательным и позволяет проводить аудит и мониторинг обслуживания разделов. Если jobmon установлен и настроен правильно, он будет автоматически использоваться partman без дополнительной настройки. Jobmon также можно включать и выключать отдельно для каждого набора разделов, используя столбец jobmon в таблице part_config или с помощью параметра create_parent() во время первоначальной настройки. Обратите внимание, что если вы пытаетесь разбить таблицы pg_jobmon, вы ДОЛЖНЫ установить для опции create_parent() значение false, иначе она будет переведена в постоянную блокировку, поскольку pg_jobmon будет пытаться записывать в таблицу, которую он пытается разбить. По умолчанию любая функция, которая не может успешно запуститься 3 раза подряд, вызовет предупреждение jobmon. Вот почему значение pre-make по умолчанию установлено равным 4, так что предупреждение будет выдано вовремя для вмешательства без необходимости дополнительной настройки jobmon. Конечно, вы можете настроить jobmon для оповещения до (или позже) 3 сбоев, если это необходимо. Если вы используете partman в производственной среде, НАСТОЯТЕЛЬНО рекомендуется установить jobmon и настроить какой-либо сторонний мониторинг для оповещения в случае сбоя разделения (Nagios, Circonus и т. д.).

Background Worker

В PostgreSQL 9.4 появилась возможность создавать собственные фоновые рабочие процессы и динамически загружать их во время выполнения. BGW pg_partman — это, по сути, просто планировщик, который запускает для вас функцию run_maintenance(), поэтому вам не нужно использовать внешний планировщик (cron и т. д.). Прямо сейчас он ничем не отличается от прямого вызова run_maintenance(), но это может измениться в будущем. Инструкции по установке см. в файле README.md. Если вам нужно вызвать run_maintenance() непосредственно для каких-либо конкретных наборов разделов, вам все равно придется делать это вручную с помощью внешнего планировщика. Это поддерживает только те наборы разделов, для которых use_run_maintenance в **part_config** имеет значение true. Сообщения LOG выводятся в обычный файл журнала PostgreSQL, чтобы указать, когда запускается BGW. Дополнительные сообщения журнала доступны, если для параметра log_min_messages установлено значение «DEBUG1».

Следующие параметры конфигурации доступны для добавления в postgresql.conf для управления процессом BGW:

Объекты расширения

Для запуска всех этих функций необходимо использовать суперпользователя, чтобы правильно установить привилегии и права собственности во всех случаях. Все они устанавливаются с помощью SECURITY DEFINER, поэтому, если вы не можете использовать суперпользователя, просто назначьте роль суперпользователя в качестве владельца.

В качестве примечания для людей, которые не были в курсе, вы можете называть аргументы в вызовах функций, чтобы упростить их вызов и избежать путаницы, когда есть много возможных аргументов. Если для значения указано значение по умолчанию, передавать значение этому аргументу не требуется. Например: SELECT create_parent('schema.table', 'col1', 'time', 'daily', p_start_partition := '2015-10-20');

Creation Functions

create_parent(
    p_parent_table text, 
    p_control text, 
    p_type text, 
    p_interval text, 
    p_constraint_cols text[] DEFAULT NULL, 
    p_premake INT DEFAULT 4, 
    p_use_run_maintenance BOOLEAN DEFAULT NULL, 
    p_start_partition text DEFAULT NULL, 
    p_inherit_fk BOOLEAN DEFAULT TRUE, 
    p_epoch BOOLEAN DEFAULT 'none', 
    p_upsert text DEFAULT '', 
    p_trigger_return_null BOOLEAN DEFAULT TRUE, 
    p_jobmon BOOLEAN DEFAULT TRUE, 
    p_debug BOOLEAN DEFAULT FALSE)
create_sub_parent(
    p_top_parent text, 
    p_control text, 
    p_type text, 
    p_interval text, 
    p_constraint_cols text[] DEFAULT NULL, 
    p_premake INT DEFAULT 4, 
    p_start_partition text DEFAULT NULL, 
    p_inherit_fk BOOLEAN DEFAULT TRUE, 
    p_epoch BOOLEAN DEFAULT 'none', 
    p_jobmon BOOLEAN DEFAULT TRUE, 
    p_debug BOOLEAN DEFAULT FALSE) 
RETURNS BOOLEAN;
partition_data_time(
    p_parent_table text, 
    p_batch_count INT DEFAULT 1, 
    p_batch_interval INTERVAL DEFAULT NULL, 
    p_lock_wait NUMERIC DEFAULT 0, 
    p_order text DEFAULT 'ASC', 
    p_analyze BOOLEAN DEFAULT TRUE
    ) RETURNS BIGINT
partition_data_id(
    p_parent_table text, 
    p_batch_count INT DEFAULT 1, 
    p_batch_interval INT DEFAULT NULL, 
    p_lock_wait NUMERIC DEFAULT 0, 
    p_order text DEFAULT 'ASC') RETURNS BIGINT

Функции обслуживания

run_maintenance(
    p_parent_table text DEFAULT NULL, 
    p_analyze BOOLEAN DEFAULT TRUE, 
    p_jobmon BOOLEAN DEFAULT TRUE, 
    p_debug BOOLEAN DEFAULT FALSE) RETURNS void
show_partitions (
    p_parent_table text, 
    p_order text DEFAULT 'ASC')
show_partition_name(
    p_parent_table text, 
    p_value text, 
    OUT partition_table text, 
    OUT suffix_timestamp TIMESTAMP, 
    OUT suffix_id BIGINT, 
    OUT table_exists BOOLEAN)
check_parent(p_exact_count BOOLEAN DEFAULT TRUE)
apply_constraints(
    p_parent_table text, 
    p_child_table text DEFAULT NULL, 
    p_job_id BIGINT DEFAULT NULL, 
    p_debug BOOLEAN DEFAULT FALSE)
drop_constraints(
    p_parent_table text, 
    p_child_table text, 
    p_debug BOOLEAN DEFAULT FALSE)
reapply_privileges(p_parent_table text)
apply_foreign_keys(
    p_parent_table text, 
    p_child_table text DEFAULT NULL, 
    p_job_id BIGINT DEFAULT NULL, 
    p_debug BOOLEAN DEFAULT FALSE)

Функции разрушения

undo_partition_time(
    p_parent_table text, 
    p_batch_count INT DEFAULT 1, 
    p_batch_interval INTERVAL DEFAULT NULL, 
    p_keep_table BOOLEAN DEFAULT TRUE, 
    p_lock_wait NUMERIC DEFAULT 0) RETURNS BIGINT
undo_partition_id(
    p_parent_table text, 
    p_batch_count INT DEFAULT 1, 
    p_batch_interval BIGINT DEFAULT NULL, 
    p_keep_table BOOLEAN DEFAULT TRUE, 
    p_lock_wait NUMERIC DEFAULT 0) RETURNS BIGINT
undo_partition(
    p_parent_table text, 
    p_batch_count INT DEFAULT 1, 
    p_keep_table BOOLEAN DEFAULT TRUE, 
    p_jobmon BOOLEAN DEFAULT TRUE, 
    p_lock_wait NUMERIC DEFAULT 0) RETURNS BIGINT
drop_partition_time(
    p_parent_table text, 
    p_retention INTERVAL DEFAULT NULL, 
    p_keep_table BOOLEAN DEFAULT NULL, 
    p_keep_index BOOLEAN DEFAULT NULL, 
    p_retention_schema text DEFAULT NULL) RETURNS INT
drop_partition_id(
    p_parent_table text, 
    p_retention BIGINT DEFAULT NULL, 
    p_keep_table BOOLEAN DEFAULT NULL, 
    p_keep_index BOOLEAN DEFAULT NULL, 
    p_retention_schema text DEFAULT NULL) RETURNS INT
drop_partition_column(
    p_parent_table text, 
    p_column text) RETURNS void

Столы

part_config

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

  1. retention, retention_schema, retention_keep_table и retention_keep_index для настройки политики хранения набора разделов.
  2. constraint_cols чтобы partman управлял дополнительными ограничениями и optimize_constraint контролировал их добавление
  3. premake, optimize_trigger, inherit_fk, use_run_maintenance , jobmon чтобы изменить поведение по умолчанию.

Остальные управляются самим расширением и не должны изменяться без крайней необходимости.

part_config_sub

Скрипты

Если расширение было установлено с помощью make , указанные ниже файлы сценариев должны были быть установлены в каталог двоичных файлов PostgreSQL.

partition_data.py

undo_partition.py

dump_partition.py

vacuum_maintenance.py

reapply_indexes.py

reapply_constraints.py* Сценарий Python для переопределения ограничений для дочерних таблиц в заданном наборе разделов для столбцов, настроенных в таблице part_config . * Обычно используется режим -d для удаления ограничений, редактирования данных по мере необходимости, а затем режим -a для повторного применения ограничений. * –parent (-p): Родительская таблица уже созданного набора разделов. (Обязательно) * –connection (-c): Строка подключения для использования psycopg. По умолчанию «host=» (локальный сокет). * –drop_constraints (-d): удалить все ограничения, управляемые pg_partman. Сбрасывает ограничения для ВСЕХ дочерних таблиц в наборе разделов. * –add_constraints (-a): Применить ограничения для сконфигурированных столбцов ко всем дочерним таблицам старше предварительно созданного значения. *–jobs (-j): Используйте библиотеку многопроцессорной обработки python для параллельного воссоздания индексов. Значение для -j — это количество одновременно выполняемых заданий. Обратите внимание, что это для таблицы, а не для индекса. Будьте очень осторожны, устанавливая этот параметр, если нагрузка на ваши системы вызывает беспокойство. * –wait (-w): подождите заданное количество секунд после того, как для таблицы были сняты или применены ограничения, прежде чем переходить к следующей. При использовании с -j вместо этого будет установлена ​​пауза между пакетами параллельных заданий. * –dryrun: Показать, что будет делать скрипт, не запуская его в базе данных. Настоятельно рекомендуем ознакомиться с этим перед запуском. * –quiet (-q): Выключить весь вывод. * –version: Распечатайте минимальную версию pg_partmanэтого скрипта, с которой он должен работать. Установленная версия pg_partmanможет быть больше этой.

reapply_foreign_keys.py

check_unique_constraints.py

Пример руководства по настройке секционирования на основе триггера