F.41. pg_partman#

F.41. pg_partman

F.41. pg_partman

pg_partman - это расширение для создания и управления наборами секций таблиц, основанных как на времени, так и на последовательности. Нативное партицирование в Tantor SE 10 поддерживается начиная с pg_partman v3.0.1 и гораздо более широко начиная с 4.0.0 вместе с PostgreSQL 11. Обратите внимание, что все функции партицирования на основе триггера еще не поддерживаются в нативной версии, но производительность значительно выше как при чтении, так и при записи.

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

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

В большинстве случаев фоновый рабочий процесс (BGW) автоматически выполняется для обслуживания секций без необходимости внешнего планировщика (cron и т.д.).

Отчеты об ошибках и запросы на новые функции можно направлять в раздел Issues на Github - https://github.com/pgpartman/pg_partman/issues

Для вопросов, комментариев, или если вы просто не уверены, куда публиковать , пожалуйста, используйте раздел Обсуждения на Github. Вопросы можно публиковать и здесь, независимо от того, насколько незначительной может быть ваша проблема или вопрос - https://github.com/pgpartman/pg_partman/discussions

Если вы все еще пытаетесь оценить, подходит ли партицирование для вашей среды, обратите внимание на проект HypoPG. Версия 2 будет иметь гипотетическую функцию партицирования, которая позволит вам оценить различные схемы партицирования, не требуя от вас фактического партицирования данных. Я могу подумать об интеграции этой функции в pg_partman, как только она станет доступна. - https://hypopg.readthedocs.io

F.41.1. О pg_partman

Версия: 5.0.1

GitHub

F.41.2. INSTALLATION

Рекомендуется:

  • pg_jobmon (>=v1.4.0). PG Job Monitor будет автоматически использоваться, если он установлен и настроен правильно. https://github.com/omniti-labs/pg_jobmon

Фоновый рабочий процесс должен быть загружен при запуске базы данных путем добавления библиотеки в shared_preload_libraries в postgresql.conf

shared_preload_libraries = 'pg_partman_bgw'     # (change requires restart)

Вы также можете установить другие управляющие переменные для BGW в postgresql.conf. dbname требуется как минимум для выполнения обслуживания на указанной базе данных. Эти переменные могут быть добавлены/изменены в любое время с помощью простой перезагрузки. Смотрите документацию для получения дополнительной информации. Пример с некоторыми из них:

pg_partman_bgw.interval = 3600
pg_partman_bgw.role = 'keith'
pg_partman_bgw.dbname = 'keith'

Войдите в Tantor SE и выполните следующие команды. Схема не обязательна (но рекомендуется) и может быть любой, которую вы выберете, но ее нельзя изменить после установки. Если вы используете BGW, кластер базы данных может быть безопасно запущен без создания расширения в настроенной базе данных. Вы можете создать расширение в любое время, и BGW автоматически обнаружит его существование без перезапуска кластера (при условии что shared_preload_libraries был установлен) и начнет выполнять обслуживание в соответствии с настройками.

CREATE SCHEMA partman;
CREATE EXTENSION pg_partman SCHEMA partman;

Начиная с версии 4.1.0, pg_partman больше не требует суперпользователя для работы с нативным партицированием. Для партицирования на основе триггеров все еще требуется суперпользователь, поэтому если вы не хотите использовать суперпользователя, рассмотрите возможность перехода на нативное партицирование. Для установки pg_partman все еще требуется суперпользователь. Рекомендуется создать специальную роль для выполнения функций pg_partman и быть владельцем всех наборов секций, которые поддерживает pg_partman. Как минимум, этой роли потребуются следующие привилегии (предполагая, что pg_partman установлен в схему partman и что эту специальную роль называют partman):

CREATE ROLE partman WITH LOGIN;
GRANT ALL ON SCHEMA partman TO partman;
GRANT ALL ON ALL TABLES IN SCHEMA partman TO partman;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA partman TO partman;
GRANT EXECUTE ON ALL PROCEDURES IN SCHEMA partman TO partman;  -- PG11+ only
GRANT ALL ON SCHEMA my_partition_schema TO partman;
GRANT TEMPORARY ON DATABASE mydb to partman; -- allow creation of temp tables to move data out of default 

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

GRANT CREATE ON DATABASE mydb TO partman;

F.41.3. UPGRADE

Запустите make install так же, как и выше, чтобы поместить скриптовые файлы и библиотеки на свои места. Затем выполните следующее в самом Tantor SE:

ALTER EXTENSION pg_partman UPDATE TO '<latest version>';

Если вы выполняете pg_dump/restore и обновили pg_partman на месте с предыдущих версий, рекомендуется использовать опцию –column-inserts при выгрузке и/или восстановлении таблиц конфигурации pg_partman. Это связано с возможной разной последовательностью столбцов конфигурации (при обновлении столбцы просто добавляются в конец, тогда как по умолчанию при новой установке может быть иначе).

Если вы обновляете любые основные версии pg_partman (2.x -> 3.x и т.д.), пожалуйста, внимательно прочитайте все промежуточные заметки о версиях в CHANGELOG, особенно заметки для основной версии. Зачастую есть дополнительные инструкции (например, обновление триггерных функций) и другие важные моменты для обновлений.

ВАЖНОЕ ЗАМЕЧАНИЕ: Некоторые обновления pg_partman могут требовать удаления и повторного создания своих собственных объектов базы данных. Если вы отменяете общедоступные привилегии для функций/процедур, они могут быть возвращены объектам, которые воссоздаются в рамках обновления. Если требуются ограничения для общедоступного использования pg_partman, рекомендуется установить его в собственную схему, как показано выше, и отменить нежелательный доступ к этой схеме. В противном случае вам может потребоваться добавить дополнительный шаг в процедуры обновления расширения, чтобы снова отменить общедоступный доступ.

F.41.4. EXAMPLES

Для настройки собственной разбивки с помощью pg_partman на совершенно новой таблице или для миграции существующей обычной таблицы на собственную разбивку, см. pg_partman_howto_native.md.

Для переноса таблицы с триггерной партицированием на нативное партицирование с использованием pg_partman, смотрите migrate_to_native.md.

Другие документы HowTo также доступны в папке с документами.

F.41.5. TESTING

Это расширение может использовать набор модульных тестов pgTAP для оценки его корректной работы (http://www.pgtap.org). ВНИМАНИЕ: Вы НЕОБХОДИМО увеличить max_locks_per_transaction выше стандартного значения 64. Для меня, 128 работает хорошо на данный момент. Это связано с тестами субпартиционирования, которые создают/уничтожают несколько сотен таблиц в одной транзакции. Если вы этого не сделаете, вы рискуете сбоем кластера при выполнении тестов субпартиционирования.

F.41.6. Расширение менеджера партицирования Tantor SE (pg_partman)

F.41.7. Краткое описание

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

Начиная с версии 3.0.1, это расширение будет поддерживать нативные методы разделения, которые были введены в PostgreSQL 10. Функция триггера больше не требуется в нативном разделении, но автоматическое создание дочерних таблиц не обрабатывается нативно, и здесь пригодится данное расширение. Версия 4.0.0 добавляет еще больше нативной поддержки для функций, введенных в PG11 (более простое наследование индексов/внешних ключей, секция по умолчанию).

Для ненативного разделения, если вы пытаетесь вставить данные в набор секций, который содержит данные для раздела, которого не существует, эти данные будут помещены в родительскую таблицу набора. Это предпочтительнее, чем автоматическое создание новых секций для соответствия этим данным, поскольку ошибка, вызывающая вставку неразделенных данных, может привести к созданию нежелательных дочерних таблиц и конфликтам из-за транзакционного DDL. Функция check_default() обеспечивает мониторинг вставки данных в родительскую/таблицу по умолчанию, а набор функций partition_data_* может легко разделить эти данные для вас, если они являются допустимыми. Это гораздо проще, чем чистить потенциально сотни или тысячи нежелательных секций. И лучше, чем вызывать ошибку и потерять данные! В нативном разделении вставка данных без соответствующего дочернего раздела вызывает ошибку в PostgreSQL 10. Раздел по умолчанию для нативного разделения доступен только в PostgreSQL 11+.

Обратите внимание, что в дальнейшем создание дочерних таблиц будет основано на данных, находящихся в наборе секций. Это означает, что если вы вводите будущие данные, вновь созданные таблицы будут основаны на этом значении. Это может привести к тому, что промежуточные данные попадут в родительскую/таблицу по умолчанию, как указано выше, если не существует дочерней таблицы. Рекомендуется установить значение premake достаточно высоким, чтобы охватить ожидаемый диапазон вставляемых данных. И для ненативного разделения установите значение optimize_trigger для эффективной обработки наиболее частого диапазона данных. См. ниже дополнительные объяснения этих значений конфигурации.

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

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

Для этого расширения большинство атрибутов дочерних секций получаются из родительской таблицы. Для нативного разделения, основанного на триггерах, все свойства управляются через родительскую таблицу и всегда будут такими. Однако, при нативном разделении некоторые функции не могут быть унаследованы от родительской таблицы в зависимости от версии PostgreSQL. Поэтому pg_partman использует таблицу-шаблон. В следующей таблице показано, как управляются определенные свойства наследования с помощью pg_partman для нативного разделения. Если свойство не указано здесь, предполагается, что оно управляется через родительскую таблицу. Обратите внимание, что если вы обновите основную версию, вам придется соответствующим образом изменить способ управления свойствами, если что-то перейдет от управления через шаблон к управлению реальным родителем (например, внешние ключи с версии 10 на 11+). Свойство WITH OIDS больше не поддерживается официально pg_partman (нативным или не нативным) с релизом PostgreSQL 12, так как оно было удалено.

Функция Наследование родителя Наследование шаблона
несекционный столбец первичного ключа Все
уникальный индекс для несекционного столбца Все
несекционный столбец уникального индекса табличного пространства таблиц Все
незарегистрированная таблица state* Все
неуникальные индексы 11, 12 10
внешние ключи 11, 12 10
табличные пространства 12 10, 11
привилегии/владение Все
ограничения Все
значения по умолчанию Все
публикации 14 10,11,12,13

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

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

Новая функция IDENTITY, введенная в PG10, поддерживается только в таблицах с нативным разделением, и автоматическое создание новых значений последовательности с использованием этой функции поддерживается только при вставке данных через родительскую таблицу, а не напрямую в дочерние таблицы.

ВАЖНО!

  • Функция шаблонной таблицы, используемая в PostgreSQL 10+ для обработки определенных функций, является временным решением, которое помогает ускорить принятие нативного разделения. Так как функции обрабатываются лучше нативно (как в PG11), использование таблицы-шаблона будет быстро исключено из pg_partman. Поэтому, если вы используете эту функцию, рекомендуется заранее готовиться к срочным обновлениям основной версии и внимательно изучать заметки к релизу на наличие изменений.

  • Статус UNLOGGED был перемещен на таблицу-шаблон начиная с v4.2.0 pg_partman. Это связано с несоответствием в обработке свойства при включении или отключении UNLOGGED на родительской таблице набора нативных секций. Это свойство на самом деле не меняется, когда команда ALTER записывается, поэтому новые дочерние таблицы продолжат использовать свойство, которое существовало ранее. Так что если вы хотели изменить набор секций с UNLOGGED на LOGGED для всех будущих дочерних элементов, это не работает. Теперь, когда свойство управляется на таблице-шаблоне, его изменение там позволит изменение распространиться на вновь созданные дочерние элементы. Существующие дочерние таблицы придется изменять вручную, но это всегда было так.

F.41.7.2. Часовые пояса

Важно обеспечить согласованность часовых поясов для всех систем, которые будут выполнять операции обслуживания pg_partman, особенно при выполнении временного партицирования. Вызовы функций pg_partman будут использовать часовой пояс, который установлен клиентом в момент вызова функций. Это согласуется с общим принципом работы клиентов Tantor SE.

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

В настоящее время есть открытая проблема для тех, кто хочет помочь в решении некоторых из этих проблем с переходом на летнее время - https://github.com/pgpartman/pg_partman/issues/334

F.41.7.3. Разделение на подсекции

Поддерживается субпартиционирование с несколькими уровнями, но оно имеет очень ограниченное применение в Tantor SE и практически НЕ ПРИНОСИТ ПРЕИМУЩЕСТВ ПО ПРОИЗВОДИТЕЛЬНОСТИ, за исключением случаев с экстремально большими данными в одном наборе партиций (сотни терабайт, петабайты). Если вы ищете преимущества в производительности, настройте интервал партиционирования перед рассмотрением субпартиционирования. Его основное применение - организация данных и управление их сохранностью.

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

PUBLICATION/SUBSCRIPTION для логической репликации НЕ поддерживается с использованием нативного разделение на подсекции.

Смотрите функции create_parent_sub() и run_maintenance() ниже для получения дополнительной информации.

F.41.7.4. Удержание

Если вам не нужно сохранять данные в старых секциях, доступна система удержания, которая автоматически удаляет ненужные дочерние секции. По умолчанию они только открепляются/отсоединяются, а не удаляются, но это можно настроить, если это необходимо. Также доступен метод для выгрузки таблиц, если они больше не нужны в базе данных, но все еще должны быть сохранены. Чтобы установить политику удержания, введите либо интервал, либо целочисленное значение в столбец retention таблицы part_config. Для временного партицирования значение интервала установит, что все секции, содержащие только данные, старше этого, будут удалены (включая безопасную обработку случаев, когда интервал удержания не кратен размеру секции). Для партицирования по id целочисленное значение установит, что все секции с id меньше текущего максимального значения id минус значение удержания, будут удалены. Например, если текущий максимальный id равен 100, а значение удержания равно 30, все секции с id меньше 70 будут удалены. Текущее максимальное значение id в момент выполнения функции удаления всегда используется. Имейте в виду, что для наборов подсекций, когда у родительской таблицы удаляется дочерний элемент, если эта дочерняя таблица в свою очередь разделена, удаление происходит КАСКАДНО и ВСЕ дочерние таблицы по всему дереву наследования будут удалены. Также обратите внимание, что набор секций, управляемый pg_partman, всегда должен иметь хотя бы одного потомка, поэтому удержание никогда не удалит последнюю дочернюю таблицу в наборе.

F.41.7.5. Исключение ограничений

Одним из больших преимуществ партицирования является функция под названием исключение ограничений. Проблема большинства настроек партицирования, однако, заключается в том, что это будет использоваться только на управляющем столбце партицирования. Если вы используете условие WHERE на любом другом столбце в наборе секций, будет произведено сканирование всех дочерних таблиц, если только на этих столбцах также нет ограничений. И предсказать, какими будут значения столбца для предварительного создания ограничений, может быть очень сложно или невозможно. pg_partman имеет функцию для применения ограничений к старым таблицам в наборе секций, которые, возможно, больше не подвергаются редактированию (старыми считаются старше, чем значение конфигурации optimize_constraint). Он проверяет текущие минимальные/максимальные значения в заданных столбцах, а затем применяет ограничение к этой дочерней таблице. Это может позволить функции исключения ограничений потенциально исключить сканирование старых дочерних таблиц, когда другие столбцы используются в условиях WHERE. Обратите внимание, что это ограничивает возможность редактирования этих столбцов, но в тех ситуациях, где это применимо, это может оказать огромное влияние на производительность запросов для очень больших наборов секций. Так что если вы только вставляете новые данные, это может быть очень полезно, но если данные регулярно вставляются/обновляются во всем наборе секций, это имеет ограниченное применение. Функции для легкого воссоздания ограничений также доступны, если данные все же придется редактировать в этих старых секциях. Обратите внимание, что ограничения, управляемые PG Partman, НЕ ДОЛЖНЫ быть переименованы, чтобы позволить расширению правильно управлять ими для вас. Для лучшего понимания того, как это работает, пожалуйста, посмотрите этот блог-пост: http://www.keithf4.com/managing-constraint-exclusion-in-table-partitioning

Добавление этих ограничений может потенциально вызвать конфликт с данными, содержащимися в этих таблицах, а также сделать обслуживание pg_partman длительным процессом. Начиная с версии 4.2+ pg_partman, в таблице part_config(_sub) теперь есть столбец constraint_valid, чтобы установить, должны ли эти ограничения быть установлены как NOT VALID при создании. Хотя это может сделать создание ограничения(й) почти мгновенным, исключение ограничений не может быть использовано, пока оно не будет проверено. Вот почему ограничения добавляются как действительные по умолчанию.

ПРИМЕЧАНИЕ: Это может не работать с разделение на подсекции. Оно будет работать на первом уровне разделение на подсекции, но не гарантируется правильная работа на дальнейших наборах подсекций в зависимости от комбинаций интервалов и значения optimize_constraint. Например, еженедельное разделение -> ежедневное с ежедневным optimize_constraint равным 7 не будет работать ожидаемым образом. Еженедельные ограничения будут созданы, но скорее всего не будут созданы ежедневные подсекции.

F.41.7.6. Рассмотрение пользовательского временного интервала

Список временных интервалов, указанных для create_parent() ниже, оптимизирован для максимально быстрой работы с не встроенным, основанным на триггерах, партицированием. Интервалы, отличные от этих значений, возможны, но производительность значительно снизится, чтобы обеспечить такую гибкость. Для встроенного партицирования, в отличие от метода pg_partman, основанного на триггерах, нет различных методов партицирования для любых заданных интервалов. Все возможные интервалы, использующие встроенный метод, имеют одинаковые характеристики производительности и лучше любого метода, основанного на триггерах. Если вы все еще используете партицирование на основе триггеров и вам нужен другой интервал партицирования, отличный от тех, что предоставляет pg_partman, настоятельно рекомендуется обновиться до последней версии Tantor SE и перейти на встроенное партицирование.

Наименьший поддерживаемый интервал времени составляет 1 секунду, а верхний предел ограничен минимальными и максимальными значениями временных меток, которые поддерживает Tantor SE Раздел 8.5. Наименьший поддерживаемый в настоящее время интервал целых чисел составляет 10.

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

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

F.41.7.7. Ограничения на длину имен

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

F.41.7.8. Уникальные ограничения & Upsert

Наследование таблиц в Tantor SE не позволяет применять первичный ключ или уникальный индекс/ограничение родителя ко всем дочерним таблицам. Ограничение применяется к каждой отдельной таблице, но не ко всему набору секций в целом. Например, это означает, что небрежное приложение может привести к дублированию значения первичного ключа в наборе секций. В то же время, вместе с pg_partman поставляется скрипт на python, который может обеспечить мониторинг, чтобы помочь гарантировать, что отсутствие этой функции не приведет к долгосрочному ущербу. См. check_unique_constraint.py в разделе Скрипты.

ВАЖНОЕ ЗАМЕЧАНИЕ: Версия 4.6.0 и PostgreSQL 11+ больше не поддерживают операцию upsert в pg_partman для нативного разделения. Пожалуйста, используйте функцию INSERT...ON CONFLICT, встроенную в PostgreSQL.

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

Функция upsert в pg_partman является необязательной, по умолчанию отключена и была включена только потому, что в основном плане развития PostgreSQL на момент ее реализации не было нативной поддержки.

F.41.7.9. Ведение журнала/Мониторинг

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

F.41.8. Фоновые рабочие процессы

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

ПОМНИТЕ: Необходимо, чтобы pg_partman_bgw был в таблицах shared_preload_libraries (требуется перезагрузка).

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

  • pg_partman_bgw.dbname

    • Требуется. База данных(ы), на которой будет выполняться функция run_maintenance(). Если их несколько, используйте список, разделенный запятыми. Если не задано, BGW ничего не будет делать.

  • pg_partman_bgw.interval

    • Количество секунд между вызовами функции run_maintenance(). По умолчанию - 3600 (1 час).

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

  • pg_partman_bgw.role

    • Роль, под которой будет выполняться run_maintenance(). По умолчанию - postgres. Разрешено только одно имя роли.

  • pg_partman_bgw.analyze

    • Назначение то же, что и у аргумента p_analyze в функции run_maintenance(). Подробнее см. ниже подробнее. Установите значение on для TRUE (по умолчанию для PG10 и старше). Установите значение off для FALSE (по умолчанию для PG11+).

  • pg_partman_bgw.jobmon

    • Назначение то же, что и у аргумента p_jobmon в функции run_maintenance(). Подробнее см. ниже подробнее. Устанавливается в on для TRUE. Установите значение off для FALSE. По умолчанию on.

Если по какой-либо причине основной процесс фонового рабочего процесса завершается аварийно, он будет пытаться перезапуститься каждые 10 минут. Проверьте журналы postgres на наличие проблем, если фоновый рабочий процесс не запускается.

Начиная с версии 4.0.0, фоновый рабочий процесс все еще использует обычную функцию run_maintenance(). Возможность использовать новую процедуру находится в разработке.

F.41.9. Расширяемые объекты

Начиная с версии 4.4.0, SECURITY DEFINER был удален из всех функций в pg_partman. Теперь использование pg_partman не требует наличия суперпользователя. Для работы в качестве обычного пользователя, роль(и), которые выполняют функции и обслуживание pg_partman, должны иметь владение всеми наборами секций, которыми они управляют, и разрешения на создание объектов в любой схеме, которая будет содержать управляемые наборы секций. Для удобства использования и управления привилегиями рекомендуется создать отдельную роль для управления разделами. Пожалуйста, обратитесь к основному файлу README.md для инструкций по настройке роли и привилегий.

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

F.41.9.1. Функции создания

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_automatic_maintenance text DEFAULT 'on', p_start_partition text DEFAULT NULL, p_inherit_fk boolean DEFAULT true, p_epoch text DEFAULT 'none', p_upsert text DEFAULT '', p_publications text[] DEFAULT NULL, p_trigger_return_null boolean DEFAULT true, p_template_table text DEFAULT NULL, p_jobmon boolean DEFAULT true, p_date_trunc_interval text DEFAULT NULL) RETURNS boolean

  • Основная функция для создания набора секций с одной родительской таблицей и унаследованными дочерними таблицами. Родительская таблица должна уже существовать. Пожалуйста, примените все значения по умолчанию, индексы, ограничения, привилегии и владение к родительской таблице, чтобы они передавались дочерним таблицам. Для нативного разделения родительская таблица должна уже быть объявлена как таковая, и параметры конфигурации, передаваемые этой функции, должны соответствовать этому определению. См. примечания выше о работе с индексами и внешними ключами.

  • Во время выполнения этой функции на родительскую таблицу накладывается блокировка ACCESS EXCLUSIVE. во время выполнения этой функции. При выполнении этой функции данные не перемещаются при выполнении этой функции данные не перемещаются, поэтому блокировка должна быть кратковременной.

  • Для PG11+, автоматически создается секция по умолчанию. К текущему имени таблицы добавляется суффикс "_default".

  • p_parent_table - существующая родительская таблица. ДОЛЖНА быть указана схемой, даже если она находится в общей схеме.

  • p_control - столбец, на основе которого будет осуществляться разделение. Должен быть временным или целочисленным столбцом.

  • p_type - одно из следующих значений для установки типа разделения, который будет использоваться:

    • native

      • Используйте встроенные методы разделения, доступные в PostgreSQL 10+.

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

      • Обеспечивает значительно лучшую производительность записи и чтения, чем разделение с помощью partman.

      • Создание дочерней таблицы поддерживается в актуальном состоянии путем запуска run_maintenance(_proc). Нет поддержки триггеров.

    • partman

      • Создайте набор секций на основе триггера, используя метод разделения pg_partman.

      • Будет ли это основано на времени или серийном номере, определяется типом данных столбца управления и установленным флагом p_epoch.

      • Количество секций, наиболее эффективно управляемых позади и впереди от текущего, определяется значением конфигурации optimize_trigger в таблице part_config (значение по умолчанию 4 означает, что данные для 4 предыдущих и 4 будущих секций обрабатываются наилучшим образом).

      • Будьте внимательны при указании высокого значения optimize_trigger, так как это уменьшит эффективность улучшения.

      • Вставки в родительскую таблицу вне окна optimize_trigger будут перенаправлены в соответствующую дочернюю таблицу, если она существует, но производительность будет снижена из-за больших издержек на обработку этого условия.

      • Если дочерняя таблица не существует для заданного значения, строка будет перенесена в родительскую таблицу.

      • Создание дочерней таблицы и функция триггера поддерживаются в актуальном состоянии с помощью функции run_maintenance().

  • p_interval - временной или целочисленный интервал для каждого раздела. Независимо от типа разделения, значение должно быть указано в виде текста. Общие интервалы годовой -> четверть часа предназначены для временного разделения, и использование одного из этих явных значений при использовании разделения на основе триггеров pg_partman позволит достичь значительно лучшей производительности по сравнению с произвольным временным интервалом. Для встроенного разделения любое значение интервала допустимо и будет иметь одинаковую производительность, которая всегда лучше, чем у разделения на основе триггеров.

    • годовой - Одна секция (Partition) в год

    • quaterly - Одна секция на каждый квартал года. секции называются в формате ГГГГкК (например, 2012к4)

    • ежемесячно - Одна секция разделение в месяц

    • еженедельно - Одна секция в неделю. Следует формату даты недели ISO (http://en.wikipedia.org/wiki/ISO_week_date). Разделения называются в формате IYYYwIW (например, 2012w36).

    • ежедневно - Одна секция в день

    • ежечасно - Одна секция в час

    • каждые полчаса - Одна секция на каждый 30-минутный интервал на полчаса (1200, 1230)

    • каждые четверть часа - Одна секция на каждый 15-минутный интервал на четверть часа (1200, 1215, 1230, 1245)

    • <interval> - Любой другой интервал, кроме указанных выше значений, который допустим для типа интервала Tantor SE. Обратите внимание, что это приведет к значительному снижению производительности, если не использовать нативное партицирование. Не приводите значение параметра к типу, просто оставьте его как текст.

    • <integer> - Для секций на основе идентификаторов, диапазон целочисленных значений ID, которые должны быть установлены для каждого раздела. Введите это как целое число в текстовом формате (100, а не 100). Должно быть больше или равно 10.

  • p_constraint_cols - необязательный массив параметров для установки столбцов, которым будут установлены дополнительные ограничения. См. раздел Описание выше для получения дополнительной информации о том, как это работает, и функцию apply_constraints() для понимания, как это используется.

  • p_premake - это количество дополнительных секций, которые всегда должны опережать текущий раздел. Значение по умолчанию - 4. Это позволит создавать как минимум 5 секций, включая текущий. Например, если сегодня 6 сентября, и для ежедневной секции установлено значение premake 4, то будут созданы секции для 6-го, а также для 7-го, 8-го, 9-го и 10-го чисел. Обратите внимание, что некоторые интервалы могут иногда приводить к созданию дополнительной секции или пропуску секции из-за високосных лет, различной длины месяцев, перехода на летнее время (в системах, не использующих UTC) и т. д. Это не повредит ничему и будет автоматически исправлено. Если разделение отстает от значения premake, нормальное выполнение функции run_maintenance() и вставка данных должны автоматически догнать отставание.

  • p_automatic_maintenance - параметр для установки автоматического управления обслуживанием, когда run_maintenance() вызывается без параметра таблицы или процессом фонового рабочего процесса. Текущие допустимые значения - on и off. По умолчанию установлено значение on. Когда установлено значение off, run_maintenance() все еще может быть вызван на отдельной секции, если передать его в качестве параметра функции. См. run_maintenance в разделе «Функции обслуживания» ниже для получения дополнительной информации.

  • p_start_partition - позволяет указать первое разделение набора вместо автоматического определения. Должно быть допустимым значением временной метки (для разделения по времени) или положительным целым числом (для разделения по идентификатору). Однако следует помнить, что фактический тип данных параметра - текст. Для разделения по времени будут созданы все секции, начиная с указанной временной метки и до текущей временной метки (плюс premake). Для разделения по идентификатору будет создан только раздел, начинающийся с указанного значения (плюс premake). Обратите внимание, что для разделения на подсекции это применяется только во время начальной настройки, а не во время текущего обслуживания.

  • p_inherit_fk- позволяет pg_partman автоматически управлять наследованием любых внешних ключей, существующих на родительской (или шаблонной для нативной) таблице, ко всем ее дочерним таблицам. По умолчанию TRUE. Обратите внимание, что эта опция актуальна только для PostgreSQL 10 и более ранних версий. В PG11+ все внешние ключи, размещенные на родительской таблице, автоматически наследуются и не являются опциональными.

  • p_epoch - сообщает pg_partman, что управляющий столбец имеет тип целого числа, но на самом деле представляет собой значение времени эпохи. Вы также можете указать, является ли значение секундами, миллисекундами или наносекундами. Допустимые значения для этой опции: seconds, milliseconds, nanoseconds и none. По умолчанию - none. Все триггеры, ограничения и имена таблиц будут основаны на времени. Помимо обычного индекса на управляющем столбце, убедитесь, что вы также создаете функциональный индекс на управляющем столбце (to_timestamp (controlcolumn)), чтобы это работало эффективно.

  • p_upsert - добавляет upsert к запросам на вставку в триггер раздела для обработки конфликтов. По умолчанию '' (пустая строка), что означает, что он неактивен.

    • ВАЖНОЕ ЗАМЕЧАНИЕ: Эта функция была устарела в версии 4.6.0. Пожалуйста, планируйте миграцию на PG11, нативное разделение и INSERT...ON CONFLICT, если вы используете эту функцию.

    • введенное здесь значение является полным ON CONFLICT-предложением, которое затем будет добавлено к оператору INSERT в триггере

    • Например, чтобы игнорировать конфликтующие строки в таблице с первичным ключом id, установите p_upsert в 'ON CONFLICT (id) DO NOTHING'

    • Например, чтобы обновить конфликтующую строку в таблице с колонками (id (pk), val), установите p_upsert в 'ON CONFLICT (id) DO UPDATE SET val=EXCLUDED.val'

    • Требуется postgresql 9.5

    • См. раздел About выше для получения дополнительной информации.

  • p_publications - Опция добавления дочерних таблиц к публикациям для использования с логической репликацией. Значение представляет собой список имен публикаций, поэтому к каждому дочернему элементу можно добавить несколько публикаций. Обратите внимание, что если вы реплицируете на набор секций на стороне подписчика, вам придется установить опцию subscription_refresh в таблице part_config на стороне подписчика, чтобы получить новые таблицы из исходной публикации. В настоящее время не поддерживается подразделение для нативных наборов секций, так как публикацию нельзя добавить к родительской таблице с нативным разделением.

  • p_trigger_return_null - Применяется только к не нативному разделению на основе триггеров. Булево значение, которое позволяет контролировать поведение триггера раздела RETURN. По умолчанию это true, и триггер возвращает NULL, чтобы предотвратить попадание данных как в родительскую таблицу, так и в дочерние. Однако, если у вас есть несколько триггеров и вы полагаетесь на возвращаемое значение в качестве значения столбца NEW, это может вызвать проблему. Установка этого значения конфигурации в false заставит триггер раздела возвращать RETURN NEW. Затем вам придется обрабатывать возвращаемое значение в другом триггере соответствующим образом. В противном случае, новые данные будут попадать как в дочернюю, так и в родительскую таблицу набора секций.

  • p_template_table - Для нативного разделения в PG10 индексы, внешние ключи и табличные пространства имен не могут быть установлены на родительской таблице. Для PG11 только уникальные индексы, не включающие ключ разделения, не могут быть созданы на родительской таблице. Поэтому, если вы хотите, чтобы они автоматически создавались на дочерних таблицах, они должны быть управляемы в другом месте. Если вы не передаете значение здесь, шаблонная таблица будет автоматически создана для вас в той же схеме, в которой был установлен pg_partman. Обратите внимание, что до создания индексов, внешних ключей или таблиц пространств имен на шаблоне, у дочерних таблиц не будет ничего. Используйте скрипты на Python для повторного применения индексов и внешних ключей к набору секций, когда шаблонная таблица будет готова. Для пространств имен вам придется вручную переместить любые ранее существующие дочерние таблицы. Если вы предварительно создаете шаблонную таблицу и передаете ее имя здесь, то начальные дочерние таблицы немедленно получат эти свойства.

  • p_jobmon - позволяет p_jobmon использовать расширение pg_jobmon для контроля правильности работы разделения. По умолчанию TRUE.

  • p_date_trunc_interval - По умолчанию временное разделение pg_partman будет усекать начальные значения дочерней таблицы, чтобы они соответствовали началу типичных границ (полночь для ежедневного, 1-й день для ежемесячного, 1 января для ежегодного и т. д.). Если требуется пользовательский временной интервал, не совпадающий с этими границами, может потребоваться использование этой опции для обеспечения ожидаемых границ дочерней таблицы (особенно если вы также устанавливаете p_start_partition). Допустимые значения, разрешенные для этого параметра, - это значения интервала, принимаемые встроенной функцией date_trunc() (день, неделя, месяц и т. д.). Например, если вы установите интервал в 9 недель, по умолчанию pg_partman будет усекать таблицы по месяцам (поскольку интервал больше одного месяца, но меньше 1 года) и неожиданно начинать с первого числа месяца в некоторых случаях. Установите это значение на неделю, чтобы начальные значения дочерней таблицы правильно усекались еженедельно, чтобы соответствовать 9-недельному интервалу. Если вы используете пользовательский временной интервал, пожалуйста, экспериментируйте с этой опцией, чтобы получить ожидаемый набор дочерних таблиц, который вам нужен, или используйте более типичный интервал разделения, чтобы упростить управление разделами.

create_sub_parent(p_top_parent text, p_control text, p_type text, p_interval text, p_native_check text DEFAULT NULL, 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 text DEFAULT 'none', p_upsert text DEFAULT '', p_trigger_return_null boolean DEFAULT true, p_jobmon boolean DEFAULT true, p_date_trunc_interval text DEFAULT NULL) RETURNS boolean

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

  • p_top_parent - Этот параметр является родительской таблицей уже существующего набора секций. Он указывает pg_partman превратить все дочерние таблицы данного набора секций в их собственные родительские таблицы собственных наборов секций, используя остальные параметры для этой функции.

  • p_native_check - Превращение существующего набора нативных секций в подразделенный набор является разрушительным процессом. Таблица должна быть объявлена как нативно партицированная при создании и не может быть изменена позже. Следовательно, существующие дочерние таблицы должны быть удалены и созданы заново как партицированные родительские таблицы. Этот флаг здесь, чтобы помочь убедиться, что эта функция не запускается без предварительного знания о том, что все данные в наборе секций будут уничтожены в процессе создания. Он должен быть установлен в yes, чтобы продолжить подразделение нативного набора секций. Этот параметр можно игнорировать, если вы создаете набор секций pg_partman на основе триггера.

  • Все остальные параметры этой функции имеют точно такую же цель, как и у create_parent(), но вместо этого они используются для указания pg_partman, как каждая дочерняя таблица будет сама разделена.

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

  • Рекомендуется использовать короткие имена таблиц для подмножеств подразделений, если вы планируете полагаться на имена таблиц для организации. Суффикс, добавленный в конец имени таблицы, всегда будет присутствовать для любого активного типа раздела в этом наборе, но если общая длина превышает 63 символа, исходное имя будет усечено. Более длинные имена таблиц могут привести к усечению исходных имен родительских таблиц и возможному обрезанию суффикса разделения верхнего уровня. Я не могу контролировать это и сделал требование, чтобы суффикс разделения нижнего уровня сохранялся.

  • Обратите внимание, что для первого уровня подразделений аргумент p_parent_table, который вы изначально передали в create_parent(), будет точно таким же значением, которое вы передаете в create_sub_parent(). Если вам нужно дальнейшее подразделение, вы должны начать передавать create_sub_parent() другое значение (дочерние таблицы набора секций верхнего уровня).

  • Для нативного разделения будет автоматически использоваться шаблонная таблица, которая уже установлена для заданного p_top_parent.

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, p_source_table text DEFAULT NULL, p_ignored_columns text[] DEFAULT NULL) RETURNS bigint

  • Эта функция используется для разделения данных, которые могли существовать до установки родительской таблицы в качестве раздела на основе времени. Она также исправляет данные, которые случайно попадают в родительскую таблицу (только на основе триггера) или таблицу по умолчанию (только для нативного разделения, PG11+).

  • Если требуемая партиция не существует, он будет автоматически создан. Если требуемая партиция уже существует, данные будут перемещены туда.

  • Если вы пытаетесь автоматически разделить большое количество данных, рекомендуется использовать скрипт partition_data.py для фиксации данных в меньших партиях. Или, если вы используете PG11+, используйте процедуру partition_data_proc() для выполнения той же операции. Это значительно снизит проблемы, вызванные длительными транзакциями и конкуренцией данных.

  • Для подразделенных наборов вы должны начать разделение данных с самого верхнего уровня и продолжать по каждому уровню. Это означает, что вы должны сначала запустить эту функцию, а затем запустить create_sub_parent(), чтобы создать дополнительные уровни разделения. Затем продолжайте запускать эту функцию снова для каждого нового подродительского элемента после их создания. См. документ pg_partman_howto.md для полного примера. ВАЖНОЕ ЗАМЕЧАНИЕ: это может не работать ожидаемым образом для нативного разделения, так как подразделение нативного набора в pg_partman является разрушительной операцией. См. create_sub_parent().

  • p_parent_table - существующая родительская таблица. Для ненативного разделения предполагается, что здесь находятся неразделенные данные. Должно быть указано имя со схемой, даже если оно находится в общей схеме.

  • p_batch_interval - необязательный аргумент, применим только для ненативного разделения. Временной интервал перемещения данных. Он может быть меньше интервала разделения, что позволяет разбить очень большие секции на более мелкие партии. По умолчанию используется настроенный интервал разделения, если он не указан или если вы указываете интервал, превышающий интервал разделения. ПРИМЕЧАНИЕ: Эта опция НЕ МОЖЕТ быть использована при перемещении данных из секции по умолчанию в PostgreSQL 11+.

  • p_batch_count - необязательный аргумент, сколько раз запустить batch_interval в одном вызове этой функции. Значение по умолчанию - 1. Для нативного разделения это устанавливает, сколько дочерних таблиц будет обработано за один запуск.

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

  • p_order - необязательный аргумент, по умолчанию данные перемещаются из родительской таблицы в порядке возрастания (ASC). Позволяет изменить порядок на убывающий (DESC).

  • p_analyze - необязательный аргумент, по умолчанию при создании новой дочерней таблицы выполняется анализ родительской таблицы набора секций для обеспечения исключения ограничений. Этот анализ можно пропустить, установив значение false, что поможет увеличить скорость перемещения больших объемов данных. Если это установлено в false, настоятельно рекомендуется вручную выполнить анализ набора секций после завершения, чтобы обновить статистику.

  • p_source_table - Эта опция может использоваться, когда вам нужно переместить данные в нативно разделенный набор. При передаче схемы и имени таблицы в этот параметр, любые данные в этой таблице будут ПЕРЕМЕЩЕНЫ в раздел, указанный параметром p_parent_table, создавая при необходимости дочерние таблицы.

  • p_ignored_columns - Эта опция позволяет фильтровать определенные столбцы при перемещении данных из таблицы по умолчанию/родительской в соответствующую дочернюю таблицу(ы). Это обычно требуется только при использовании столбцов со значением GENERATED ALWAYS, поскольку прямая вставка значения приведет к ошибке при перемещении данных. Значение представляет собой массив текстовых имен столбцов.

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

partition_data_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval bigint DEFAULT NULL, p_lock_wait numeric DEFAULT 0, p_order text DEFAULT 'ASC', p_analyze boolean DEFAULT true, p_source_table text DEFAULT NULL, p_ignored_columns text[] DEFAULT NULL) RETURNS bigint

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

  • Если требуемая партиция не существует, он будет автоматически создан. Если требуемая партиция уже существует, данные будут перемещены туда.

  • Если вы пытаетесь автоматически разделить большое количество данных, рекомендуется использовать скрипт partition_data.py для фиксации данных в меньших партиях. Или, если вы используете PG11+, используйте процедуру partition_data_proc() для выполнения той же операции. Это значительно снизит проблемы, вызванные длительными транзакциями и конкуренцией данных.

  • Для подразделенных наборов вы должны начать разделение данных с самого верхнего уровня и продолжать по каждому уровню. Это означает, что вы должны сначала запустить эту функцию, а затем запустить create_sub_parent(), чтобы создать дополнительные уровни разделения. Затем продолжайте запускать эту функцию снова для каждого нового подродительского элемента после их создания. См. документ pg_partman_howto.md для полного примера. ВАЖНОЕ ЗАМЕЧАНИЕ: это может не работать ожидаемым образом для нативного разделения, так как подразделение нативного набора в pg_partman является разрушительной операцией. См. create_sub_parent().

  • p_parent_table - существующая родительская таблица. Для ненативного разделения предполагается, что здесь находятся неразделенные данные. Должно быть указано имя со схемой, даже если оно находится в общей схеме.

  • p_batch_interval - необязательный аргумент, применим только для ненативного разделения. Временной интервал перемещения данных. Он может быть меньше интервала разделения, что позволяет разбить очень большие секции на более мелкие партии. По умолчанию используется настроенный интервал разделения, если он не указан или если вы указываете интервал, превышающий интервал разделения. ПРИМЕЧАНИЕ: Эта опция НЕ МОЖЕТ быть использована при перемещении данных из секции по умолчанию в PostgreSQL 11+.

  • p_batch_count - необязательный аргумент, сколько раз запустить batch_interval в одном вызове этой функции. Значение по умолчанию - 1. Для нативного разделения это устанавливает, сколько дочерних таблиц будет обработано за один запуск.

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

  • p_order - необязательный аргумент, по умолчанию данные перемещаются из родительской таблицы в порядке возрастания (ASC). Позволяет изменить порядок на убывающий (DESC).

  • p_analyze - необязательный аргумент, по умолчанию при создании новой дочерней таблицы выполняется анализ родительской таблицы набора секций для обеспечения исключения ограничений. Этот анализ можно пропустить, установив значение false, что поможет увеличить скорость перемещения больших объемов данных. Если это установлено в false, настоятельно рекомендуется вручную выполнить анализ набора секций после завершения, чтобы обновить статистику.

  • p_source_table - Эта опция может использоваться, когда вам нужно переместить данные в нативно разделенный набор. При передаче схемы и имени таблицы в этот параметр, любые данные в этой таблице будут ПЕРЕМЕЩЕНЫ в раздел, указанный параметром p_parent_table, создавая при необходимости дочерние таблицы.

  • p_ignored_columns - Эта опция позволяет фильтровать определенные столбцы при перемещении данных из таблицы по умолчанию/родительской в соответствующую дочернюю таблицу(ы). Это обычно требуется только при использовании столбцов со значением GENERATED ALWAYS, поскольку прямая вставка значения приведет к ошибке при перемещении данных. Значение представляет собой массив текстовых имен столбцов.

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

partition_data_proc(p_parent_table text, p_interval text DEFAULT NULL, p_batch int DEFAULT NULL, p_wait int DEFAULT 1, p_source_table text DEFAULT NULL, p_order text DEFAULT 'ASC', p_lock_wait int DEFAULT 0, p_lock_wait_tries int DEFAULT 10, p_quiet boolean DEFAULT false, p_ignored_columns text[] DEFAULT NULL)

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

  • Работает только с PostgreSQL 11+

  • Вызывает либо partition_data_time(), либо partition_data_id() в цикле в зависимости от типа разделения.

  • p_parent_table - Родительская таблица уже созданного набора секций.

  • p_interval - Значение, которое передается в функцию разделения в качестве аргумента p_batch_interval. Используйте это, чтобы установить интервал, меньший, чем интервал разделения, чтобы фиксировать данные в меньших партиях. По умолчанию используется интервал разделения, если не указано. ВНИМАНИЕ: Этот параметр НЕ МОЖЕТ быть использован при перемещении данных из раздела по умолчанию в PostgreSQL 11+.

  • p_batch - Сколько раз пройти через значение, указанное для p_interval. Если p_interval не установлен, будет использоваться интервал разделения по умолчанию, и будет создано не более -b секций. Процедура фиксирует данные в конце каждой отдельной партии. (Не передается как p_batch_count в функцию разделения). Если не установлено, все данные в родительской/исходной таблице будут разделены в одном запуске процедуры.

  • p_wait - Заставляет процедуру ожидать заданное количество секунд между фиксацией (партиями), чтобы снизить нагрузку на запись.

  • p_source_table - То же самое, что и опция p_source_table в вызываемой функции разделения.

  • p_order - Позволяет указать порядок миграции данных от родительской/по умолчанию к дочерним, либо по возрастанию (ASC), либо по убыванию (DESC). По умолчанию ASC.

  • p_lock_wait - Параметр, передаваемый непосредственно в основную функцию partition_data_*(). Количество секунд ожидания строк, которые могут быть заблокированы другой транзакцией. По умолчанию ожидать вечно (0).

  • p_lock_wait_tries - Параметр, устанавливающий, сколько раз процедура будет пытаться ожидать заданное время для p_lock_wait. По умолчанию 10 попыток.

  • p_quiet - Процедуры не могут возвращать значения, поэтому по умолчанию они выводят УВЕДОМЛЕНИЯ, чтобы показать прогресс. Установите эту опцию, чтобы подавить эти уведомления.

  • p_ignored_columns - Эта опция позволяет фильтровать определенные столбцы при перемещении данных из таблицы по умолчанию/родительской в соответствующую дочернюю таблицу(ы). Это обычно требуется только при использовании столбцов со значением GENERATED ALWAYS, поскольку прямая вставка значения приведет к ошибке при перемещении данных. Значение представляет собой массив текстовых имен столбцов.

create_partition_time(p_parent_table text, p_partition_times timestamptz[], p_analyze boolean DEFAULT true, p_start_partition text DEFAULT NULL) RETURNS boolean

  • Эта функция используется для создания дочерних секций для заданной родительской таблицы.

  • Обычно эта функция никогда не вызывается вручную, так как создание секций управляется run_maintenance(). Но если вам нужно принудительно создать определенные дочерние таблицы вне нормального обслуживания, эта функция может упростить эту задачу.

  • Для несистемного разделения вам также может потребоваться вызвать create_function_time(), чтобы обновить триггер разделения, если вы создали секции в текущем окне оптимизации.

  • p_parent_table - родительская таблица для создания новой дочерней таблицы(таблиц).

  • p_partition_times - Массив значений timestamptz для создания дочерних таблиц. Если дочерняя таблица не существует, она будет создана. Если она уже существует, она не будет создана, и функция все равно завершится без ошибок. Обратите внимание, что указанное значение будет использоваться в качестве нижней границы для дочерней таблицы и также повлияет на имя, данное дочерней таблице. Убедитесь, что указанное значение временной метки согласуется с другими дочерними таблицами, иначе вы можете столкнуться с проблемой пропуска значений.

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

  • p_start_partition - При разделении на подсекции позволяет передавать значение начального раздела для дочерних таблиц подсекции.

  • Возвращает TRUE, если для заданных значений timestamptz были созданы дочерние таблицы. Возвращает false, если дочерние таблицы не были созданы.

create_partition_id(p_parent_table text, p_partition_ids bigint[], p_analyze boolean DEFAULT true, p_start_partition text DEFAULT NULL) RETURNS boolean

  • Эта функция используется для создания дочерних секций для заданной родительской таблицы.

  • Обычно эта функция никогда не вызывается вручную, так как создание секций управляется run_maintenance(). Но если вам нужно принудительно создать определенные дочерние таблицы вне нормального обслуживания, эта функция может упростить эту задачу.

  • Для ненативного разделения вам также может потребоваться вызвать create_function_id(), чтобы обновить триггер разделения, если вы создали секции в текущем окне оптимизации.

  • p_parent_table - родительская таблица для создания новой дочерней таблицы(таблиц).

  • p_partition_ids - Массив целочисленных значений для создания дочерних таблиц. Если дочерняя таблица не существует, она будет создана. Если она уже существует, она не будет создана, и функция все равно завершится без ошибок. Обратите внимание, что указанное значение будет использоваться в качестве нижней границы для дочерней таблицы и также повлияет на имя, данное дочерней таблице. Убедитесь, что указанное целочисленное значение согласуется с другими дочерними таблицами, иначе вы можете столкнуться с проблемой пропуска значений.

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

  • p_start_partition - При разделении на подсекции позволяет передавать значение начального раздела для дочерних таблиц подсекции.

  • Возвращает TRUE, если для заданных значений integer были созданы дочерние таблицы. Возвращает false, если дочерние таблицы не были созданы.

create_function_time(p_parent_table text, p_job_id bigint DEFAULT NULL) RETURNS void * Эта функция используется для создания триггерной функции для временного разделения, не являющегося встроенным. * Обычно эта функция никогда не вызывается вручную, так как создание функции управляется run_maintenance(). Но если вам нужно принудительно повторно создать триггерную функцию, это позволит вам сделать это. * p_parent_table - родительская таблица для повторного создания триггерной функции. * Параметр p_job_id является необязательным. Он предназначен для внутреннего использования и позволяет объединить ведение журнала заданий в исходное задание, которое вызвало эту функцию, если это применимо.

create_function_id(p_parent_table text, p_job_id bigint DEFAULT NULL) RETURNS void

  • Эта функция используется для создания триггерной функции для не встроенного последовательного разделения.

  • Обычно эта функция никогда не вызывается вручную, так как создание функции управляется run_maintenance(). Но если вам нужно принудительно повторно создать триггерную функцию, это позволит вам сделать это.

  • p_parent_table - родительская таблица для воссоздания триггерной функции.

  • Параметр p_job_id является необязательным. Он предназначен для внутреннего использования и позволяет объединить ведение журнала заданий в исходное задание, которое вызвало эту функцию, если это применимо.

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

run_maintenance(p_parent_table text DEFAULT NULL, p_analyze boolean DEFAULT NULL, p_jobmon boolean DEFAULT true) RETURNS void

  • Запустите эту функцию как запланированное задание (cron и т. д.), чтобы автоматически создавать дочерние таблицы для наборов секций, настроенных для их использования.

  • Вы также можете использовать включенный фоновый рабочий процесс (BGW), чтобы Tantor SE сам выполнил это автоматически. Обратите внимание, что параметр p_parent_table не доступен с этим методом, поэтому, если вам нужно запустить его для определенного набора секций, вы должны сделать это вручную или запланировать, как указано выше. Другие параметры имеют значения в postgresql.conf, которые можно установить. См. раздел BGW выше.

  • Эта функция также поддерживает систему сохранения секций для любых наборов секций, у которых она включена (см. Описание и таблицу part_config ниже).

  • Каждый запуск проверяет все таблицы, перечисленные в таблице part_config с automatic_maintenance установленным в true и либо создает для них новые секции, либо запускает их политику удержания.

  • По умолчанию все наборы секций имеют значение automatic_maintenance, установленное на true.

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

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

  • p_parent_table - необязательный параметр, который, если передан, вызовет запуск run_maintenance() только для этой указанной таблицы, независимо от значения automatic_maintenance. Таблицы с высокой скоростью транзакций могут вызывать конфликты при выполнении обслуживания для множества таблиц одновременно, поэтому это позволяет более точно контролировать время выполнения обслуживания секций для конкретных таблиц. Обратите внимание, что это также приведет к запуску системы сохранения только для данной таблицы.

  • p_analyze - Для невстроенного разделения и встроенного разделения в PG10 при создании новой дочерней таблицы выполняется анализ родительской таблицы для обновления статистики. В PG11+ это больше не делается, поэтому по умолчанию это не выполняется. Для больших наборов секций этот анализ может занять некоторое время, и если run_maintenance() управляет несколькими разделами в одном запуске, это может вызвать конфликт, пока анализ не завершится. Установите это значение в false (или просто оставьте NULL для PG11+), чтобы отключить выполнение анализа и избежать этого конфликта. Для PG10 и более ранних версий обратите внимание, что вам все равно нужно запланировать анализ родительской таблицы в какой-то момент.

  • p_jobmon - необязательный параметр для контроля использования run_maintenance() расширения pg_jobmon для регистрации своих действий. Использование pg_jobmon для обслуживания конкретной таблицы контролируется настройкой в таблице part_config и эта настройка не будет влиять на это. По умолчанию установлено значение true, если не указано иное.

run_maintenance_proc(p_wait int DEFAULT 0, p_analyze boolean DEFAULT NULL, p_jobmon boolean DEFAULT true)

  • В PG11+ это предпочтительный метод запуска обслуживания секций по сравнению с прямым вызовом функции run_maintenance().

  • Эта процедура может быть вызвана вместо функции run_maintenance() для того, чтобы заставить Tantor SE делать коммит после завершения обслуживания каждого набора секций. Это значительно снижает проблемы с конкуренцией при долгих транзакциях, когда есть много наборов секций для обслуживания.

  • ПРИМЕЧАНИЕ: BGW пока не использует эту процедуру и по-прежнему использует стандартную функцию run_maintenance().

  • p_wait - Сколько секунд ждать между запусками обслуживания каждого набора секций. По умолчанию 0.

  • p_analyze - См. параметр p_analyze в функции run_maintenance.

check_default(p_exact_count boolean DEFAULT true)

  • Запустите эту функцию для контроля того, чтобы родительские таблицы (невстроенные) или таблицы по умолчанию (встроенные в PG11+) наборов секций, управляемых pg_partman, не получали вставленные строки.

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

  • partition_data_time() и partition_data_id() могут использоваться для перемещения данных из этих родительских/таблиц по умолчанию в соответствующие дочерние таблицы.

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

show_partitions (p_parent_table text, p_order text DEFAULT 'ASC', p_include_default boolean DEFAULT false) RETURNS TABLE (partition_schemaname text, partition_tablename text)

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

  • Таблицы возвращаются в порядке, который имеет смысл для интервала разделения, а не в соответствии с локализованным порядком их имен.

  • Для PG11+ по умолчанию раздел может быть возвращен в этом наборе результатов, если p_include_default установлено в true. По умолчанию оно равно false, так как это гораздо более распространено во внутреннем коде.

  • p_order - необязательный параметр для установки порядка, в котором возвращаются дочерние таблицы. По умолчанию установлено значение ASCending. Установите значение DESC, чтобы вернуть в порядке убывания. Если используется значение по умолчанию, оно всегда указывается первым.

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)

  • Учитывая родительскую таблицу, управляемую pg_partman (p_parent_table), и соответствующее значение (время или идентификатор, но представленное в текстовой форме для p_value), вернуть имя дочерней раздела, в котором это значение существовало бы.

  • Если используется разделение времени по эпохе, укажите значение временной метки, а НЕ целочисленное значение эпохи (используйте функцию to_timestamp() для преобразования значения эпохи).

  • Возвращает имя дочерней таблицы, независимо от того, существует она или нет

  • Также возвращает необработанное значение (suffix_timestamp или suffix_id) для суффикса раздела для указанной дочерней таблицы.

  • Также возвращает логическое значение (table_exists), чтобы указать, существует ли на самом деле этот дочерний стол.

@[email protected]_partition_info (p_child_table text, p_partition_interval text DEFAULT NULL, p_parent_table text DEFAULT NULL, OUT child_start_time timestamptz, OUT child_end_time timestamptz, OUT child_start_id bigint, OUT child_end_id bigint, OUT suffix text) RETURNS record

  • Учитывая имя дочерней таблицы с указанием схемы (p_child_table), вернуть соответствующие граничные значения этой дочерней таблицы, а также суффикс, добавленный к имени дочерней таблицы.

  • p_partition_interval - Если указано, возвращает граничные результаты на основе этого интервала. Если не указано, функция ищет интервал, сохраненный в таблице part_config для этого набора секций.

  • p_parent_table - Дополнительный аргумент, который может быть указан, когда известна таблица-родитель (parent_table), и чтобы избежать поиска в каталоге для таблицы-родителя, связанной с p_child_table.

  • OUT child_start_times & child_end_time - Функция возвращает значения для этих выходных параметров, если набор секций основан на времени. В противном случае возвращает NULL. Обратите внимание, что начальное значение инклюзивно, а конечное значение исключительно для заданных границ дочерней таблицы, точно так, как они определены в базе данных.

  • OUT child_start_id & child_end_id - Функция возвращает значения для этих выходных параметров, если набор секций основан на целых числах. В противном случае возвращает NULL. Обратите внимание, что начальное значение инклюзивно, а конечное значение исключительно для заданных границ дочерней таблицы, точно так, как они определены в базе данных.

  • OUT suffix - Выводит текстовую часть, добавленную к дочерней таблице, которая идентифицирует ее содержимое, минус "_p" (Например, 2020_01_30 ИЛИ 920000). Полезно для генерации собственных суффиксов для партицирования, аналогично тому, как это делает pg_partman.

@[email protected]_partitioned_table_definition(p_parent_table text, p_ignore_template_table boolean default false) RETURNS text

  • Функция для возврата необходимых команд для воссоздания набора секций в pg_partman для заданной родительской таблицы (p_parent_table).

  • Возвращает как вызов create_parent(), так и оператор UPDATE для установки дополнительных параметров, хранящихся в part_config.

  • ПРИМЕЧАНИЕ: В настоящее время это работает только с наборами секций одного уровня. Ищем вклады для добавления поддержки наборов подсекций.

  • p_ignore_template- Для нативных разделенных таблиц необходимо создать шаблонную таблицу перед тем, как SQL, сгенерированный этой функцией, будет работать правильно. Если вы не изменяли шаблонную таблицу, то безопасно передать здесь значение TRUE, чтобы сгенерированный SQL указал partman'у создать новую шаблонную таблицу. Но для безопасности рекомендуется использовать pg_dump для создания резервной копии шаблонных таблиц и их восстановления перед использованием сгенерированного SQL, чтобы сохранить все изменения шаблона.

partition_gap_fill(p_parent_table text) RETURNS integer

  • Функция для заполнения пробелов, которые могут существовать в ряду дочерних таблиц для данной родительской таблицы (p_parent_table). дочерних таблиц для заданной родительской таблицы (p_parent_table).

  • Начинает с текущей минимальной дочерней таблицы и заполняет любые пробелы, встреченные на основе интервала разделения, до текущей максимальной дочерней таблицы.

  • Возвращает количество созданных дочерних таблиц. Возвращает 0, если ни одна не создана.

apply_constraints(p_parent_table text, p_child_table text DEFAULT NULL, p_analyze boolean DEFAULT FALSE, p_job_id bigint DEFAULT NULL) RETURNS void

  • Примените ограничения к дочерним таблицам в заданном наборе секций для настроенных столбцов (имена ограничений начинаются с префикса partmanconstr_).

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

  • Столбцы, которым должны быть назначены ограничения, устанавливаются в таблице part_config в массиве constraint_cols или при создании с параметром для create_parent().

  • Если ограничения pg_partman уже существуют на дочерней таблице, функция пропустит их и не создаст дубликаты.

  • Если столбец(ы), указанные, содержат только значения NULL, то ограничение не будет установлено.

  • Если задан параметр дочерней таблицы, то только для этой дочерней таблицы будут применены ограничения.

  • Если параметр p_child_table не указан, ограничения будут наложены на последнюю дочернюю таблицу, старше значения optimize_constraint. Например, если значение optimize_constraint равно 30, то ограничения будут наложены на дочернюю таблицу, находящуюся на 31 позицию назад от текущего раздела (при условии, что предварительное создание секций было поддержано в актуальном состоянии).

  • Если необходимо применить ограничения ко всем старым дочерним таблицам, используйте включенный скрипт на языке Python (reapply_constraint.py). Или, если вы используете PG11+, используйте процедуру reapply_constraints_proc. Оба этих метода имеют опции, которые облегчают применение ограничений с минимальным влиянием на производительность.

  • Параметр p_job_id является необязательным. Он предназначен для внутреннего использования и позволяет объединить ведение журнала заданий в исходное задание, которое вызвало эту функцию, если это применимо.

drop_constraints(p_parent_table text, p_child_table text, p_debug boolean DEFAULT false)

  • Удалите ограничения, созданные pg_partman для столбцов, настроенных в part_config. Это упрощает очистку ограничений, если требуется редактировать старые данные, и ограничения не позволяют это сделать.

  • Будут удалены только ограничения, которые начинаются с partmanconstr_* для указанной дочерней таблицы и настроенных столбцов.

  • Если вам нужно удалить ограничения на все дочерние таблицы, используйте включенный скрипт на языке Python (reapply_constraint.py). Или, если вы используете PG11+, используйте функцию reapply_constraints_proc(). Оба варианта имеют опции, которые облегчают удаление ограничений с минимальным влиянием на производительность.

  • Параметр отладки покажет вам оператор сброса ограничения, который был использован.

reapply_constraints_proc(p_parent_table text, p_drop_constraints boolean DEFAULT false, p_apply_constraints boolean DEFAULT false, p_wait int DEFAULT 0, p_dryrun boolean DEFAULT false) * Процедуры для PG11+ для повторного применения дополнительных ограничений, управляемых pg_partman (см. раздел "Исключение ограничений" в разделе "О программе" выше). * Вызывает drop_constraints() и/или apply_constraint() в цикле, фиксируя изменения после удаления или добавления каждого объекта. Это помогает избежать длительной транзакции и конкуренции при работе с большим количеством секций. * Обычное использование заключается в сначала удалении ограничений, редактировании данных по необходимости, а затем повторном применении ограничений. * p_parent_table - Родительская таблица уже созданного набора секций. * p_drop_constraints - Удалить все ограничения, управляемые pg_partman. Удаляет ограничения на всех дочерних таблицах, включая текущие и будущие. * p_apply_constraints - Применить ограничения к настроенным столбцам на всех дочерних таблицах, старше предварительно заданного значения. * p_wait - Подождать заданное количество секунд после удаления или применения ограничений для перехода к следующей таблице.

reapply_privileges(p_parent_table text)

  • Эта функция используется для повторного применения владения и разрешений на все дочерние таблицы на основе того, что установлено в родительской таблице.

  • Привилегии, которые имеет родительская таблица, будут предоставлены всем дочерним таблицам, а привилегии, которых у родителя нет, будут отозваны (с CASCADE).

  • Привилегии, которые проверяются, включают SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES и TRIGGER.

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

  • p_parent_table - родительская таблица набора секций. Должна быть указана с указанием схемы и соответствовать уже настроенному имени родительской таблицы в pg_partman.

apply_foreign_keys(p_parent_table text, p_child_table text DEFAULT NULL, p_job_id bigint DEFAULT NULL, p_debug boolean DEFAULT false) * ВАЖНО: Эта функция больше не требуется для PG11+ поскольку наследование FK автоматически управляется. * Применяет любые внешние ключи, которые существуют на родительской таблице в наборе секций к всем дочерним таблицам для PG10 и старше. * Эта функция автоматически вызывается при создании новой дочерней таблицы, поэтому нет необходимости вручную запускать ее, если вам нужно исправить существующую дочернюю таблицу. * Если вам нужно применить это к всему набору секций, см. reapply_foreign_keys.py python скрипт. Это приведет к фиксации после каждого создания FK, чтобы избежать конфликтов. * Эта функция может быть использована на любом наборе наследования таблиц, не только на тех, которые управляются pg_partman. * Параметр p_job_id является необязательным. Он предназначен для внутреннего использования и позволяет логирование работы быть объединенным в исходную работу, которая вызвала эту функцию, если это применимо. * Параметр p_debug покажет вам оператор создания ограничения, который был использован.

stop_sub_partition(p_parent_table text, p_jobmon boolean DEFAULT true) RETURNS boolean. * По умолчанию, если отменить дочернюю таблицу, которая также является разделена на секции, это не приведет к тому, что дочерние таблицы родительского набора секций не будут разделены на секции родительского набора секций, если только родительский набор секций также не будет отменен. если родительский набор также не отменен. Чтобы справиться с этой ситуацией, когда вы можете вы не удаляете родительский набор, но не хотите иметь дополнительных дочерних секций, можно использовать эту функцию. * Эта функция просто удаляет запись parent_table из таблицы таблицы part_config_sub. Но это дает предсказуемый, программный способ, а также обеспечивает протоколирование jobmon для этой операции. операции.

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

undo_partition(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval text DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0, p_target_table text DEFAULT NULL, p_ignored_columns text[] DEFAULT NULL, p_drop_cascade boolean DEFAULT false, OUT partitions_undone int, OUT rows_undone bigint) RETURNS record

  • Отменить набор секций, созданных с помощью pg_partman. Эта функция ПЕРЕМЕЩАЕТ данные из дочерних таблиц либо в родительскую таблицу (ненативную), либо в указанную целевую таблицу (нативную).

  • Если вы пытаетесь автоматически отменить разделение большого количества данных, рекомендуется либо использовать скрипт undo_partition.py для коммита данных в меньших партиях. Или, если вы используете PG11+, используйте процедуру undo_partition_data() для выполнения той же операции. Это значительно снизит проблемы, вызванные длительными транзакциями и конфликтом данных.

  • При выполнении этой функции, столбец undo_in_progress в таблице конфигурации устанавливается в значение true. Это приводит к остановке создания всех секций и управления их сохранением.

  • По умолчанию секции не УДАЛЯЮТСЯ (DROPPED), они ОТСОЕДИНЯЮТСЯ/ОТВЯЗЫВАЮТСЯ (UNINHERITED/UNATTACHED). Это оставляет предыдущие дочерние таблицы пустыми, независимыми таблицами.

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

  • Без ручной установки какого-либо аргумента пакета, каждый запуск функции переместит все данные из одной секции в родительский/целевой.

  • Когда все дочерние таблицы были отсоединены/удалены, конфигурационные данные автоматически удаляются из pg_partman.

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

  • p_parent_table - родительская таблица набора секций. Должна быть указана с указанием схемы и соответствовать уже настроенному имени родительской таблицы в pg_partman.

  • p_batch_count - необязательный аргумент, который устанавливает количество перемещений данных, равное аргументу p_batch_interval (или интервалу раздела по умолчанию, если не установлен), в одном запуске функции. По умолчанию равно 1.

  • p_batch_interval - необязательный аргумент. Интервал времени или id, на который нужно переместить данные. Этот интервал может быть меньше интервала секции, что позволяет разбивать очень большие секции на более мелкие партии коммита. По умолчанию используется сконфигурированный интервал секции если он не задан или если задан интервал, больший, чем интервал секции. Обратите внимание, что значение должно быть задано в виде текста для этого параметра.

  • p_keep_table - необязательный аргумент, установка его значения в false приведет к удалению старой дочерней таблицы вместо отсоединения/открепления после перемещения всех ее данных. Обратите внимание, что для фактического удаления таблицы из набора требуется как минимум две партии.

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

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

  • p_ignored_columns - Эта опция позволяет фильтровать определенные столбцы при перемещении данных из дочерних таблиц в целевую таблицу. Это обычно требуется только при использовании столбцов со значением GENERATED ALWAYS, поскольку прямая вставка значения приведет к ошибке при перемещении данных. Значение представляет собой массив текстовых имен столбцов.

  • p_drop_cascade - Позволяет отменить подчиненные наборы подсекций от родительских таблиц выше в иерархии наследования. Применяется только при установке p_keep_tables в значение false. Обратите внимание, что это приводит к удалению всех дочерних таблиц ниже родительской таблицы подсекций при удалении этой родительской таблицы.

  • Возвращает количество отмененных секций и количество строк, перемещенных в родительскую таблицу. Значение отмененных секций равно -1, если возникает проблема.

undo_partition_proc(p_parent_table text, p_interval text DEFAULT NULL, p_batch int DEFAULT NULL, p_wait int DEFAULT 1, p_target_table text DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait int DEFAULT 0, p_lock_wait_tries int DEFAULT 10, p_quiet boolean DEFAULT false, p_ignored_columns text[] DEFAULT NULL, p_drop_cascade boolean DEFAULT false)

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

  • Работает только с PostgreSQL 11+

  • Вызывает функцию undo_partition() в цикле, подтверждая изменения при необходимости.

  • p_parent_table - Родительская таблица уже созданного набора секций.

  • p_interval - Значение, передаваемое в функцию undo_partition в качестве аргумента p_batch_interval. Используйте это значение, чтобы установить интервал, меньший, чем интервал секции, для фиксации данных пакетами меньшего размера. Если значение не указано, по умолчанию используется интервал секции.

  • p_batch - Сколько раз пройти через значение, заданное для –interval. Если –interval не установлен, будет использоваться интервал разделения по умолчанию и отменяться не более -b секций. Процедура фиксирует изменения в конце каждой отдельной партии. (НЕ передается как p_batch_count в функцию undo_partition). Если не установлено, все данные во всем наборе секций будут перемещены в одном запуске процедуры.

  • p_wait - Заставляет процедуру ожидать заданное количество секунд между фиксацией (партиями), чтобы снизить нагрузку на запись.

  • p_target_table - То же самое, что и опция p_target_table в функции undo_partition().

  • p_keep_table - То же самое, что и опция p_keep_table в функции undo_partition().

  • p_lock_wait - Параметр, передаваемый непосредственно в основную функцию partition_data_*(). Количество секунд ожидания строк, которые могут быть заблокированы другой транзакцией. По умолчанию ожидать вечно (0).

  • p_lock_wait_tries - Параметр, устанавливающий, сколько раз процедура будет пытаться ожидать заданное время для p_lock_wait. По умолчанию 10 попыток.

  • p_quiet - Процедуры не могут возвращать значения, поэтому по умолчанию они выводят УВЕДОМЛЕНИЯ, чтобы показать прогресс. Установите эту опцию, чтобы подавить эти уведомления.

  • p_ignored_columns - Эта опция позволяет фильтровать определенные столбцы при перемещении данных из дочерних таблиц в целевую таблицу. Это обычно требуется только при использовании столбцов со значением GENERATED ALWAYS, поскольку прямая вставка значения приведет к ошибке при перемещении данных. Значение представляет собой массив текстовых имен столбцов.

  • p_drop_cascade - Позволяет отменить подчиненные наборы подсекций от родительских таблиц выше в иерархии наследования. Применяется только при установке p_keep_tables в значение false. Обратите внимание, что это приводит к удалению всех дочерних таблиц ниже родительской таблицы подсекций при удалении этой родительской таблицы.

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

  • Эта функция используется для удаления дочерних таблиц из набора секций, основанных на времени. По умолчанию таблица просто отсоединяется, а не удаляется фактически. Для автоматического удаления старых таблиц рекомендуется использовать функцию run_maintenance() с настроенным сроком хранения вместо прямого вызова этой функции.

  • p_parent_table- существующая родительская таблица набора секций с временным критерием. ДОЛЖНА быть указана схемой, даже если она находится в общей схеме.

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

  • p_keep_table - необязательный параметр, который сообщает partman, следует ли сохранять или удалять таблицу в дополнение к ее отмене наследования. TRUE означает, что таблица на самом деле не будет удалена; FALSE означает, что таблица будет удалена. Эта функция будет просто использовать значение, настроенное в part_config, если оно не установлено явно. Этот параметр игнорируется, если установлен retention_schema.

  • p_keep_index - необязательный параметр, который сообщает partman, следует ли сохранять или удалять индексы дочерней таблицы, когда она не наследуется. TRUE означает, что индексы будут сохранены; FALSE означает, что все индексы будут удалены. Эта функция просто использует значение, настроенное в part_config, если оно не установлено явно. Этот параметр игнорируется, если p_keep_table установлен в FALSE или если установлен retention_schema.

  • p_retention_schema - необязательный параметр, указывающий partman на перемещение таблицы в другую схему вместо ее удаления. Установите это значение в схему, в которую вы хотите переместить таблицу. Эта функция будет использовать значение, настроенное в part_config, если оно не задано явно. Если этот параметр установлен, параметры удержания p_keep_table & p_keep_index игнорируются.

  • p_reference_timestamp - необязательный параметр, который указывает partman использовать другую временную метку в качестве опорной для определения, какие секции должны быть затронуты, значение по умолчанию - CURRENT_TIMESTAMP.

  • Возвращает количество затронутых секций.

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

  • Эта функция используется для удаления дочерних таблиц из набора секций, основанных на id. По умолчанию таблица просто отсоединяется, а не удаляется фактически. Для автоматического удаления старых таблиц рекомендуется использовать функцию run_maintenance() с настроенным сроком хранения вместо прямого вызова этой функции.

  • p_parent_table- существующая родительская таблица набора секций с временным критерием. ДОЛЖНА быть указана схемой, даже если она находится в общей схеме.

  • p_retention - необязательный параметр для указания целочисленного интервала удержания и немедленного удаления таблиц, содержащих только данные, меньшие текущего максимального значения id, минус заданное значение удержания. Если у вас уже установлено значение удержания в таблице конфигурации, функция будет использовать его, в противном случае это переопределит его. В противном случае, этот параметр является обязательным. См. раздел Описание выше для получения дополнительной информации о настройках удержания.

  • p_keep_table - необязательный параметр, который сообщает partman, следует ли сохранять или удалять таблицу в дополнение к ее отмене наследования. TRUE означает, что таблица на самом деле не будет удалена; FALSE означает, что таблица будет удалена. Эта функция будет просто использовать значение, настроенное в part_config, если оно не установлено явно. Этот параметр игнорируется, если установлен retention_schema.

  • p_keep_index - необязательный параметр, который сообщает partman, следует ли сохранять или удалять индексы дочерней таблицы, когда она не наследуется. TRUE означает, что индексы будут сохранены; FALSE означает, что все индексы будут удалены. Эта функция просто использует значение, настроенное в part_config, если оно не установлено явно. Этот параметр игнорируется, если p_keep_table установлен в FALSE или если установлен retention_schema.

  • p_retention_schema - необязательный параметр, указывающий partman на перемещение таблицы в другую схему вместо ее удаления. Установите это значение в схему, куда вы хотите переместить таблицу. Эта функция будет использовать значение, настроенное в part_config, если оно не установлено явно. Если этот параметр установлен, параметры сохранения p_keep_table & p_keep_index игнорируются.

  • Возвращает количество затронутых секций.

drop_partition_column(p_parent_table text, p_column text) RETURNS void

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

  • Это должно быть актуально только для ненативных настроек секций.

  • Используйте оператор IF EXISTS во всех операторах DROP, чтобы избежать возможных предупреждений о том, что столбец не найден. Вы можете безопасно игнорировать эти предупреждения. Ошибок не должно возникать.

F.41.9.4. Таблицы

part_config

Хранит все конфигурационные данные для наборов секций, управляемых расширением.

  • parent_table

    • Родительская таблица набора секций

  • control

    • Столбец, используемый в качестве контроля для ограничений разделения. Должен быть столбцом, основанным на времени или целочисленным.

  • partition_type

    • Тип разделения. Должен быть одним из указанных типов в информации create_parent() выше.

  • partition_interval

    • Текстовый тип значения, определяющий интервал для каждого раздела.

    • Должно быть значение, которое может быть приведено к типам данных интервал или bigint.

  • constraint_cols

    • Массивная колонка, которая перечисляет колонки для применения дополнительных ограничений. Смотрите раздел "Об этом" для получения дополнительной информации о том, как работает эта функция.

  • premake

    • Сколько секций нужно заранее создать перед текущим разделом. По умолчанию - 4.

  • optimize_trigger

    • Управляет количеством секций, которые обрабатываются наиболее эффективно с помощью триггера. См. функцию create_parent() для получения дополнительной информации. По умолчанию 4.

    • Этот вариант игнорируется для нативного разделения.

  • optimize_constraint

    • Управляет тем, какие старые таблицы получают дополнительные ограничения, если это настроено. См. раздел Описание для получения дополнительной информации. По умолчанию 30.

  • epoch

    • Отметьте таблицу для разделения по времени с использованием целочисленного значения эпохи вместо метки времени. См. функцию create_parent() для получения дополнительной информации. По умолчанию none.

  • inherit_fk

    • Установите, управляет ли pg_partman наследованием внешних ключей от родительской таблицы ко всем дочерним таблицам.

    • Значение по умолчанию - TRUE. Может быть установлено с помощью функции create_parent() при создании.

    • Этот вариант игнорируется для нативного разделения.

  • retention

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

    • Должно быть значение, которое может быть приведено к интервалу (для разделения на основе времени) или к типу данных bigint (для последовательного разделения).

    • Оставьте эту колонку NULL (по умолчанию), чтобы всегда сохранять все дочерние секции. Смотрите О разделе для получения дополнительной информации.

  • retention_schema

    • Схема для перемещения таблиц в рамках системы сохранения вместо их удаления. Переопределяет параметры retention_keep_*.

  • retention_keep_table

    • Логическое значение, определяющее, сохраняются ли удаленные дочерние таблицы или фактически удаляются.

    • По умолчанию значение TRUE сохраняет таблицу и только отменяет наследование от нее. Установите значение FALSE, чтобы полностью удалить дочерние таблицы из базы данных.

  • retention_keep_index

    • Примечание: Эта настройка не влияет на наследование секций в PG11+. Вы не можете удалить индексы, унаследованные нативно.

    • Логическое значение для определения, будут ли удалены индексы для дочерних таблиц, которые не наследуются.

    • По умолчанию значение TRUE. Установите значение FALSE, чтобы удалить индексы дочерней таблицы при отсутствии наследования.

  • infinite_time_partitions

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

    • Если вы все еще хотите создать новые секции, несмотря на отсутствие новых данных, установите это значение на TRUE.

    • По умолчанию установлено значение false.

  • datetime_string

    • Для разделения по времени используется строка формата даты и времени при именовании дочерних секций.

  • automatic_maintenance

    • Флаг для установки автоматического управления обслуживанием, когда вызывается run_maintenance() без параметра таблицы или фоновым рабочим процессом.

    • Текущие допустимые значения: вкл и выкл. По умолчанию - вкл.

    • Когда установлено значение "выключено", run_maintenance() все равно может быть вызван для отдельного набора секций, передав его в качестве параметра функции.

  • jobmon

    • Булево значение, определяющее, используется ли расширение pg_jobmon для ведения журнала/мониторинга обслуживания секций. По умолчанию установлено значение true.

  • sub_partition_set_full

    • Логическое значение, обозначающее, что окончательное разбиение для набора подразделов было создано. Позволяет функции run_maintenance() работать более эффективно при наличии большого количества наборов подсекций.

  • undo_in_progress

    • Устанавливается функциями undo_partition при каждом их запуске. Если значение true, то это приводит к остановке всех операций создания и управления хранением секций функцией run_maintenance()). По умолчанию значение false.

  • trigger_exception_handling

    • Этот вариант игнорируется для нативного разделения.

    • Булево значение, которое может быть установлено для разрешения обработки исключений, возникающих при записи в эту таблицу триггерной функцией разделения. Обработка в данном случае означает помещение данных в родительскую таблицу для попытки предотвратить потерю данных в случае ошибок. Имейте в виду, что перехват исключения здесь переопределит любую другую обработку исключений, которая может быть выполнена при записи в этот разделенный набор (например, обработка нарушения уникального ограничения для его игнорирования). Просто наличие этого блока исключения также увеличит потребление xid, поскольку каждая вставленная строка будет увеличивать глобальное значение xid. Если у этой таблицы высокая скорость вставки, вы быстро можете достичь оборота xid, поэтому используйте это осторожно. По умолчанию это значение установлено в false, чтобы избежать неожиданного поведения в других ситуациях обработки исключений.

  • upsert

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

    • Перевод с английского на русский: текстовое значение для предложения ON CONFLICT, которое следует включить в триггер разделения. По умолчанию равно '' (пустая строка), что означает, что оно неактивно. См. определение функции create_parent()) и раздел Описание для получения дополнительной информации.

    • Этот параметр в настоящее время игнорируется для нативного разделения, так как нет триггера, но upsert все еще может работать в ограниченном режиме.

  • trigger_return_null

    • Логическое значение, которое позволяет контролировать поведение триггера разделения RETURN. По умолчанию оно равно true, и триггер возвращает NULL, чтобы предотвратить передачу данных как в родительскую таблицу, так и в дочерние. Однако, если у вас есть несколько триггеров и вы полагаетесь на возвращаемое значение NEW столбца, это может вызвать проблему. Установка этого значения конфигурации в false приведет к тому, что триггер разделения будет возвращать NEW. Затем вам придется обрабатывать возвращаемое значение в другом триггере соответствующим образом. В противном случае, новые данные будут попадать как в дочернюю, так и в родительскую таблицу набора разделения.

    • Этот вариант игнорируется для нативного разделения.

  • template_table

    • Схематически полное имя таблицы, используемой в качестве шаблона для применения любых опций наследования, не обрабатываемых встроенными опциями разделения в PG.

  • inherit_privileges

    • Устанавливает, наследовать ли владение/привилегии родительской таблицы для всех дочерних таблиц. По умолчанию true для неоригинальных и PG10. По умолчанию false для оригинальных PG11+ и должно быть необходимо только в случае, если вам нужен прямой доступ к дочерним таблицам, обходя родительскую таблицу.

  • constraint_valid

    • Логическое значение, которое позволяет создавать дополнительные ограничения, которые pg_partman может управлять за вас, как НЕДЕЙСТВИТЕЛЬНЫЕ. См. раздел "Исключение ограничений" в начале для получения более подробной информации об этих ограничениях. Это может позволить выполнять обслуживание намного быстрее на больших наборах секций, поскольку существующие данные не проверяются перед добавлением ограничения. Новые вставленные данные проверяются, поэтому это полностью безопасный вариант для обеспечения целостности данных. Обратите внимание, что исключение ограничений НЕ БУДЕТ работать, пока ограничения не будут проверены. По умолчанию установлено значение true, чтобы ограничения создавались как ДЕЙСТВИТЕЛЬНЫЕ (VALID). Установите значение false, чтобы новые ограничения создавались как НЕДЕЙСТВИТЕЛЬНЫЕ (NOT VALID).

  • subscription_refresh - Имя логической подписки на репликацию для обновления при выполнении обслуживания. Если набор секций подписан на публикацию, которая будет добавлять/удалять таблицы и вам нужно, чтобы ваш набор секций был в курсе этих изменений, необходимо указать это имя подписки с этой опцией. В противном случае подписка никогда не узнает о новых таблицах, добавленных к издателю, если вы не обновляете подписку каким-то другим способом. См. документацию PG для ALTER SUBSCRIPTION для получения дополнительной информации об обновлении подписок - ALTER SUBSCRIPTION

  • drop_cascade_fk - Позволяет каскадное удаление внешних ключей при удалении дочерней таблицы. Эта опция разрешена только при использовании не нативного разделения и не поддерживается при использовании разделений на подсекции.

part_config_sub

  • Хранит все конфигурационные данные для подразделенных наборов, управляемых pg_partman.

  • Столбец sub_parent является родительской таблицей набора подсекций, и все остальные столбцы определяют, как дочерние элементы этого родителя подразделяются.

  • Все остальные столбцы работают точно так же, как их аналоги в таблице part_config или как параметры, передаваемые в create_parent().

F.41.9.5. Скрипты

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

partition_data.py

  • ПРИМЕЧАНИЕ: Этот скрипт установлен только для PostgreSQL 10 и ниже. Он был заменен на partition_data_proc().

  • Скрипт на языке Python для упрощения разделения на пакеты при выполнении операций.

  • Скрипт в настоящее время не работает с нативным разделением.

  • Вызывает либо partition_data_time(), либо partition_data_id(), в зависимости от значения, указанного для –type.

  • Коммит выполняется в конце каждого интервала и/или полностью созданного раздела.

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

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

  • --parent (-p): Родительская таблица уже созданного набора секций. Обязательно.

  • --type (-t): Тип разделения. Допустимые значения: time и id. Обязательно.

  • --connection (-c): Строка подключения для использования psycopg. По умолчанию host= (локальный сокет).

  • --interval (-i) - Значение, передаваемое в функцию раздела в качестве аргумента p_batch_interval. Используйте это значение, чтобы установить интервал, меньший, чем интервал секции, для коммита данных пакетами меньшего размера. Если значение не указано, по умолчанию используется интервал секции.

  • --batch (-b) - Сколько раз пройти через значение, указанное для –interval. Если –interval не установлен, будет использоваться интервал разделения по умолчанию, и будет создано не более -b секций. Скрипт коммитит данные в конце каждой отдельной партии. (НЕ передается как p_batch_count в функцию разделения). Если не установлено, все данные в родительской таблице будут разделены в одном запуске скрипта.

  • --wait (-w): Заставляет скрипт приостановиться на заданное количество секунд между коммитами (партиями).

  • --order (-o): Позволяет указать порядок миграции данных от родительской таблицы к дочерним, либо по возрастанию (ASC), либо по убыванию (DESC). По умолчанию ASC.

  • --lockwait (-l): Устанавливает тайм-аут блокировки на перемещение данных на указанное количество секунд. Если блокировка не удается получить, данная партия будет повторно попытаться.

  • --lockwait_tries: Количество попыток ожидания блокировки перед отказом от разделения. По умолчанию 10.

  • --autovacuum_on: Отключение автоочистки требует кратковременной блокировки для изменения свойства таблицы. Установите эту опцию, чтобы оставить автоочистку включенной и избежать попытки блокировки.

  • --quiet (-q): Переключатель для остановки вывода во время и после разделения.

  • --version: Выводит минимальную версию pg_partman, с которой должен работать этот скрипт. Установленная версия pg_partman может быть выше этой.

  • --debug: Показывает дополнительный отладочный вывод.

  • Примеры:

Partition all data in a parent table. Commit after each partition is made.
      python partition_data.py -c "host=localhost dbname=mydb" -p schema.parent_table -t time
Partition by id in smaller intervals and pause between them for 5 seconds (assume >100 partition interval)
      python partition_data.py -p schema.parent_table -t id -i 100 -w 5
Partition by time in smaller intervals for at most 10 partitions in a single run (assume monthly partition interval)
      python partition_data.py -p schema.parent_table -t time -i "1 week" -b 10

undo_partition.py

  • ПРИМЕЧАНИЕ: Этот скрипт установлен только для PostgreSQL 10 и ниже. Он был заменен на undo_partition_proc().

  • Скрипт на языке Python для упрощения отмены разделения в зафиксированных партиях.

  • Также может работать с любым ненативным набором родительских/дочерних секций, не управляемых pg_partman, если не установлен параметр –type.

  • Этот скрипт вызывает функцию undo_partition(), undo_partition_time() или undo_partition_id в зависимости от значения, заданного для –type.

  • коммит выполняется в конце каждого –interval и/или очищенного раздела.

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

  • --parent (-p): Родительская таблица набора секций. Обязательно.

  • --type (-t): Тип разделения. Допустимые значения: time, id и native. Если этот аргумент не задан, будет использоваться undo_partition() и работать с любым ненативным набором родительских/дочерних таблиц.

  • --connection (-c): Строка подключения для использования psycopg. По умолчанию host= (локальный сокет).

  • --interval (-i) - Значение, передаваемое в функцию раздела в качестве p_batch_interval. Используйте это значение, чтобы установить интервал, меньший, чем интервал секции, для коммита данных пакетами меньшего размера. Если значение не указано, по умолчанию используется интервал секции.

  • --batch (-b): Сколько раз пройти через значение, заданное для –interval. Если –interval не установлен, будет использоваться интервал разделения по умолчанию и будет отменено не более -b секций. Скрипт коммитит изменения в конце каждой отдельной партии. (НЕ передается как p_batch_count в функцию отмены). Если не установлено, все данные будут перемещены в родительскую таблицу за один запуск скрипта.

  • --wait (-w): Заставляет скрипт приостановиться на заданное количество секунд между коммитами (партиями).

  • --droptable (-d): Переключатель для удаления дочерних таблиц, когда они пусты. Оставьте без параметра, чтобы просто отменить наследование.

  • --quiet (-q): Переключатель для остановки вывода во время и после отмены разделения.

  • --version: Выводит минимальную версию pg_partman, с которой должен работать этот скрипт. Установленная версия pg_partman может быть выше этой.

  • --debug: Показывает дополнительный отладочный выво.

dump_partition.py

  • Python-скрипт для выгрузки таблиц, содержащихся в указанной схеме. Использует pg_dump, создает файл хеша SHA-512 для выгрузки и затем удаляет таблицу.

  • В сочетании с параметром конфигурации retention_schema предоставляет надежный способ выгрузки таблиц, которые обычно просто удаляются системой удержания.

  • Таблицы не удаляются, если pg_dump не завершается успешно.

  • Параметры подключения для psycopg и pg_dump были разделены из-за различий в их требованиях в зависимости от конфигурации подключения к базе данных.

  • Все параметры по умолчанию для опций dump_* такие же, как у pg_dump, если они не указаны.

  • Будет работать с любой указанной схемой, не только с той, которая используется для управления удержанием pg_partman.

  • --schema (-n): Схема, содержащая таблицы, которые будут выгружены. (Обязательно).

  • --connection (-c): Строка подключения для использования psycopg. Роль, используемая, должна иметь возможность выбирать из pg_catalog.pg_tables в соответствующей базе данных и удалять все таблицы в указанной схеме. По умолчанию host= (локальный сокет). Обратите внимание, что это отличается от параметров, отправляемых в pg_dump.

  • --output (-o): Путь к месту выгрузки файла. По умолчанию - текущий каталог.

  • --dump_database (-d): Используется для pg_dump, аналогично его параметру –dbname или последнему параметру имени базы данных.

  • --dump_host: Используется для pg_dump, аналогично его параметру –host.

  • --dump_username: Используется для pg_dump, аналогично его параметру –username.

  • --dump_port: Используется для pg_dump, аналогично его параметру –port option.

  • --pg_dump_path: Путь к исполняемому файлу pg_dump. Должен быть указан, если он не находится в текущем PATH.

  • --Fp: Выгрузка в формате plain text с использованием pg_dump. По умолчанию - бинарный формат custom (-Fc).

  • --nohashfile: НЕ создавать отдельный файл с хешем SHA-512 выгрузки. Если файлы выгрузки очень большие, генерация хеша может занять много времени.

  • --nodrop: НЕ удалять таблицы из указанной схемы после выгрузки/хеширования.

  • --verbose (-v): Предоставить более подробный вывод.

  • --version: Выводит минимальную версию pg_partman, с которой должен работать этот скрипт. Установленная версия pg_partman может быть выше этой.

vacuum_maintenance.py

  • Скрипт на python для выполнения дополнительного обслуживания VACUUM на заданном наборе секций. Основная цель этого - предоставить более простой способ замораживания кортежей в старых секциях, в которые больше не производится запись. Это позволяет автовакууму безопасно пропускать их, не вызывая проблем с зацикливанием идентификатора транзакции. См. документацию Tantor SE для получения дополнительной информации об этой проблеме обслуживания: Раздел 24.1.5.

  • Очищает все дочерние таблицы в заданном наборе секций, у которых возраст (relfrozenxid) больше vacuum_freeze_min_age, включая родительскую таблицу.

  • Настоятельно рекомендуется запускать этот скрипт по расписанию с опцией –freeze, если у вас есть дочерние таблицы, в которые после определенного периода времени не происходят записи.

  • –parent (-р): Родительская таблица уже созданного набора секций. (Обязательно)

  • –connection (-c): Строка подключения для использования psycopg. По умолчанию установлено значение host= (локальный сокет).

  • –freeze (-z): Устанавливает опцию FREEZE для команды VACUUM.

  • –full (-f): Устанавливает опцию FULL для команды VACUUM. Обратите внимание, что –freeze не является обязательным, если вы установите это. Рекомендуется ознакоммиться с –dryrun перед запуском этой команды, так как она заблокирует все таблицы, с которыми она работает, возможно, включая родительскую.

  • –vacuum_freeze_min_age (-a): По умолчанию сценарий получает это значение из системных каталогов. Установка этого значения позволяет переопределить значение, полученное из базы данных. Обратите внимание, что это не изменяет значение в базе данных, а только значение, используемое этим сценарием.

  • –noparent: Обычно родительская таблица включается в список таблиц для очистки, если ее возраст (relfrozenxid) выше, чем vacuum_freeze_min_age. Установите это значение, чтобы исключить родительскую таблицу, даже если она соответствует этим критериям.

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

  • –quiet (-q): Выключить все выводы.

  • –debug: Показать дополнительный отладочный вывод.

reapply_indexes.py

  • Скрипт на языке Python для повторного применения индексов на дочерних таблицах в наборе секций после их изменения в родительской таблице.

  • ПРИМЕЧАНИЕ: Этот скрипт работает только с ненативным разделением и нативным разделением в PG10. Он не работает с PG11+ и, скорее всего, не понадобится, так как в этих версиях большинство индексов теперь управляются автоматически. Единственным исключением могут быть первичные и уникальные индексы, которые не включают разделительный столбец. К сожалению, нет простого способа управлять наследованием индексов в PG11+ с помощью этого скрипта, и это придется делать вручную.

  • Любые индексы, которые в настоящее время существуют на дочерних элементах и соответствуют определению на родительском элементе, останутся без изменений. Существует возможность воссоздать соответствующие индексы, а также первичный ключ, если это желательно.

  • Индексы, которых нет у родительского элемента, будут удалены у всех дочерних элементов.

  • Коммиты выполняются после удаления/создания каждого индекса, чтобы предотвратить длительные транзакции и блокировки.

  • ПРИМЕЧАНИЕ: Новые имена индексов создаются на основе имени дочерней таблицы и используемых столбцов, поэтому их именование может отличаться от имени, указанного в родительской таблице. Это делается для того, чтобы инструмент мог учитывать длинные или повторяющиеся имена индексов. Если имя индекса будет дублироваться, к нему добавляется инкрементный счетчик в конце имени индекса, чтобы его можно было создать. Сначала используйте опцию --dryrun, чтобы увидеть, что она сделает и какие имена могут вызвать обработку дубликатов таким образом.

  • --parent (-p): Родительская таблица уже созданного набора секций. Обязательно.

  • --connection (-c): Строка подключения для использования psycopg. По умолчанию host= (локальный сокет).

  • --concurrent: Создавать индексы с опцией CONCURRENTLY. Обратите внимание, что это не работает для первичных ключей, когда указана опция –primary.

  • --drop_concurrent: Удалять индексы одновременно при их повторном создании (PostgreSQL >= v9.2). Обратите внимание, что это не работает для первичных ключей, когда указан параметр –primary.

  • --recreate_all (-R): По умолчанию, если индекс существует на дочернем элементе и соответствует родительскому, он не будет изменен. Установка этой опции принудительно удалит и создаст все дочерние индексы. Будет учитывать опции –concurrent и –drop_concurrent, если они указаны. Не будет пересоздавать первичные ключи, если не указана опция –primary.

  • --primary: По умолчанию первичный ключ не пересоздается. Установите эту опцию, если это необходимо. Обратите внимание, что это приведет к исключающей блокировке дочерней таблицы на время пересоздания.

  • --jobs (-j): Используйте библиотеку multiprocessing Python для параллельного воссоздания индексов. Обратите внимание, что это происходит для каждой таблицы, а не для каждого индекса. Будьте очень осторожны при установке этой опции, если нагрузка является проблемой для ваших систем.

  • --wait (-w): Подождите указанное количество секунд после завершения создания индексов на таблице, прежде чем перейти к следующей операции. При использовании с -j это установит паузу между пакетами параллельных заданий.

  • --dryrun: Показывает, что скрипт будет делать, не запуская его на базе данных. Настоятельно рекомендуется просмотреть это перед запуском. Обратите внимание, что если несколько индексов получат одно и то же имя по умолчанию, дублированные имена будут отображаться в режиме пробного запуска (потому что индекс не существует в каталоге для проверки). При запуске реального скрипта дублированные имена будут обработаны, как указано в ПРИМЕЧАНИИ выше.

  • --quiet: Отключить все выводы.

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

  • --version: Выводит минимальную версию pg_partman, с которой должен работать этот скрипт. Установленная версия pg_partman может быть выше этой.

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

reapply_foreign_keys.py

  • ПРИМЕЧАНИЕ: Этот скрипт установлен только для PostgreSQL 10 и ниже.

  • Скрипт на языке Python для переопределения унаследованных внешних ключей для всего набора секций.

  • Сценарий в настоящее время не работает с нативным разделением, а внешние ключи обрабатываются нативно начиная с версии PG11+.

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

  • Коммиты выполняются после создания каждого внешнего ключа, чтобы избежать длительных периодов конфликта.

  • --parent (-p): Родительская таблица уже созданного набора секций. (Обязательно)

  • --connection (-c): Строка подключения для использования psycopg. По умолчанию host= (локальный сокет).

  • --quiet (-q): Переключатель для остановки вывода во время и после отмены разделения.

  • --dryrun: Показывает, что скрипт будет делать, не запуская его на базе данных. Настоятельно рекомендуется просмотреть это перед запуском.

  • --nonpartman Если набор секций, на котором вы запускаете это, не управляется pg_partman, установите этот флаг. В противном случае будут использоваться внутренние функции pg_partman, и этот скрипт может не работать. Когда этот флаг установлен, порядок переиндексации таблиц является алфавитным, а не логическим.

  • --version: Выводит минимальную версию pg_partman, с которой должен работать этот скрипт. Установленная версия pg_partman может быть выше этой.

  • --debug: Показывает дополнительный отладочный выво.

check_unique_constraints.py

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

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

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

  • --parent (-p): Родительская таблица набора секций, которую необходимо проверить. (Обязательно)

  • --column_list (-l): Список столбцов, разделенных запятыми, которые составляют уникальное ограничение для проверки. (Обязательно)

  • --connection (-c): Строка подключения для использования psycopg. По умолчанию host= (локальный сокет).

  • --temp (-t): Путь к папке, в которую можно записывать и использовать временные рабочие файлы. По умолчанию используется системная папка для временных файлов.

  • --psql: Полный путь к исполняемому файлу psql, если он не находится в текущем пути PATH.

  • --simple: Выводит одно целочисленное значение с общим количеством дубликатов. Используйте это для программного обеспечения мониторинга, которое требует проверки простого значения.

  • --quiet (-q): Подавить все выводы, кроме случаев обнаружения нарушения ограничений.

  • --version: Выводит минимальную версию pg_partman, с которой должен работать этот скрипт. Установленная версия pg_partman может быть выше этой.

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

Это руководство покажет вам некоторые примеры того, как настроить как простую, одноуровневую разбивку, так и многоуровневую подразбивку. Оно также покажет вам, как разделить данные из таблицы, которая уже содержит данные (см. Подраздел ID->ID->ID) и отменить разбивку существующего набора секций. Для получения более подробной информации о том, что делает каждая функция и о дополнительных возможностях в этом расширении, пожалуйста, см. файл документации pg_partman.md. Примеры в этом документе предполагают, что вы используете как минимум v3.0.1 pg_partman. Если вам нужно руководство для предыдущей версии, пожалуйста, смотрите старые релизы, доступные на github.

Обратите внимание, что все примеры здесь предназначены для не встроенного, основанного на триггерах партицирования. Документация по встроенному партицированию находится в процессе разработки, но она в основном будет сосредоточена вокруг PostgreSQL 11, поскольку 10 версия имела очень ограниченную поддержку партицирования.

F.41.10.1. Простое разделение по времени: 1 раздел в день

    keith@keith=# \d partman_test.time_taptest_table
                  Table "partman_test.time_taptest_table"
     Column |           Type           | Collation | Nullable | Default 
    --------+--------------------------+-----------+----------+---------
     col1   | integer                  |           | not null | 
     col2   | text                     |           |          | 
     col3   | timestamp with time zone |           | not null | now()
    Indexes:
        "time_taptest_table_pkey" PRIMARY KEY, btree (col1)

    keith@keith=# SELECT partman.create_parent('partman_test.time_taptest_table', 'col3', 'partman', 'daily');
     create_parent 
    ---------------
     t
    (1 row)

    keith@keith=# \d+ partman_test.time_taptest_table
                                      Table "partman_test.time_taptest_table"
     Column |           Type           | Collation | Nullable | Default | Storage  | Stats target | Description 
    --------+--------------------------+-----------+----------+---------+----------+--------------+-------------
     col1   | integer                  |           | not null |         | plain    |              | 
     col2   | text                     |           |          |         | extended |              | 
     col3   | timestamp with time zone |           | not null | now()   | plain    |              | 
    Indexes:
        "time_taptest_table_pkey" PRIMARY KEY, btree (col1)
    Triggers:
        time_taptest_table_part_trig BEFORE INSERT ON partman_test.time_taptest_table FOR EACH ROW EXECUTE PROCEDURE partman_test.time_taptest_table_part_trig_func()
    Child tables: partman_test.time_taptest_table_p2017_03_23,
                  partman_test.time_taptest_table_p2017_03_24,
                  partman_test.time_taptest_table_p2017_03_25,
                  partman_test.time_taptest_table_p2017_03_26,
                  partman_test.time_taptest_table_p2017_03_27,
                  partman_test.time_taptest_table_p2017_03_28,
                  partman_test.time_taptest_table_p2017_03_29,
                  partman_test.time_taptest_table_p2017_03_30,
                  partman_test.time_taptest_table_p2017_03_31

Функция триггера наиболее эффективно охватывает определенный период времени за 4 дня до и 4 дня после сегодняшнего дня. Это можно настроить с помощью опции конфигурации optimize_trigger в таблице part_config. Вне этого, динамическое выражение пытается найти соответствующую дочернюю таблицу для вставки данных. Обратите внимание, что это динамическое выражение гораздо менее эффективно, поскольку требуется поиск в каталоге, и план выражения не может быть так же хорошо кеширован, как и поиск существования дочерней таблицы. Если дочерняя таблица вообще не существует для данного временного значения, данные переходят к родительской:

keith@keith=# \sf partman_test.time_taptest_table_part_trig_func 
CREATE OR REPLACE FUNCTION partman_test.time_taptest_table_part_trig_func()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
            DECLARE
            v_count                 int;
            v_partition_name        text;
            v_partition_timestamp   timestamptz;
        BEGIN 
        IF TG_OP = 'INSERT' THEN 
            v_partition_timestamp := date_trunc('day', NEW.col3);
            IF NEW.col3 >= '2017-03-27 00:00:00-04' AND NEW.col3 < '2017-03-28 00:00:00-04' THEN 
            INSERT INTO partman_test.time_taptest_table_p2017_03_27 VALUES (NEW.*) ; 
            ELSIF NEW.col3 >= '2017-03-26 00:00:00-04' AND NEW.col3 < '2017-03-27 00:00:00-04' THEN 
                INSERT INTO partman_test.time_taptest_table_p2017_03_26 VALUES (NEW.*) ; 
            ELSIF NEW.col3 >= '2017-03-28 00:00:00-04' AND NEW.col3 < '2017-03-29 00:00:00-04' THEN 
                INSERT INTO partman_test.time_taptest_table_p2017_03_28 VALUES (NEW.*) ;
            ELSIF NEW.col3 >= '2017-03-25 00:00:00-04' AND NEW.col3 < '2017-03-26 00:00:00-04' THEN 
                INSERT INTO partman_test.time_taptest_table_p2017_03_25 VALUES (NEW.*) ; 
            ELSIF NEW.col3 >= '2017-03-29 00:00:00-04' AND NEW.col3 < '2017-03-30 00:00:00-04' THEN 
                INSERT INTO partman_test.time_taptest_table_p2017_03_29 VALUES (NEW.*) ;
            ELSIF NEW.col3 >= '2017-03-24 00:00:00-04' AND NEW.col3 < '2017-03-25 00:00:00-04' THEN 
                INSERT INTO partman_test.time_taptest_table_p2017_03_24 VALUES (NEW.*) ; 
            ELSIF NEW.col3 >= '2017-03-30 00:00:00-04' AND NEW.col3 < '2017-03-31 00:00:00-04' THEN 
                INSERT INTO partman_test.time_taptest_table_p2017_03_30 VALUES (NEW.*) ;
            ELSIF NEW.col3 >= '2017-03-23 00:00:00-04' AND NEW.col3 < '2017-03-24 00:00:00-04' THEN 
                INSERT INTO partman_test.time_taptest_table_p2017_03_23 VALUES (NEW.*) ; 
            ELSIF NEW.col3 >= '2017-03-31 00:00:00-04' AND NEW.col3 < '2017-04-01 00:00:00-04' THEN 
                INSERT INTO partman_test.time_taptest_table_p2017_03_31 VALUES (NEW.*) ;
            ELSE
                v_partition_name := partman.check_name_length('time_taptest_table', to_char(v_partition_timestamp, 'YYYY_MM_DD'), TRUE);
                SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = 'partman_test'::name AND tablename = v_partition_name::name;
                IF v_count > 0 THEN 
                    EXECUTE format('INSERT INTO %I.%I VALUES($1.*) ', 'partman_test', v_partition_name) USING NEW;
                ELSE
                    RETURN NEW;
                END IF;
            END IF;
        END IF;
        RETURN NULL;
        END $function$

F.41.10.2. Простой серийный ID: 1 раздел на 10 значений ID, начиная с пустой таблицы

    keith=# \d partman_test.id_taptest_table
                   Table "partman_test.id_taptest_table"
     Column |           Type           |           Modifiers            
    --------+--------------------------+--------------------------------
     col1   | integer                  | not null
     col2   | text                     | not null default 'stuff'::text
     col3   | timestamp with time zone | default now()
    Indexes:
        "id_taptest_table_pkey" PRIMARY KEY, btree (col1)


    keith=# SELECT create_parent('partman_test.id_taptest_table', 'col1', 'partman', '10');
     create_parent 
    ---------------
     t
    (1 row)


    keith=# \d+ partman_test.id_taptest_table
                                       Table "partman_test.id_taptest_table"
     Column |           Type           |           Modifiers            | Storage  | Stats target | Description 
    --------+--------------------------+--------------------------------+----------+--------------+-------------
     col1   | integer                  | not null                       | plain    |              | 
     col2   | text                     | not null default 'stuff'::text | extended |              | 
     col3   | timestamp with time zone | default now()                  | plain    |              | 
    Indexes:
        "id_taptest_table_pkey" PRIMARY KEY, btree (col1)
    Triggers:
        id_taptest_table_part_trig BEFORE INSERT ON partman_test.id_taptest_table FOR EACH ROW EXECUTE PROCEDURE partman_test.id_taptest_table_part_trig_func()
    Child tables: partman_test.id_taptest_table_p0,
                  partman_test.id_taptest_table_p10,
                  partman_test.id_taptest_table_p20,
                  partman_test.id_taptest_table_p30,
                  partman_test.id_taptest_table_p40

Эта функция триггера наиболее эффективно обеспечивает покрытие для 4x10 интервалов выше текущего максимума (0). Как только максимальный id достигает более высоких значений, он также эффективно покрывает до 4x10 интервалов позади текущего максимума. За пределами этого, динамический оператор пытается найти подходящую дочернюю таблицу для вставки данных. И как я сказал выше для времени, динамическая часть менее эффективна.

    keith@keith=# \sf partman_test.id_taptest_table_part_trig_func 
    CREATE OR REPLACE FUNCTION partman_test.id_taptest_table_part_trig_func()
     RETURNS trigger
     LANGUAGE plpgsql
    AS $function$ 
        DECLARE
            v_count                     int;
            v_current_partition_id      bigint;
            v_current_partition_name    text;
            v_id_position               int;
            v_last_partition            text := 'id_taptest_table_p40';
            v_next_partition_id         bigint;
            v_next_partition_name       text;
            v_partition_created         boolean;
        BEGIN
        IF TG_OP = 'INSERT' THEN 
            IF NEW.col1 >= 0 AND NEW.col1 < 10 THEN  
                INSERT INTO partman_test.id_taptest_table_p0 VALUES (NEW.*) ; 
            ELSIF NEW.col1 >= 10 AND NEW.col1 < 20 THEN 
                INSERT INTO partman_test.id_taptest_table_p10 VALUES (NEW.*) ;
            ELSIF NEW.col1 >= 20 AND NEW.col1 < 30 THEN 
                INSERT INTO partman_test.id_taptest_table_p20 VALUES (NEW.*) ;
            ELSIF NEW.col1 >= 30 AND NEW.col1 < 40 THEN 
                INSERT INTO partman_test.id_taptest_table_p30 VALUES (NEW.*) ;
            ELSIF NEW.col1 >= 40 AND NEW.col1 < 50 THEN 
                INSERT INTO partman_test.id_taptest_table_p40 VALUES (NEW.*) ;
            ELSE
                v_current_partition_id := NEW.col1 - (NEW.col1 % 10);
                v_current_partition_name := partman.check_name_length('id_taptest_table', v_current_partition_id::text, TRUE);
                SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = 'partman_test'::name AND tablename = v_current_partition_name::n
    ame;
                IF v_count > 0 THEN 
                    EXECUTE format('INSERT INTO %I.%I VALUES($1.*) ', 'partman_test', v_current_partition_name) USING NEW;
                ELSE
                    RETURN NEW;
                END IF;
            END IF;
        END IF;
        RETURN NULL;
        END $function$

F.41.10.3. Простой серийный ID: 1 раздел на 10 значений ID, начиная с пустой таблицы и используя upsert для удаления конфликтующих строк

    Uses same example table as above

    keith@keith=# SELECT partman.create_parent('partman_test.id_taptest_table', 'col1', 'partman', '10', p_upsert := 'ON CONFLICT (col1) DO NOTHING');
     create_parent 
    ---------------
     t
    (1 row)

    keith@keith=# \d+ partman_test.id_taptest_table
                                          Table "partman_test.id_taptest_table"
     Column |           Type           | Collation | Nullable |    Default    | Storage  | Stats target | Description 
    --------+--------------------------+-----------+----------+---------------+----------+--------------+-------------
     col1   | integer                  |           | not null |               | plain    |              | 
     col2   | text                     |           | not null | 'stuff'::text | extended |              | 
     col3   | timestamp with time zone |           |          | now()         | plain    |              | 
    Indexes:
        "id_taptest_table_pkey" PRIMARY KEY, btree (col1)
    Triggers:
        id_taptest_table_part_trig BEFORE INSERT ON partman_test.id_taptest_table FOR EACH ROW EXECUTE PROCEDURE partman_test.id_taptest_table_part_trig_func()
    Child tables: partman_test.id_taptest_table_p0,
                  partman_test.id_taptest_table_p10,
                  partman_test.id_taptest_table_p20,
                  partman_test.id_taptest_table_p30,
                  partman_test.id_taptest_table_p40

Кроме нового условия ON CONFLICT, эта функция триггера работает точно так же, как и предыдущий пример с ID.

    keith@keith=# \sf partman_test.id_taptest_table_part_trig_func 
    CREATE OR REPLACE FUNCTION partman_test.id_taptest_table_part_trig_func()
     RETURNS trigger
     LANGUAGE plpgsql
    AS $function$ 
        DECLARE
            v_count                     int;
            v_current_partition_id      bigint;
            v_current_partition_name    text;
            v_id_position               int;
            v_last_partition            text := 'id_taptest_table_p40';
            v_next_partition_id         bigint;
            v_next_partition_name       text;
            v_partition_created         boolean;
        BEGIN
        IF TG_OP = 'INSERT' THEN 
            IF NEW.col1 >= 0 AND NEW.col1 < 10 THEN  
                INSERT INTO partman_test.id_taptest_table_p0 VALUES (NEW.*) ON CONFLICT (col1) DO NOTHING; 
            ELSIF NEW.col1 >= 10 AND NEW.col1 < 20 THEN 
                INSERT INTO partman_test.id_taptest_table_p10 VALUES (NEW.*) ON CONFLICT (col1) DO NOTHING;
            ELSIF NEW.col1 >= 20 AND NEW.col1 < 30 THEN 
                INSERT INTO partman_test.id_taptest_table_p20 VALUES (NEW.*) ON CONFLICT (col1) DO NOTHING;
            ELSIF NEW.col1 >= 30 AND NEW.col1 < 40 THEN 
                INSERT INTO partman_test.id_taptest_table_p30 VALUES (NEW.*) ON CONFLICT (col1) DO NOTHING;
            ELSIF NEW.col1 >= 40 AND NEW.col1 < 50 THEN 
                INSERT INTO partman_test.id_taptest_table_p40 VALUES (NEW.*) ON CONFLICT (col1) DO NOTHING;
            ELSE
                v_current_partition_id := NEW.col1 - (NEW.col1 % 10);
                v_current_partition_name := partman.check_name_length('id_taptest_table', v_current_partition_id::text, TRUE);
                SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = 'partman_test'::name AND tablename = v_current_partition_name::n
    ame;
                IF v_count > 0 THEN 
                    EXECUTE format('INSERT INTO %I.%I VALUES($1.*) ON CONFLICT (col1) DO NOTHING', 'partman_test', v_current_partition_name) USING NEW;
                ELSE
                    RETURN NEW;
                END IF;
            END IF;
        END IF;
        RETURN NULL;
        END $function$

Выполнение следующего запроса приведет к вставке строки в таблицу

    keith@keith=# INSERT INTO partman_test.id_taptest_table(col1,col2) VALUES(1,'insert1');
    INSERT 0 0
    Time: 4.876 ms
    keith@keith=# SELECT * FROM partman_test.id_taptest_table;
     col1 |  col2   |             col3              
    ------+---------+-------------------------------
        1 | insert1 | 2017-03-27 14:23:02.769999-04
    (1 row)

Выполнение следующего запроса не вызовет ошибку, но строка в таблице не изменится, и col2 по-прежнему будет insert1

    keith@keith=# INSERT INTO partman_test.id_taptest_table(col1,col2) VALUES(1,'insert2');
    INSERT 0 0
    Time: 1.583 ms
    keith@keith=# SELECT * FROM partman_test.id_taptest_table;
     col1 |  col2   |             col3              
    ------+---------+-------------------------------
        1 | insert1 | 2017-03-27 14:23:02.769999-04
    (1 row)

F.41.10.4. Простой серийный ID: 1 раздел на 10 значений ID, начиная с пустой таблицы и используя upsert для обновления конфликтующих строк

    Uses same example table as above

    keith@keith=# SELECT partman.create_parent('partman_test.id_taptest_table', 'col1', 'partman', '10', p_upsert := 'ON CONFLICT (col1) DO UPDATE SET col2=EXCLUDED.col2, col3=EXCLUDED.col3');
     create_parent 
    ---------------
     t
    (1 row)

    keith@keith=# \d+ partman_test.id_taptest_table
                                          Table "partman_test.id_taptest_table"
     Column |           Type           | Collation | Nullable |    Default    | Storage  | Stats target | Description 
    --------+--------------------------+-----------+----------+---------------+----------+--------------+-------------
     col1   | integer                  |           | not null |               | plain    |              | 
     col2   | text                     |           | not null | 'stuff'::text | extended |              | 
     col3   | timestamp with time zone |           |          | now()         | plain    |              | 
    Indexes:
        "id_taptest_table_pkey" PRIMARY KEY, btree (col1)
    Triggers:
        id_taptest_table_part_trig BEFORE INSERT ON partman_test.id_taptest_table FOR EACH ROW EXECUTE PROCEDURE partman_test.id_taptest_table_part_trig_func()
    Child tables: partman_test.id_taptest_table_p0,
                  partman_test.id_taptest_table_p10,
                  partman_test.id_taptest_table_p20,
                  partman_test.id_taptest_table_p30,
                  partman_test.id_taptest_table_p40

Кроме нового условия ON CONFLICT, эта функция триггера работает точно так же, как и предыдущий пример с ID.

    keith@keith=# \sf partman_test.id_taptest_table_part_trig_func 
    CREATE OR REPLACE FUNCTION partman_test.id_taptest_table_part_trig_func()
     RETURNS trigger
     LANGUAGE plpgsql
    AS $function$ 
        DECLARE
            v_count                     int;
            v_current_partition_id      bigint;
            v_current_partition_name    text;
            v_id_position               int;
            v_last_partition            text := 'id_taptest_table_p40';
            v_next_partition_id         bigint;
            v_next_partition_name       text;
            v_partition_created         boolean;
        BEGIN
        IF TG_OP = 'INSERT' THEN 
            IF NEW.col1 >= 0 AND NEW.col1 < 10 THEN  
                INSERT INTO partman_test.id_taptest_table_p0 VALUES (NEW.*) ON CONFLICT (col1) DO UPDATE SET col2=EXCLUDED.col2, col3=EXCLUDED.col3; 
            ELSIF NEW.col1 >= 10 AND NEW.col1 < 20 THEN 
                INSERT INTO partman_test.id_taptest_table_p10 VALUES (NEW.*) ON CONFLICT (col1) DO UPDATE SET col2=EXCLUDED.col2, col3=EXCLUDED.col3;
            ELSIF NEW.col1 >= 20 AND NEW.col1 < 30 THEN 
                INSERT INTO partman_test.id_taptest_table_p20 VALUES (NEW.*) ON CONFLICT (col1) DO UPDATE SET col2=EXCLUDED.col2, col3=EXCLUDED.col3;
            ELSIF NEW.col1 >= 30 AND NEW.col1 < 40 THEN 
                INSERT INTO partman_test.id_taptest_table_p30 VALUES (NEW.*) ON CONFLICT (col1) DO UPDATE SET col2=EXCLUDED.col2, col3=EXCLUDED.col3;
            ELSIF NEW.col1 >= 40 AND NEW.col1 < 50 THEN 
                INSERT INTO partman_test.id_taptest_table_p40 VALUES (NEW.*) ON CONFLICT (col1) DO UPDATE SET col2=EXCLUDED.col2, col3=EXCLUDED.col3;
            ELSE
                v_current_partition_id := NEW.col1 - (NEW.col1 % 10);
                v_current_partition_name := partman.check_name_length('id_taptest_table', v_current_partition_id::text, TRUE);
                SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = 'partman_test'::name AND tablename = v_current_partition_name::n
    ame;
                IF v_count > 0 THEN 
                    EXECUTE format('INSERT INTO %I.%I VALUES($1.*) ON CONFLICT (col1) DO UPDATE SET col2=EXCLUDED.col2, col3=EXCLUDED.col3', 'partman_test
    ', v_current_partition_name) USING NEW;
                ELSE
                    RETURN NEW;
                END IF;
            END IF;
        END IF;
        RETURN NULL;
        END $function$

Выполнение следующего запроса приведет к вставке строки в таблицу

    keith@keith=# INSERT INTO partman_test.id_taptest_table(col1,col2) VALUES(1,'insert1');
    INSERT 0 0
    Time: 6.012 ms
    keith@keith=# SELECT * FROM partman_test.id_taptest_table;
     col1 |  col2   |             col3             
    ------+---------+------------------------------
        1 | insert1 | 2017-03-27 14:32:26.59552-04
    (1 row)

Выполнение следующего запроса не вызовет ошибку, и строка в таблице изменится, а col2 теперь будет insert2 и метка времени в col3 обновится до значения по умолчанию now()

    keith@keith=# INSERT INTO partman_test.id_taptest_table(col1,col2) VALUES(1,'insert2');
    INSERT 0 0
    Time: 4.235 ms
    keith@keith=# SELECT * FROM partman_test.id_taptest_table;
     col1 |  col2   |             col3              
    ------+---------+-------------------------------
        1 | insert2 | 2017-03-27 14:33:00.949928-04
    (1 row)

F.41.10.5. Подразделение по времени->Время->Время: Годовое -> Ежемесячное -> Ежедневное

    keith@keith=# \d partman_test.time_taptest_table
                  Table "partman_test.time_taptest_table"
     Column |           Type           | Collation | Nullable | Default 
    --------+--------------------------+-----------+----------+---------
     col1   | integer                  |           | not null | 
     col2   | text                     |           |          | 
     col3   | timestamp with time zone |           | not null | now()
    Indexes:
        "time_taptest_table_pkey" PRIMARY KEY, btree (col1)

Создайте верхний набор секций по годам, который охватывает только 2 года вперед/назад

    keith@keith=# SELECT partman.create_parent('partman_test.time_taptest_table', 'col3', 'partman', 'yearly', p_premake := 2);
     create_parent 
    ---------------
     t
    (1 row)

Теперь скажите pg_partman разделить все годовые дочерние таблицы по месяцам. Сделайте это, указав ему родительскую таблицу набора годовых секций (случайно совпадает с указанным выше)

    keith@keith=# SELECT partman.create_sub_parent('partman_test.time_taptest_table', 'col3', 'partman', 'monthly', p_premake := 2);
     create_sub_parent 
    -------------------
     t
    (1 row)

    keith@keith=# SELECT tablename FROM pg_tables WHERE schemaname = 'partman_test' ORDER BY tablename;
                 tablename             
    -----------------------------------
     time_taptest_table
     time_taptest_table_p2015
     time_taptest_table_p2015_p2015_01
     time_taptest_table_p2016
     time_taptest_table_p2016_p2016_01
     time_taptest_table_p2017
     time_taptest_table_p2017_p2017_01
     time_taptest_table_p2017_p2017_02
     time_taptest_table_p2017_p2017_03
     time_taptest_table_p2017_p2017_04
     time_taptest_table_p2017_p2017_05
     time_taptest_table_p2018
     time_taptest_table_p2018_p2018_01
     time_taptest_table_p2019
     time_taptest_table_p2019_p2019_01
    (15 rows)

День обновления этого учебника - 2017-03-27. Теперь вы видите, что это приводит только к созданию 2 новых будущих секций. И для месячных секций они также были созданы на 2 месяца вперед. Обратите внимание, что триггер все еще будет покрывать 4 вперед и 4 назад для обоих уровней разделения, если вы не измените опцию optimize_trigger в конфигурационной таблице. Родительская таблица ВСЕГДА имеет хотя бы одного потомка, поэтому для временного периода, который выходит за рамки того, что покрывает предварительное создание, была создана всего одна таблица для самого раннего возможного месяца в этом годовом периоде (январь). Теперь скажите pg_partman разделить каждую месячную таблицу, которая существует на данный момент, по дням. Сделайте это, указав ему родительскую таблицу каждого набора месячных секций (родитель только с годовым суффиксом, так как его дети - это месячные секции).

    SELECT partman.create_sub_parent('partman_test.time_taptest_table_p2015', 'col3', 'partman', 'daily', p_premake := 2);
    SELECT partman.create_sub_parent('partman_test.time_taptest_table_p2016', 'col3', 'partman', 'daily', p_premake := 2);
    SELECT partman.create_sub_parent('partman_test.time_taptest_table_p2017', 'col3', 'partman', 'daily', p_premake := 2);
    SELECT partman.create_sub_parent('partman_test.time_taptest_table_p2018', 'col3', 'partman', 'daily', p_premake := 2);
    SELECT partman.create_sub_parent('partman_test.time_taptest_table_p2019', 'col3', 'partman', 'daily', p_premake := 2);

    keith@keith=# SELECT tablename FROM pg_tables WHERE schemaname = 'partman_test' ORDER BY tablename;
                       tablename                   
    -----------------------------------------------
     time_taptest_table
     time_taptest_table_p2015
     time_taptest_table_p2015_p2015_01
     time_taptest_table_p2015_p2015_01_p2015_01_01
     time_taptest_table_p2016
     time_taptest_table_p2016_p2016_01
     time_taptest_table_p2016_p2016_01_p2016_01_01
     time_taptest_table_p2017
     time_taptest_table_p2017_p2017_01
     time_taptest_table_p2017_p2017_01_p2017_01_01
     time_taptest_table_p2017_p2017_02
     time_taptest_table_p2017_p2017_02_p2017_02_01
     time_taptest_table_p2017_p2017_03
     time_taptest_table_p2017_p2017_03_p2017_03_25
     time_taptest_table_p2017_p2017_03_p2017_03_26
     time_taptest_table_p2017_p2017_03_p2017_03_27
     time_taptest_table_p2017_p2017_03_p2017_03_28
     time_taptest_table_p2017_p2017_03_p2017_03_29
     time_taptest_table_p2017_p2017_04
     time_taptest_table_p2017_p2017_04_p2017_04_01
     time_taptest_table_p2017_p2017_05
     time_taptest_table_p2017_p2017_05_p2017_05_01
     time_taptest_table_p2018
     time_taptest_table_p2018_p2018_01
     time_taptest_table_p2018_p2018_01_p2018_01_01
     time_taptest_table_p2019
     time_taptest_table_p2019_p2019_01
     time_taptest_table_p2019_p2019_01_p2019_01_01
    (28 rows)

Снова предполагая, что сегодняшняя дата - 2017-03-27, он создал подсекции для покрытия 2 дней в будущем. Все остальные родительские таблицы за пределами текущего временного периода имеют наименьший возможный день создания для них.

F.41.10.6. Подразделение ID->ID->ID: 10,000 -> 1,000 -> 100

В этом наборе секций уже есть существующие данные. Мы разделим их с помощью python-скрипта, который находится в каталоге bin репозитория. Возможно использовать функцию partition_data_id() в postgres также, но это приведет к разделению всех данных в одной транзакции, что, для активной таблицы, может вызвать серьезные проблемы с конкуренцией и вводом/выводом. Python-скрипт позволяет выполнять коммиты пакетами и избегать этого конфликта, и вы можете добавить паузу между пакетами, чтобы ограничить активность ввода/вывода. Флаг p_jobmon устанавливается в функциях создания только для того, чтобы минимизировать спам в логах jobmon для этих тестовых примеров.

    keith@keith=# \d partman_test.id_taptest_table
                      Table "partman_test.id_taptest_table"
     Column |           Type           | Collation | Nullable |    Default    
    --------+--------------------------+-----------+----------+---------------
     col1   | integer                  |           | not null | 
     col2   | text                     |           | not null | 'stuff'::text
     col3   | timestamp with time zone |           |          | now()
    Indexes:
        "id_taptest_table_pkey" PRIMARY KEY, btree (col1)

    
    keith@keith=# SELECT count(*) FROM partman_test.id_taptest_table ;
     count  
    --------
     100000
    (1 row)

    keith@keith=# SELECT min(col1), max(col1) FROM partman_test.id_taptest_table ;
     min |  max   
    -----+--------
       1 | 100000
    (1 row)

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

    keith@keith=# SELECT partman.create_parent('partman_test.id_taptest_table', 'col1', 'partman', '10000', p_jobmon := false, p_premake := 2); 
    ---------------
     t
    (1 row)

    keith@keith=# SELECT tablename FROM pg_tables WHERE schemaname = 'partman_test' ORDER BY tablename;
            tablename         
    --------------------------
     id_taptest_table
     id_taptest_table_p100000
     id_taptest_table_p110000
     id_taptest_table_p120000
     id_taptest_table_p80000
     id_taptest_table_p90000
    (6 rows)

Однако, данные все еще находятся в родительской таблице на данный момент. Чтобы разделить их, используйте python-скрипт, как упоминалось выше. Параметры ниже заставят его фиксировать каждые 100 строк. Если параметр интервала не был задан, он бы фиксировал их на настроенном интервале в 10 000. Разрешение меньшего интервала уменьшает возможные конфликты и позволяет данным быть более доступными в вновь созданных секциях:

    $ python partition_data.py -c host=localhost -p partman_test.id_taptest_table -t id -i 100
    Attempting to turn off autovacuum for partition set...
    ... Success!
    Rows moved: 100
    Rows moved: 100
    ...
    Rows moved: 99
    ...
    Rows moved: 100
    Rows moved: 1
    Total rows moved: 100000
    Running vacuum analyze on parent table...
    Attempting to reset autovacuum for old parent table and all child tables...
        ... Success!

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

    keith@keith=# SELECT tablename FROM pg_tables WHERE schemaname = 'partman_test' ORDER BY tablename;
            tablename         
    --------------------------
     id_taptest_table
     id_taptest_table_p0
     id_taptest_table_p10000
     id_taptest_table_p100000
     id_taptest_table_p110000
     id_taptest_table_p120000
     id_taptest_table_p20000
     id_taptest_table_p30000
     id_taptest_table_p40000
     id_taptest_table_p50000
     id_taptest_table_p60000
     id_taptest_table_p70000
     id_taptest_table_p80000
     id_taptest_table_p90000
    (14 rows)

Теперь создайте подсекции для 1000. Как было отмечено выше для времени, мы указываем родительскую таблицу, дочерние элементы которой мы хотим разделить, вместе с свойствами, которые мы хотим присвоить этим дочерним элементам:

    keith@keith=# SELECT partman.create_sub_parent('partman_test.id_taptest_table', 'col1', 'partman', '1000', p_jobmon := false, p_premake := 2);
     create_sub_parent 
    -------------------
     t
    (1 row)

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

    keith@keith=# SELECT tablename FROM pg_tables WHERE schemaname = 'partman_test' ORDER BY tablename;
                tablename             
    ----------------------------------
     id_taptest_table
     id_taptest_table_p0
     id_taptest_table_p0_p0
     id_taptest_table_p10000
     id_taptest_table_p100000
     id_taptest_table_p100000_p100000
     id_taptest_table_p100000_p101000
     id_taptest_table_p100000_p102000
     id_taptest_table_p10000_p10000
     id_taptest_table_p110000
     id_taptest_table_p110000_p110000
     id_taptest_table_p120000
     id_taptest_table_p120000_p120000
     id_taptest_table_p20000
     id_taptest_table_p20000_p20000
     id_taptest_table_p30000
     id_taptest_table_p30000_p30000
     id_taptest_table_p40000
     id_taptest_table_p40000_p40000
     id_taptest_table_p50000
     id_taptest_table_p50000_p50000
     id_taptest_table_p60000
     id_taptest_table_p60000_p60000
     id_taptest_table_p70000
     id_taptest_table_p70000_p70000
     id_taptest_table_p80000
     id_taptest_table_p80000_p80000
     id_taptest_table_p90000
     id_taptest_table_p90000_p98000
     id_taptest_table_p90000_p99000
    (30 rows)

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

    keith@keith=# SELECT * FROM partman.check_parent() ORDER BY 1;
                 parent_table              | count 
    ---------------------------------------+-------
     partman_test.id_taptest_table_p0      |  9999
     partman_test.id_taptest_table_p10000  | 10000
     partman_test.id_taptest_table_p100000 |     1
     partman_test.id_taptest_table_p20000  | 10000
     partman_test.id_taptest_table_p30000  | 10000
     partman_test.id_taptest_table_p40000  | 10000
     partman_test.id_taptest_table_p50000  | 10000
     partman_test.id_taptest_table_p60000  | 10000
     partman_test.id_taptest_table_p70000  | 10000
     partman_test.id_taptest_table_p80000  | 10000
     partman_test.id_taptest_table_p90000  | 10000
    (11 rows)

Итак, давайте исправим это:

    python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p0 -t id -i 100
    python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p10000 -t id -i 100
    python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p20000 -t id -i 100
    python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p30000 -t id -i 100
    python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p40000 -t id -i 100
    python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p50000 -t id -i 100
    python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p60000 -t id -i 100
    python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p70000 -t id -i 100
    python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p80000 -t id -i 100
    python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p90000 -t id -i 100
    python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p100000 -t id -i 100

Теперь функция мониторинга не возвращает ничего (как и должно быть нормой):

    keith@keith=# SELECT * FROM partman.check_parent() ORDER BY 1;
     parent_table | count 
    --------------+-------
    (0 rows)

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

    keith@keith=# SELECT tablename FROM pg_tables WHERE schemaname = 'partman_test' ORDER BY tablename;
                tablename             
    ----------------------------------
     id_taptest_table
     id_taptest_table_p0
     id_taptest_table_p0_p0
     id_taptest_table_p0_p1000
     id_taptest_table_p0_p2000
     id_taptest_table_p0_p3000
     id_taptest_table_p0_p4000
     id_taptest_table_p0_p5000
     id_taptest_table_p0_p6000
     id_taptest_table_p0_p7000
     id_taptest_table_p0_p8000
     id_taptest_table_p0_p9000
     id_taptest_table_p10000
     id_taptest_table_p100000
     id_taptest_table_p100000_p100000
     id_taptest_table_p100000_p101000
     id_taptest_table_p100000_p102000
     id_taptest_table_p10000_p10000
     id_taptest_table_p10000_p11000
     id_taptest_table_p10000_p12000
     id_taptest_table_p10000_p13000
     id_taptest_table_p10000_p14000
     id_taptest_table_p10000_p15000
     id_taptest_table_p10000_p16000
     id_taptest_table_p10000_p17000
     id_taptest_table_p10000_p18000
     id_taptest_table_p10000_p19000
     id_taptest_table_p110000
     id_taptest_table_p110000_p110000
     id_taptest_table_p120000
     id_taptest_table_p120000_p120000
     id_taptest_table_p20000
     id_taptest_table_p20000_p20000
     id_taptest_table_p20000_p21000
     id_taptest_table_p20000_p22000
     id_taptest_table_p20000_p23000
     id_taptest_table_p20000_p24000
     id_taptest_table_p20000_p25000
     id_taptest_table_p20000_p26000
     id_taptest_table_p20000_p27000
     id_taptest_table_p20000_p28000
     id_taptest_table_p20000_p29000
     id_taptest_table_p30000
     id_taptest_table_p30000_p30000
     id_taptest_table_p30000_p31000
     id_taptest_table_p30000_p32000
     id_taptest_table_p30000_p33000
     id_taptest_table_p30000_p34000
     id_taptest_table_p30000_p35000
     id_taptest_table_p30000_p36000
     id_taptest_table_p30000_p37000
     id_taptest_table_p30000_p38000
     id_taptest_table_p30000_p39000
     id_taptest_table_p40000
     id_taptest_table_p40000_p40000
     id_taptest_table_p40000_p41000
     id_taptest_table_p40000_p42000
     id_taptest_table_p40000_p43000
     id_taptest_table_p40000_p44000
     id_taptest_table_p40000_p45000
     id_taptest_table_p40000_p46000
     id_taptest_table_p40000_p47000
     id_taptest_table_p40000_p48000
     id_taptest_table_p40000_p49000
     id_taptest_table_p50000
     id_taptest_table_p50000_p50000
     id_taptest_table_p50000_p51000
     id_taptest_table_p50000_p52000
     id_taptest_table_p50000_p53000
     id_taptest_table_p50000_p54000
     id_taptest_table_p50000_p55000
     id_taptest_table_p50000_p56000
     id_taptest_table_p50000_p57000
     id_taptest_table_p50000_p58000
     id_taptest_table_p50000_p59000
     id_taptest_table_p60000
     id_taptest_table_p60000_p60000
     id_taptest_table_p60000_p61000
     id_taptest_table_p60000_p62000
     id_taptest_table_p60000_p63000
     id_taptest_table_p60000_p64000
     id_taptest_table_p60000_p65000
     id_taptest_table_p60000_p66000
     id_taptest_table_p60000_p67000
     id_taptest_table_p60000_p68000
     id_taptest_table_p60000_p69000
     id_taptest_table_p70000
     id_taptest_table_p70000_p70000
     id_taptest_table_p70000_p71000
     id_taptest_table_p70000_p72000
     id_taptest_table_p70000_p73000
     id_taptest_table_p70000_p74000
     id_taptest_table_p70000_p75000
     id_taptest_table_p70000_p76000
     id_taptest_table_p70000_p77000
     id_taptest_table_p70000_p78000
     id_taptest_table_p70000_p79000
     id_taptest_table_p80000
     id_taptest_table_p80000_p80000
     id_taptest_table_p80000_p81000
     id_taptest_table_p80000_p82000
     id_taptest_table_p80000_p83000
     id_taptest_table_p80000_p84000
     id_taptest_table_p80000_p85000
     id_taptest_table_p80000_p86000
     id_taptest_table_p80000_p87000
     id_taptest_table_p80000_p88000
     id_taptest_table_p80000_p89000
     id_taptest_table_p90000
     id_taptest_table_p90000_p90000
     id_taptest_table_p90000_p91000
     id_taptest_table_p90000_p92000
     id_taptest_table_p90000_p93000
     id_taptest_table_p90000_p94000
     id_taptest_table_p90000_p95000
     id_taptest_table_p90000_p96000
     id_taptest_table_p90000_p97000
     id_taptest_table_p90000_p98000
     id_taptest_table_p90000_p99000
    (119 rows)

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

    SELECT partman.create_sub_parent('partman_test.id_taptest_table_p0', 'col1', 'partman', '100', p_jobmon := false, p_premake := 2);
    SELECT partman.create_sub_parent('partman_test.id_taptest_table_p10000', 'col1', 'partman', '100', p_jobmon := false, p_premake := 2);
    SELECT partman.create_sub_parent('partman_test.id_taptest_table_p20000', 'col1', 'partman', '100', p_jobmon := false, p_premake := 2);
    SELECT partman.create_sub_parent('partman_test.id_taptest_table_p30000', 'col1', 'partman', '100', p_jobmon := false, p_premake := 2);
    SELECT partman.create_sub_parent('partman_test.id_taptest_table_p40000', 'col1', 'partman', '100', p_jobmon := false, p_premake := 2);
    SELECT partman.create_sub_parent('partman_test.id_taptest_table_p50000', 'col1', 'partman', '100', p_jobmon := false, p_premake := 2);
    SELECT partman.create_sub_parent('partman_test.id_taptest_table_p60000', 'col1', 'partman', '100', p_jobmon := false, p_premake := 2);
    SELECT partman.create_sub_parent('partman_test.id_taptest_table_p70000', 'col1', 'partman', '100', p_jobmon := false, p_premake := 2);
    SELECT partman.create_sub_parent('partman_test.id_taptest_table_p80000', 'col1', 'partman', '100', p_jobmon := false, p_premake := 2);
    SELECT partman.create_sub_parent('partman_test.id_taptest_table_p90000', 'col1', 'partman', '100', p_jobmon := false, p_premake := 2);
    SELECT partman.create_sub_parent('partman_test.id_taptest_table_p100000', 'col1', 'partman', '100', p_jobmon := false, p_premake := 2);

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

    keith@keith=# SELECT tablename FROM pg_tables WHERE schemaname = 'partman_test' order by tablename;
                    tablename                 
    ------------------------------------------
     id_taptest_table
     id_taptest_table_p0
     id_taptest_table_p0_p0
     id_taptest_table_p0_p0_p0
     id_taptest_table_p0_p1000
     id_taptest_table_p0_p1000_p1000
     id_taptest_table_p0_p2000
     id_taptest_table_p0_p2000_p2000
     ...
     id_taptest_table_p10000
     id_taptest_table_p100000
     id_taptest_table_p100000_p100000
     id_taptest_table_p100000_p100000_p100000
     id_taptest_table_p100000_p100000_p100100
     id_taptest_table_p100000_p100000_p100200
     id_taptest_table_p100000_p101000
     id_taptest_table_p100000_p101000_p101000
     id_taptest_table_p100000_p102000
     id_taptest_table_p100000_p102000_p102000
     id_taptest_table_p10000_p10000
     id_taptest_table_p10000_p10000_p10000
     id_taptest_table_p10000_p11000
     id_taptest_table_p10000_p11000_p11000
     ...
     id_taptest_table_p90000_p98000
     id_taptest_table_p90000_p98000_p98000
     id_taptest_table_p90000_p99000
     id_taptest_table_p90000_p99000_p99800
     id_taptest_table_p90000_p99000_p99900
    (225 rows)

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

    SELECT 'python partition_data.py -c host=localhost -p '||parent_table||' -t id -i 100' FROM partman.part_config ORDER BY parent_table;

                                                    ?column?                                                 
    ---------------------------------------------------------------------------------------------------------
     python partition_data.py -c host=localhost -p partman_test.id_taptest_table -t id -i 100
     python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p0 -t id -i 100
     python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p0_p0 -t id -i 100
     python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p0_p1000 -t id -i 100
    ...

Это сгенерирует команды для разделения данных, найденных в любой родительской таблице, управляемой pg_partman. Да, некоторые уже пусты, но это не имеет значения, поскольку они просто ничего не делают и это упрощает запрос на генерацию этих команд. Рекомендуется поместить вывод из этого в исполняемый shell-файл, а не просто вставлять все это напрямую в оболочку. Теперь, если вы получите список всех таблиц, вы увидите, что их теперь довольно много (возвращаемое количество строк - это количество таблиц).

    keith@keith=# SELECT tablename FROM pg_tables WHERE schemaname = 'partman_test' order by tablename;
                    tablename                
    -----------------------------------------
     id_taptest_table
     id_taptest_table_p0
     id_taptest_table_p0_p0
     id_taptest_table_p0_p0_p0
     id_taptest_table_p0_p0_p100
     id_taptest_table_p0_p0_p200
     id_taptest_table_p0_p0_p300
     id_taptest_table_p0_p0_p400
     id_taptest_table_p0_p0_p500
     id_taptest_table_p0_p0_p600
     id_taptest_table_p0_p0_p700
     id_taptest_table_p0_p0_p800
     id_taptest_table_p0_p0_p900
     id_taptest_table_p0_p1000
     id_taptest_table_p0_p1000_p1000
     id_taptest_table_p0_p1000_p1100
     id_taptest_table_p0_p1000_p1200
     id_taptest_table_p0_p1000_p1300
     id_taptest_table_p0_p1000_p1400
     id_taptest_table_p0_p1000_p1500
     id_taptest_table_p0_p1000_p1600
     id_taptest_table_p0_p1000_p1700
     id_taptest_table_p0_p1000_p1800
     id_taptest_table_p0_p1000_p1900
     id_taptest_table_p0_p2000
     id_taptest_table_p0_p2000_p2000
     id_taptest_table_p0_p2000_p2100
     ...
     id_taptest_table_p90000_p98000_p98800
     id_taptest_table_p90000_p98000_p98900
     id_taptest_table_p90000_p99000
     id_taptest_table_p90000_p99000_p99000
     id_taptest_table_p90000_p99000_p99100
     id_taptest_table_p90000_p99000_p99200
     id_taptest_table_p90000_p99000_p99300
     id_taptest_table_p90000_p99000_p99400
     id_taptest_table_p90000_p99000_p99500
     id_taptest_table_p90000_p99000_p99600
     id_taptest_table_p90000_p99000_p99700
     id_taptest_table_p90000_p99000_p99800
     id_taptest_table_p90000_p99000_p99900
    (1124 rows)

Теперь все 100 000 строк правильно разделены, как и должно быть, и все новые строки должны идти туда, куда они предполагаются.

F.41.10.7. Установите run_maintenance() для частого запуска

Используя указанные выше секции по времени, run_maintenance() должен вызываться как минимум дважды в день, чтобы гарантировать соответствие требованиям наименьшего временного интервала секции (ежедневно).

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

Если вы используете фоновый рабочий процесс (BGW), установите значение pg_partman_bgw.interval в postgresql.conf. В этом примере оно устанавливлено на каждые 12 часов (43200 секунд). См. файл doc/pg_partman.md для получения дополнительной информации о настройках BGW.

pg_partman_bgw.interval = 43200
pg_partman_bgw.role = 'keith'
pg_partman_bgw.dbname = 'keith'

Если вы не используете BGW, необходимо использовать сторонний инструмент планирования, такой как cron, для планирования вызовов run_maintenance()

03 01,13 * * * psql -c "SELECT run_maintenance()"

F.41.10.8. Использование Политики Хранения

Чтобы удалить секции в первом примере выше, которые старше 30 дней, установите следующее:

    UPDATE part_config SET retention = '30 days', retention_keep_table = false WHERE parent_table = 'partman_test.time_taptest_table';

Чтобы удалить секции во втором примере выше, которые содержат значение на 100 меньше текущего максимума (max(col1) - 100), установите следующее:

    UPDATE part_config SET retention = '100', retention_keep_table = false WHERE parent_table = 'partman_test.id_taptest_table';

Например, как только текущее значение id для col1 достигнет 1000, все секции со значениями меньше 900 будут удалены.

Если вы хотите сохранить старые данные в офлайн-файлах дампа, также установите столбец retention_schema (настройки конфигурации keep* будут переопределены, если это установлено):

    UPDATE part_config SET retention = '30 days', retention_schema = 'archive' WHERE parent_table = 'partman_test.time_taptest_table';

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

    $ python dump_partition.py -c "host=localhost username=postgres" -d mydatabase -n archive -o /path/to/dump/location 

Для реализации любой политики сохранения просто убедитесь, что функция run_maintenance() вызывается достаточно часто для ваших потребностей. Эта функция обрабатывает как создание секций, так и политики сохранения.

F.41.10.9. Отмена партицирования: Простое партицирование по времени

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

    $ python undo_partition.py -p partman_test.time_taptest_table -c host=localhost -t time -i "1 hour"
    Attempting to turn off autovacuum for partition set...
        ... Success!
    Total rows moved: 0
    Running vacuum analyze on parent table...
    Attempting to reset autovacuum for old parent table...
        ... Success!

F.41.10.10. Отмена партицирования: Простой последовательный ID

Это просто отменяет фиксацию секций id с заданным выше стандартным интервалом разделения, равным 10.

    $ python undo_partition.py -p partman_test.id_taptest_table -c host=localhost -t id
    Attempting to turn off autovacuum for partition set...
        ... Success!
    Total rows moved: 0
    Running vacuum analyze on parent table...
    Attempting to reset autovacuum for old parent table...
        ... Success!

F.41.10.11. Отмена партицирования: ID подсекции->ID->ID

Отмена подразделения требует немного больше работы (или возможно много, если это большой набор). Необходимо начать с нижнего уровня. Так же, как я сделал выше для генерации операторов для партицирования данных, я могу сделать то же самое для скрипта undo_partition.py. Имейте в виду, что это дает оператор отмены для ВСЕХ родительских элементов сразу. Вам придется пройти через и проанализировать вызовы верхнего уровня, а также средний уровень партицирования, но это по крайней мере сэкономит вам много потенциального набора (и опечаток). Нижние секции должны быть выполнены сначала и верхние последними. Кроме того, в этом случае у меня нет намерения сохранять старые, пустые таблицы, поэтому дана опция –droptable. pg_partman старается быть максимально безопасным, поэтому он только отменяет наследование таблиц по умолчанию при отмене партицирования. Если вы хотите что-то удалить, необходимо быть уверенным в действии и подтвердить это.

    SELECT 'python undo_partition.py -c host=localhost -p '||parent_table||' -t id -i 100 --droptable' FROM partman.part_config ORDER BY parent_table;

Сначала выполните наименьшие подсекции:

    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p0_p0 -t id -i 100 --droptable
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p0_p1000 -t id -i 100 --droptable
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p0_p2000 -t id -i 100 --droptable
    ...
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p100000_p100000 -t id -i 100 --droptable
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p100000_p101000 -t id -i 100 --droptable
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p100000_p102000 -t id -i 100 --droptable

Далее делайте то, что были средние подсекции:

    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p0 -t id -i 100 --droptable
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p10000 -t id -i 100 --droptable
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p100000 -t id -i 100 --droptable
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p110000 -t id -i 100 --droptable
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p120000 -t id -i 100 --droptable
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p20000 -t id -i 100 --droptable
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p30000 -t id -i 100 --droptable
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p40000 -t id -i 100 --droptable
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p50000 -t id -i 100 --droptable
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p60000 -t id -i 100 --droptable
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p70000 -t id -i 100 --droptable
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p80000 -t id -i 100 --droptable
    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p90000 -t id -i 100 --droptable

И, наконец, выполните последнюю, верхнеуровневую разбивку:

    python undo_partition.py -c host=localhost -p partman_test.id_taptest_table -t id -i 100 --droptable

Теперь осталась только одна таблица со всеми данными

    keith@keith=# SELECT tablename FROM pg_tables WHERE schemaname = 'partman_test' ORDER BY tablename;
        tablename    
    -----------------
     id_taptest_table

    keith@keith=# SELECT count(*) FROM partman_test.id_taptest_table ;
     count  
    --------
     100000
    (1 row)

F.41.10.12. Отмена партицирования: Подраздел Время->Время->Время

Это делается точно таким же образом, как для ID->ID->ID, за исключением того, что скрипт undo_partition.py будет использовать настройку времени -t, а -i будет использовать значение временного интервала.

Надеемся, что эти рабочие примеры помогут вам начать. Еще раз, пожалуйста, обратитесь к документу pg_partman.md за полной информацией обо всех функциях и возможностях этого расширения. Если у вас возникнут какие-либо проблемы или вопросы, не стесняйтесь открыть вопрос на странице github: https://github.com/pgpartman/pg_partman

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

Это руководство покажет вам некоторые примеры того, как настроить простую, одноуровневую разбивку. Оно также покажет вам несколько способов партицирования данных из таблицы, в которой уже есть данные (см. партицирование существующей таблицы) и отмены партицирования существующего набора секций (см. Отмена нативного партицирования). Для получения более подробной информации о том, что делает каждая функция и дополнительных возможностях этого расширения, пожалуйста, см. pg_partman.md файл документации.

Примеры в этом документе предполагают, что вы используете как минимум 4.4.1 версию pg_partman с PostgreSQL 11 или выше.

Обратите внимание, что все примеры здесь предназначены для нативного партицирования. Если вам нужно использовать не нативное, основанное на триггерах партицирование, пожалуйста, смотрите Как настроить партицирование на основе триггеров файл.

F.41.11.1. Простое разделение по времени: 1 раздел в день

Для нативного партицирования вам нужно начать с родительской таблицы, которая уже была настроена для партицирования нужного типа. В настоящее время pg_partman поддерживает только тип партицирования RANGE (как для времени, так и для id). Вы не можете превратить неразделенную таблицу в родительскую таблицу разделенного набора, что может создать проблемы при миграции. В этом документе будет показано несколько методов управления этим позже. Пока что, мы начнем с совершенно новой таблицы для этого примера. Любые неуникальные индексы также могут быть добавлены в родительскую таблицу в PG11+ и они автоматически будут созданы на всех дочерних таблицах.

CREATE SCHEMA IF NOT EXISTS partman_test;

CREATE TABLE partman_test.time_taptest_table 
    (col1 int, 
    col2 text default 'stuff', 
    col3 timestamptz NOT NULL DEFAULT now()) 
PARTITION BY RANGE (col3);

CREATE INDEX ON partman_test.time_taptest_table (col3);
\d+ partman_test.time_taptest_table 
                               Partitioned table "partman_test.time_taptest_table"
 Column |           Type           | Collation | Nullable |    Default    | Storage  | Stats target | Description 
--------+--------------------------+-----------+----------+---------------+----------+--------------+-------------
 col1   | integer                  |           |          |               | plain    |              | 
 col2   | text                     |           |          | 'stuff'::text | extended |              | 
 col3   | timestamp with time zone |           | not null | now()         | plain    |              | 
Partition key: RANGE (col3)
Indexes:
    "time_taptest_table_col3_idx" btree (col3)
Number of partitions: 0

Уникальные индексы (включая первичные ключи) не могут быть созданы на родительской секции, реализованном нативно, если они не включают ключ секции. Для временного партицирования это обычно не работает, поскольку это ограничивает только одно значение временной метки в каждой дочерней таблице. pg_partman помогает управлять этим, используя шаблонную таблицу для управления свойствами, которые в настоящее время не поддерживаются нативным партицированием. Обратите внимание, что это не решает проблему ограничения, которое не применяется ко всему набору секций.

Для этого примера мы сначала вручную создадим шаблонную таблицу, чтобы при запуске create_parent() первоначально созданные дочерние таблицы имели первичный ключ. Если вы не предоставляете шаблонную таблицу для pg_partman, он создаст ее для вас в схеме, в которую вы установили расширение. Однако свойства, которые вы добавляете к этому шаблону, применяются только к вновь созданным дочерним таблицам после этого момента. Вам придется ретроактивно применять эти свойства вручную к любым дочерним таблицам, которые уже существовали.

CREATE TABLE partman_test.time_taptest_table_template (LIKE partman_test.time_taptest_table);
ALTER TABLE partman_test.time_taptest_table_template ADD PRIMARY KEY (col1);
 \d partman_test.time_taptest_table_template
          Table "partman_test.time_taptest_table_template"
 Column |           Type           | Collation | Nullable | Default 
--------+--------------------------+-----------+----------+---------
 col1   | integer                  |           | not null | 
 col2   | text                     |           |          | 
 col3   | timestamp with time zone |           | not null | 
Indexes:
    "time_taptest_table_template_pkey" PRIMARY KEY, btree (col1)
SELECT partman.create_parent('partman_test.time_taptest_table', 'col3', 'native', 'daily', p_template_table := 'partman_test.time_taptest_table_template');
 create_parent 
---------------
 t
(1 row)
\d+ partman_test.time_taptest_table
                               Partitioned table "partman_test.time_taptest_table"
 Column |           Type           | Collation | Nullable |    Default    | Storage  | Stats target | Description 
--------+--------------------------+-----------+----------+---------------+----------+--------------+-------------
 col1   | integer                  |           |          |               | plain    |              | 
 col2   | text                     |           |          | 'stuff'::text | extended |              | 
 col3   | timestamp with time zone |           | not null | now()         | plain    |              | 
Partition key: RANGE (col3)
Indexes:
    "time_taptest_table_col3_idx" btree (col3)
Partitions: partman_test.time_taptest_table_p2020_10_26 FOR VALUES FROM ('2020-10-26 00:00:00-04') TO ('2020-10-27 00:00:00-04'),
            partman_test.time_taptest_table_p2020_10_27 FOR VALUES FROM ('2020-10-27 00:00:00-04') TO ('2020-10-28 00:00:00-04'),
            partman_test.time_taptest_table_p2020_10_28 FOR VALUES FROM ('2020-10-28 00:00:00-04') TO ('2020-10-29 00:00:00-04'),
            partman_test.time_taptest_table_p2020_10_29 FOR VALUES FROM ('2020-10-29 00:00:00-04') TO ('2020-10-30 00:00:00-04'),
            partman_test.time_taptest_table_p2020_10_30 FOR VALUES FROM ('2020-10-30 00:00:00-04') TO ('2020-10-31 00:00:00-04'),
            partman_test.time_taptest_table_p2020_10_31 FOR VALUES FROM ('2020-10-31 00:00:00-04') TO ('2020-11-01 00:00:00-04'),
            partman_test.time_taptest_table_p2020_11_01 FOR VALUES FROM ('2020-11-01 00:00:00-04') TO ('2020-11-02 00:00:00-05'),
            partman_test.time_taptest_table_p2020_11_02 FOR VALUES FROM ('2020-11-02 00:00:00-05') TO ('2020-11-03 00:00:00-05'),
            partman_test.time_taptest_table_p2020_11_03 FOR VALUES FROM ('2020-11-03 00:00:00-05') TO ('2020-11-04 00:00:00-05'),
            partman_test.time_taptest_table_default DEFAULT
\d+ partman_test.time_taptest_table_p2020_10_26
                               Table "partman_test.time_taptest_table_p2020_10_26"
 Column |           Type           | Collation | Nullable |    Default    | Storage  | Stats target | Description 
--------+--------------------------+-----------+----------+---------------+----------+--------------+-------------
 col1   | integer                  |           | not null |               | plain    |              | 
 col2   | text                     |           |          | 'stuff'::text | extended |              | 
 col3   | timestamp with time zone |           | not null | now()         | plain    |              | 
Partition of: partman_test.time_taptest_table FOR VALUES FROM ('2020-10-26 00:00:00-04') TO ('2020-10-27 00:00:00-04')
Partition constraint: ((col3 IS NOT NULL) AND (col3 >= '2020-10-26 00:00:00-04'::timestamp with time zone) AND (col3 < '2020-10-27 00:00:00-04'::timestamp with time zone))
Indexes:
    "time_taptest_table_p2020_10_26_pkey" PRIMARY KEY, btree (col1)
    "time_taptest_table_p2020_10_26_col3_idx" btree (col3)
Access method: heap

F.41.11.2. Простой серийный ID: 1 раздел на 10 значений ID

Для этого варианта использования таблица-шаблон не создается вручную перед вызовом create_parent(). Таким образом, показывается, что если позже добавляется первичный/уникальный ключ, он не применяется к текущим существующим дочерним таблицам. Это придется сделать вручную.

CREATE TABLE partman_test.id_taptest_table (
    col1 bigint 
    , col2 text not null
    , col3 timestamptz DEFAULT now()
    , col4 text) PARTITION BY RANGE (col1);

CREATE INDEX ON partman_test.id_taptest_table (col1);
\d+ partman_test.id_taptest_table 
                             Partitioned table "partman_test.id_taptest_table"
 Column |           Type           | Collation | Nullable | Default | Storage  | Stats target | Description 
--------+--------------------------+-----------+----------+---------+----------+--------------+-------------
 col1   | bigint                   |           |          |         | plain    |              | 
 col2   | text                     |           | not null |         | extended |              | 
 col3   | timestamp with time zone |           |          | now()   | plain    |              | 
 col4   | text                     |           |          |         | extended |              | 
Partition key: RANGE (col1)
Indexes:
    "id_taptest_table_col1_idx" btree (col1)
Number of partitions: 0

SELECT partman.create_parent('partman_test.id_taptest_table', 'col1', 'native', '10');
 create_parent 
---------------
 t
(1 row)
\d+ partman_test.id_taptest_table
                             Partitioned table "partman_test.id_taptest_table"
 Column |           Type           | Collation | Nullable | Default | Storage  | Stats target | Description 
--------+--------------------------+-----------+----------+---------+----------+--------------+-------------
 col1   | bigint                   |           |          |         | plain    |              | 
 col2   | text                     |           | not null |         | extended |              | 
 col3   | timestamp with time zone |           |          | now()   | plain    |              | 
 col4   | text                     |           |          |         | extended |              | 
Partition key: RANGE (col1)
Indexes:
    "id_taptest_table_col1_idx" btree (col1)
Partitions: partman_test.id_taptest_table_p0 FOR VALUES FROM ('0') TO ('10'),
            partman_test.id_taptest_table_p10 FOR VALUES FROM ('10') TO ('20'),
            partman_test.id_taptest_table_p20 FOR VALUES FROM ('20') TO ('30'),
            partman_test.id_taptest_table_p30 FOR VALUES FROM ('30') TO ('40'),
            partman_test.id_taptest_table_p40 FOR VALUES FROM ('40') TO ('50'),
            partman_test.id_taptest_table_default DEFAULT

Вы можете увидеть имя шаблонной таблицы, посмотрев в конфигурацию pg_partman для этой родительской таблицы

select template_table from partman.part_config where parent_table = 'partman_test.id_taptest_table';
                 template_table                 
------------------------------------------------
 partman.template_partman_test_id_taptest_table
ALTER TABLE partman.template_partman_test_id_taptest_table ADD PRIMARY KEY (col2);

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

INSERT INTO partman_test.id_taptest_table (col1, col2) VALUES (generate_series(1,20), generate_series(1,20)::text||'stuff'::text);

CALL partman.run_maintenance_proc();

\d+ partman_test.id_taptest_table
                             Partitioned table "partman_test.id_taptest_table"
 Column |           Type           | Collation | Nullable | Default | Storage  | Stats target | Description 
--------+--------------------------+-----------+----------+---------+----------+--------------+-------------
 col1   | bigint                   |           |          |         | plain    |              | 
 col2   | text                     |           | not null |         | extended |              | 
 col3   | timestamp with time zone |           |          | now()   | plain    |              | 
 col4   | text                     |           |          |         | extended |              | 
Partition key: RANGE (col1)
Indexes:
    "id_taptest_table_col1_idx" btree (col1)
Partitions: partman_test.id_taptest_table_p0 FOR VALUES FROM ('0') TO ('10'),
            partman_test.id_taptest_table_p10 FOR VALUES FROM ('10') TO ('20'),
            partman_test.id_taptest_table_p20 FOR VALUES FROM ('20') TO ('30'),
            partman_test.id_taptest_table_p30 FOR VALUES FROM ('30') TO ('40'),
            partman_test.id_taptest_table_p40 FOR VALUES FROM ('40') TO ('50'),
            partman_test.id_taptest_table_p50 FOR VALUES FROM ('50') TO ('60'),
            partman_test.id_taptest_table_p60 FOR VALUES FROM ('60') TO ('70'),
            partman_test.id_taptest_table_default DEFAULT

… вы увидите, что только новые дочерние таблицы (p50 & p60) имеют этот первичный ключ, а оригинальные таблицы не имеют (p40 и ранее).

\d partman_test.id_taptest_table_p40
             Table "partman_test.id_taptest_table_p40"
 Column |           Type           | Collation | Nullable | Default 
--------+--------------------------+-----------+----------+---------
 col1   | bigint                   |           |          | 
 col2   | text                     |           | not null | 
 col3   | timestamp with time zone |           |          | now()
 col4   | text                     |           |          | 
Partition of: partman_test.id_taptest_table FOR VALUES FROM ('40') TO ('50')
Indexes:
    "id_taptest_table_p40_col1_idx" btree (col1)

\d partman_test.id_taptest_table_p50
             Table "partman_test.id_taptest_table_p50"
 Column |           Type           | Collation | Nullable | Default 
--------+--------------------------+-----------+----------+---------
 col1   | bigint                   |           |          | 
 col2   | text                     |           | not null | 
 col3   | timestamp with time zone |           |          | now()
 col4   | text                     |           |          | 
Partition of: partman_test.id_taptest_table FOR VALUES FROM ('50') TO ('60')
Indexes:
    "id_taptest_table_p50_pkey" PRIMARY KEY, btree (col2)
    "id_taptest_table_p50_col1_idx" btree (col1)

\d partman_test.id_taptest_table_p60
             Table "partman_test.id_taptest_table_p60"
 Column |           Type           | Collation | Nullable | Default 
--------+--------------------------+-----------+----------+---------
 col1   | bigint                   |           |          | 
 col2   | text                     |           | not null | 
 col3   | timestamp with time zone |           |          | now()
 col4   | text                     |           |          | 
Partition of: partman_test.id_taptest_table FOR VALUES FROM ('60') TO ('70')
Indexes:
    "id_taptest_table_p60_pkey" PRIMARY KEY, btree (col2)
    "id_taptest_table_p60_col1_idx" btree (col1)

Добавьте их вручную:

ALTER TABLE partman_test.id_taptest_table_p0 ADD PRIMARY KEY (col2);
ALTER TABLE partman_test.id_taptest_table_p10 ADD PRIMARY KEY (col2);
ALTER TABLE partman_test.id_taptest_table_p20 ADD PRIMARY KEY (col2);
ALTER TABLE partman_test.id_taptest_table_p30 ADD PRIMARY KEY (col2);
ALTER TABLE partman_test.id_taptest_table_p40 ADD PRIMARY KEY (col2);

F.41.11.3. партицирование существующей таблицы

партицирование существующей таблицы с использованием встроенного партицирования не так прямолинейно, как методы, которые могли быть использованы со старыми методами на основе триггеров. Как указано выше, вы не можете превратить уже существующую таблицу в родительскую таблицу набора встроенных секций. Родитель таблицы с встроенным партицированием должен быть объявлен разделенным во время его создания. Однако существуют методы для партицирования существующей таблицы на встроенные секции. Два из них представлены ниже.

F.41.11.3.1. Оффлайн Партиционирование

Этот метод получил метку офлайн, потому что во время некоторых этапов этого процесса данные не доступны как для новой, так и для старой таблицы из одного объекта. Данные перемещаются из исходной таблицы в совершенно новую таблицу. Преимущество этого метода заключается в том, что вы можете перемещать свои данные гораздо меньшими партиями, чем даже размер целевой секции, что может быть огромным преимуществом в эффективности для очень больших наборов секций (вы можете фиксировать изменения партиями в несколько тысяч против нескольких миллионов). Также меньше шагов переименования объектов, как мы увидим в методе онлайн-партицирования далее.

ВАЖНОЕ ЗАМЕЧАНИЕ ОТНОСИТЕЛЬНО ВНЕШНИХ КЛЮЧЕЙ

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

Вот исходная таблица с некоторыми сгенерированными данными:

CREATE TABLE public.original_table (
    col1 bigint not null
    , col2 text not null
    , col3 timestamptz DEFAULT now()
    , col4 text); 

CREATE INDEX ON public.original_table (col1);

INSERT INTO public.original_table (col1, col2, col3, col4) VALUES (generate_series(1,100000), 'stuff'||generate_series(1,100000), now(), 'stuff');

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

ALTER TABLE public.original_table RENAME to old_nonpartitioned_table;

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

Обратите внимание, что первичные ключи/уникальные индексы не могут быть применены к родительскому разделу, если ключ секции не является его частью. В этом случае это бы сработало, однако, вероятно, это не намерение, поскольку это означало бы, что допускается только одна строка на значение, что в свою очередь означало бы, что в каждой дочерней таблице может существовать только 10 000 строк. Тогда партицирование определенно не требуется. Следующий пример онлайн-партицирования покажет, как обрабатывать ситуацию, когда вам нужен первичный ключ для столбца, который не является частью ключа секции.

CREATE TABLE public.original_table (
    col1 bigint not null
    , col2 text not null
    , col3 timestamptz DEFAULT now()
    , col4 text) PARTITION BY RANGE (col1);

CREATE INDEX ON public.original_table (col1);

SELECT partman.create_parent('public.original_table', 'col1', 'native', '10000');
\d+ original_table;
                                 Partitioned table "public.original_table"
 Column |           Type           | Collation | Nullable | Default | Storage  | Stats target | Description 
--------+--------------------------+-----------+----------+---------+----------+--------------+-------------
 col1   | bigint                   |           | not null |         | plain    |              | 
 col2   | text                     |           | not null |         | extended |              | 
 col3   | timestamp with time zone |           |          | now()   | plain    |              | 
 col4   | text                     |           |          |         | extended |              | 
Partition key: RANGE (col1)
Indexes:
    "original_table_col1_idx1" btree (col1)
Partitions: original_table_p0 FOR VALUES FROM ('0') TO ('10000'),
            original_table_p10000 FOR VALUES FROM ('10000') TO ('20000'),
            original_table_p20000 FOR VALUES FROM ('20000') TO ('30000'),
            original_table_p30000 FOR VALUES FROM ('30000') TO ('40000'),
            original_table_p40000 FOR VALUES FROM ('40000') TO ('50000'),
            original_table_default DEFAULT

Если вы случайно использовали столбцы IDENTITY или создали новую последовательность для новой разделенной таблицы, вы захотите получить значение этих старых последовательностей и сбросить новые последовательности, чтобы начать с этих старых значений. Некоторые советы по этому поводу приведены в разделе Онлайн-партиционирование ниже. Если вы просто использовали ту же самую последовательность в новой разделенной таблице, вы должны быть в порядке.

Теперь мы можем использовать процедуру partition_data_proc() для миграции наших данных из старой таблицы в новую. И мы собираемся сделать это в инкрементах по 1 000 строк против интервала в 10 000, который установлен для набора секций. Значение пакета используется, чтобы сообщить ему, сколько раз пройти через заданный интервал; значение по умолчанию 1 создает только одну дочернюю таблицу. Поскольку мы хотим разделить все данные, просто дайте ему число, равное или большее ожидаемому количеству дочерних таблиц. У этой процедуры есть опция, где вы можете указать ей источник данных, что и позволит нам мигрировать данные из старой таблицы. Без установки этой опции, она пытается очистить данные из секции DEFAULT (что мы увидим на следующем примере).

CALL partman.partition_data_proc('public.original_table', p_interval := '1000', p_batch := 200, p_source_table := 'public.old_nonpartitioned_table');
NOTICE:  Batch: 1, Rows moved: 1000
NOTICE:  Batch: 2, Rows moved: 1000
NOTICE:  Batch: 3, Rows moved: 1000
NOTICE:  Batch: 4, Rows moved: 1000
NOTICE:  Batch: 5, Rows moved: 1000
NOTICE:  Batch: 6, Rows moved: 1000
NOTICE:  Batch: 7, Rows moved: 1000
NOTICE:  Batch: 8, Rows moved: 1000
NOTICE:  Batch: 9, Rows moved: 1000
NOTICE:  Batch: 10, Rows moved: 999
NOTICE:  Batch: 11, Rows moved: 1000
NOTICE:  Batch: 12, Rows moved: 1000
[...]
NOTICE:  Batch: 100, Rows moved: 1000
NOTICE:  Batch: 101, Rows moved: 1
NOTICE:  Total rows moved: 100000
NOTICE:  Ensure to VACUUM ANALYZE the parent (and source table if used) after partitioning data
CALL
Time: 103206.205 ms (01:43.206)


VACUUM ANALYZE public.original_table;
VACUUM
Time: 352.973 ms

Снова, выполнение коммитов в таких маленьких партиях может избежать транзакций с огромным количеством строк и долгим временем выполнения, когда вы разделяете таблицу, которая может содержать миллиарды строк. Всегда рекомендуется избегать долгих транзакций, чтобы позволить процессу Tantor SE autovacuum работать эффективно для остальной части базы данных.

Использование ПРОЦЕДУРЫ partition_data_proc() против ФУНКЦИИ partition_data_id() позволяет выполнять эти пакеты коммитов. Функции в Tantor SE всегда выполняются полностью в одной транзакции, даже если вы можете указать ей делать вещи пакетами внутри функции.

Теперь, если мы проверим нашу исходную таблицу, она будет пуста

SELECT count(*) FROM old_nonpartitioned_table ;
 count 
-------
     0
(1 row)

И новая, разделенная таблица с исходным именем содержит все данные и созданные дочерние таблицы

SELECT count(*) FROM original_table;
 count  
--------
 100000
(1 row)


\d+ public.original_table
                                 Partitioned table "public.original_table"
 Column |           Type           | Collation | Nullable | Default | Storage  | Stats target | Description 
--------+--------------------------+-----------+----------+---------+----------+--------------+-------------
 col1   | bigint                   |           | not null |         | plain    |              | 
 col2   | text                     |           | not null |         | extended |              | 
 col3   | timestamp with time zone |           |          | now()   | plain    |              | 
 col4   | text                     |           |          |         | extended |              | 
Partition key: RANGE (col1)
Indexes:
    "original_table_col1_idx1" btree (col1)
Partitions: original_table_p0 FOR VALUES FROM ('0') TO ('10000'),
            original_table_p10000 FOR VALUES FROM ('10000') TO ('20000'),
            original_table_p100000 FOR VALUES FROM ('100000') TO ('110000'),
            original_table_p20000 FOR VALUES FROM ('20000') TO ('30000'),
            original_table_p30000 FOR VALUES FROM ('30000') TO ('40000'),
            original_table_p40000 FOR VALUES FROM ('40000') TO ('50000'),
            original_table_p50000 FOR VALUES FROM ('50000') TO ('60000'),
            original_table_p60000 FOR VALUES FROM ('60000') TO ('70000'),
            original_table_p70000 FOR VALUES FROM ('70000') TO ('80000'),
            original_table_p80000 FOR VALUES FROM ('80000') TO ('90000'),
            original_table_p90000 FOR VALUES FROM ('90000') TO ('100000'),
            original_table_default DEFAULT

SELECT count(*) FROM original_table_p10000;
 count 
-------
 10000
(1 row)

Теперь вы можете начать использовать вашу таблицу так же, как вы делали это раньше!

F.41.11.3.2. Онлайн-партиционирование

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

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

Это будет ежедневный набор секций по времени с последовательностью IDENTITY в качестве первичного ключа

CREATE TABLE public.original_table (
    col1 bigint not null PRIMARY KEY GENERATED ALWAYS AS IDENTITY
    , col2 text not null
    , col3 timestamptz DEFAULT now() not null
    , col4 text); 

CREATE INDEX CONCURRENTLY ON public.original_table (col3);


INSERT INTO public.original_table (col2, col3, col4) VALUES ('stuff', generate_series(now() - '1 week'::interval, now(), '5 minutes'::interval), 'stuff');

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

CREATE TABLE public.new_partitioned_table (
    col1 bigint not null GENERATED BY DEFAULT AS IDENTITY
    , col2 text not null
    , col3 timestamptz DEFAULT now() not null
    , col4 text) PARTITION BY RANGE (col3);

CREATE INDEX ON public.new_partitioned_table (col3);

Вы заметите, что я не установил col1 в качестве первичного ключа. Это потому, что мы не можем.

CREATE TABLE public.new_partitioned_table (
    col1 bigint not null PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
    , col2 text not null
    , col3 timestamptz DEFAULT now() not null
    , col4 text) PARTITION BY RANGE (col3);
ERROR:  unique constraint on partitioned table must include all partitioning columns
DETAIL:  PRIMARY KEY constraint on table "new_partitioned_table" lacks column "col3" which is part of the partition key.

pg_partman имеет механизм для применения первичных/уникальных ключей, которые не являются частью столбца разделения. Просто имейте в виду, что они НЕ применяются ко всему набору секций; только для отдельной секции. Это делается с помощью шаблонной таблицы. И чтобы гарантировать, что ключи применяются при создании начальных дочерних таблиц, эту шаблонную таблицу необходимо предварительно создать и предоставить ее имя при вызове create_parent(). Мы собираемся использовать исходную таблицу в качестве основы и дать имя, похожее на это, чтобы оно имело смысл после переименования позже.

Еще одно важное замечание заключается в том, что мы изменили столбец IDENTITY с GENERATED ALWAYS на GENERATED BY DEFAULT. Это связано с тем, что нам нужно переместить существующие значения для этого столбца идентификатора на свое место. ALWAYS обычно предотвращает ввод значений вручную.

CREATE TABLE public.original_table_template (LIKE public.original_table);

ALTER TABLE public.original_table_template ADD PRIMARY KEY (col1);

Если вы не создадите заранее шаблонную таблицу, pg_partman всегда создаст ее для вас в той же схеме, в которую было установлено расширение. Вы можете увидеть ее имя, посмотрев на столбец template_table в таблице part_config. Однако, если вы добавите индекс в эту шаблонную таблицу после вызова create_parent(), уже существующие дочерние таблицы не получат этот индекс, и вам придется вернуться и сделать это вручную. Однако, любые новые дочерние таблицы, созданные после этого, будут иметь этот индекс.

Сложность здесь в том, что у нас еще не может быть дочерних таблиц в наборе секций, которые соответствуют данным, которые в настоящее время существуют в исходной таблице. Это связано с тем, что мы собираемся добавить старую таблицу как таблицу DEFAULT в нашу новую таблицу секций. Если таблица DEFAULT содержит какие-либо данные, которые соответствуют ограничениям текущей дочерней таблицы, Tantor SE не позволит добавить эту таблицу. Так что, с помощью нижеприведенного вызова create_parent(), мы начнем набор секций значительно раньше, чем вставленные нами данные. В вашем случае вам придется посмотреть на ваш текущий набор данных и выбрать значение, значительно превышающее текущий рабочий набор данных, который может быть вставлен до того, как вы сможете запустить процесс переименования таблицы ниже. Мы также устанавливаем значение premake на низкое значение, чтобы избежать необходимости переименовывать слишком много дочерних таблиц позже. Мы увеличим premake обратно до значения по умолчанию позже (или вы можете установить его в соответствии с вашими требованиями).

select min(col3), max(col3) from original_table;
              min              |              max              
-------------------------------+-------------------------------
 2020-12-02 19:04:08.559646-05 | 2020-12-09 19:04:08.559646-05
(1 row)

SELECT partman.create_parent('public.new_partitioned_table', 'col3', 'native', 'daily', p_template_table:= 'public.original_table_template', p_premake := 1, p_start_partition := (CURRENT_TIMESTAMP+'2 days'::interval)::text);

Следующим шагом будет удаление секции DEFAULT, который создает для вас pg_partman.

DROP TABLE public.new_partitioned_table_default;

Состояние новой разделенной таблицы теперь должно выглядеть примерно так. Текущая дата, когда был написан этот HowTo, дана для справки:

SELECT CURRENT_TIMESTAMP;
       current_timestamp       
-------------------------------
 2020-12-09 19:05:15.358796-05

\d+ new_partitioned_table;
                                          Partitioned table "public.new_partitioned_table"
 Column |           Type           | Collation | Nullable |             Default              | Storage  | Stats target | Description 
--------+--------------------------+-----------+----------+----------------------------------+----------+--------------+-------------
 col1   | bigint                   |           | not null | generated by default as identity | plain    |              | 
 col2   | text                     |           | not null |                                  | extended |              | 
 col3   | timestamp with time zone |           | not null | now()                            | plain    |              | 
 col4   | text                     |           |          |                                  | extended |              | 
Partition key: RANGE (col3)
Indexes:
    "new_partitioned_table_col3_idx" btree (col3)
Partitions: new_partitioned_table_p2020_12_11 FOR VALUES FROM ('2020-12-11 00:00:00-05') TO ('2020-12-12 00:00:00-05')

Вам также нужно обновить таблицу part_config так, чтобы в ней было указано оригинальное имя таблицы. Вы также можете обновить шаблонную таблицу, если вы не создавали ее вручную, просто убедитесь, что вы переименовали таблицу и обновили таблицу part_config также. Мы также сбросим значение premake до значения по умолчанию.

UPDATE partman.part_config SET parent_table = 'public.original_table', premake = 4 WHERE parent_table = 'public.new_partitioned_table';
UPDATE 1

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

  1. НАЧАЛО транзакции

  2. Возьмите эксклюзивную блокировку на исходную таблицу и новую таблицу, чтобы гарантировать отсутствие пробелов, из-за которых данные могут быть неправильно направлены

  3. Если используется столбец IDENTITY, получите исходное последнее значение

  4. Переименуйте исходную таблицу в имя таблицы DEFAULT для набора секций

  5. Если используется столбец IDENTITY, удалите IDENTITY из старой таблицы

  6. Переименуйте новую таблицу в имя оригинальной таблицы и переименуйте дочерние таблицы & последовательность для соответствия.

  7. Если используется столбец IDENTITY, сбросьте идентификатор новой таблицы до последнего значения, чтобы любые новые вставки продолжились с того места, где остановилась последовательность старой таблицы.

  8. Добавьте исходную таблицу как DEFAULT для набора секций

  9. COMMIT транзакция

Если вы используете столбец IDENTITY, важно получить его последнее значение, пока исходная таблица заблокирована и ДО ТОГО, как вы удалите старый идентификатор. Затем используйте это возвращенное значение в операторе для СБРОСА столбца IDENTITY в новой таблице. Запрос для получения этого представлен в SQL-операторах ниже.

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

BEGIN;

LOCK TABLE public.original_table IN ACCESS EXCLUSIVE MODE;
LOCK TABLE public.new_partitioned_table IN ACCESS EXCLUSIVE MODE;

SELECT max(col1) FROM public.original_table;

ALTER TABLE public.original_table RENAME TO original_table_default;

-- IF using an IDENTITY column
ALTER TABLE public.original_table_default ALTER col1 DROP IDENTITY;

ALTER TABLE public.new_partitioned_table RENAME TO original_table;
ALTER TABLE public.new_partitioned_table_p2020_12_11 RENAME TO original_table_p2020_12_11;

-- IF using an IDENTITY column
ALTER SEQUENCE public.new_partitioned_table_col1_seq RENAME TO original_table_col1_seq;

-- IF using an IDENTITY column
ALTER TABLE public.original_table ALTER col1 RESTART WITH <<<VALUE OBTAINED ABOVE>>>;

ALTER TABLE public.original_table ATTACH PARTITION public.original_table_default DEFAULT;
COMMIT; or ROLLBACK;

После выполнения COMMIT, новая разделенная таблица должна теперь заменить оригинальную неразделенную таблицу. И если все свойства были применены к новой таблице, она должна работать без каких-либо проблем. Любые новые данные, которые поступают, должны либо идти в соответствующую дочернюю таблицу, либо, если она еще не существует, они должны идти в DEFAULT. Последнее не является проблемой, так как…

Следующим шагом будет разделение данных из значения по умолчанию. Вы НЕ должны оставлять данные в наборе секций по умолчанию на любой протяженности времени и особенно оставлять значительное количество данных. Если вы посмотрите на ограничение, которое существует на разделе по умолчанию, это в основном анти-ограничение всех других дочерних таблиц. И когда добавляется новая дочерняя таблица, Tantor SE управляет обновлением этого ограничения по умолчанию по мере необходимости. Но он должен проверить, есть ли какие-либо данные, которые должны принадлежать этой новой дочерней таблице, уже существуют в значении по умолчанию. Если он найдет такие, он завершится с ошибкой. Но что более важно, он должен проверить КАЖДУЮ запись в значении по умолчанию, что может занять довольно много времени, даже с индексом, если есть миллиарды строк. Во время этой проверки на весь набор секций налагается эксклюзивная блокировка.

Функция partition_data_proc() может обрабатывать перемещение данных из значения по умолчанию. Однако она не может перемещать данные в любом интервале, меньшем, чем интервал разделения, при перемещении данных из значения DEFAULT. Это связано с тем, что было только что упомянуто: вы не можете добавить дочернюю таблицу в набор секций, если ограничение новой дочерней таблицы охватывает данные, которые уже существуют в значении по умолчанию.

pg_partman обрабатывает это, сначала перемещая все данные для данной дочерней таблицы во временную таблицу, затем создавая дочернюю таблицу, а затем перемещая данные из временной таблицы в новую дочернюю таблицу. Поскольку мы перемещаем данные из DEFAULT и мы не можем использовать меньший интервал, единственный параметр, который нам нужно передать, это размер пакета. По умолчанию размер пакета 1 будет создавать только одну дочернюю таблицу, а затем остановится. Если вы хотите переместить все данные за один вызов, просто передайте значение, достаточно большое, чтобы охватить ожидаемое количество дочерних таблиц. Однако, при работе с активной таблицей и МНОГО строками, это потенциально может генерировать МНОГО файлов WAL, особенно учитывая, что этот метод удваивает количество записей по сравнению с оффлайн методом (default -> temp -> child table). Поэтому, если вам важно контролировать использование диска, просто укажите меньшее значение пакета и дайте Tantor SE некоторое время для выполнения нескольких CHECKPOINT и очистки собственного WAL перед переходом к следующему пакету.

CALL partman.partition_data_proc('public.original_table', p_batch := 200);

NOTICE:  Batch: 1, Rows moved: 60
NOTICE:  Batch: 2, Rows moved: 288
NOTICE:  Batch: 3, Rows moved: 288
NOTICE:  Batch: 4, Rows moved: 288
NOTICE:  Batch: 5, Rows moved: 288
NOTICE:  Batch: 6, Rows moved: 288
NOTICE:  Batch: 7, Rows moved: 288
NOTICE:  Batch: 8, Rows moved: 229
NOTICE:  Total rows moved: 2017
NOTICE:  Ensure to VACUUM ANALYZE the parent (and source table if used) after partitioning data
CALL
Time: 8432.725 ms (00:08.433)

VACUUM ANALYZE original_table;
VACUUM
Time: 60.690 ms

Если вы ранее использовали столбец IDENTITY с GENERATED ALWAYS, вам потребуется изменить идентификатор в разделенной таблице обратно на тот, что был до текущего значения BY DEFAULT

ALTER TABLE public.original_table ALTER col1 SET GENERATED ALWAYS;

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

\d+ original_table;
                                              Partitioned table "public.original_table"
 Column |           Type           | Collation | Nullable |             Default              | Storage  | Stats target | Description 
--------+--------------------------+-----------+----------+----------------------------------+----------+--------------+-------------
 col1   | bigint                   |           | not null | generated by default as identity | plain    |              | 
 col2   | text                     |           | not null |                                  | extended |              | 
 col3   | timestamp with time zone |           | not null | now()                            | plain    |              | 
 col4   | text                     |           |          |                                  | extended |              | 
Partition key: RANGE (col3)
Indexes:
    "new_partitioned_table_col3_idx" btree (col3)
Partitions: original_table_p2020_12_02 FOR VALUES FROM ('2020-12-02 00:00:00-05') TO ('2020-12-03 00:00:00-05'),
            original_table_p2020_12_03 FOR VALUES FROM ('2020-12-03 00:00:00-05') TO ('2020-12-04 00:00:00-05'),
            original_table_p2020_12_04 FOR VALUES FROM ('2020-12-04 00:00:00-05') TO ('2020-12-05 00:00:00-05'),
            original_table_p2020_12_05 FOR VALUES FROM ('2020-12-05 00:00:00-05') TO ('2020-12-06 00:00:00-05'),
            original_table_p2020_12_06 FOR VALUES FROM ('2020-12-06 00:00:00-05') TO ('2020-12-07 00:00:00-05'),
            original_table_p2020_12_07 FOR VALUES FROM ('2020-12-07 00:00:00-05') TO ('2020-12-08 00:00:00-05'),
            original_table_p2020_12_08 FOR VALUES FROM ('2020-12-08 00:00:00-05') TO ('2020-12-09 00:00:00-05'),
            original_table_p2020_12_09 FOR VALUES FROM ('2020-12-09 00:00:00-05') TO ('2020-12-10 00:00:00-05'),
            original_table_p2020_12_11 FOR VALUES FROM ('2020-12-11 00:00:00-05') TO ('2020-12-12 00:00:00-05'),

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

SELECT partman.run_maintenance('public.original_table');

\d+ original_table;
                                              Partitioned table "public.original_table"
 Column |           Type           | Collation | Nullable |             Default              | Storage  | Stats target | Description 
--------+--------------------------+-----------+----------+----------------------------------+----------+--------------+-------------
 col1   | bigint                   |           | not null | generated by default as identity | plain    |              | 
 col2   | text                     |           | not null |                                  | extended |              | 
 col3   | timestamp with time zone |           | not null | now()                            | plain    |              | 
 col4   | text                     |           |          |                                  | extended |              | 
Partition key: RANGE (col3)
Indexes:
    "new_partitioned_table_col3_idx" btree (col3)
Partitions: original_table_p2020_12_02 FOR VALUES FROM ('2020-12-02 00:00:00-05') TO ('2020-12-03 00:00:00-05'),
            original_table_p2020_12_03 FOR VALUES FROM ('2020-12-03 00:00:00-05') TO ('2020-12-04 00:00:00-05'),
            original_table_p2020_12_04 FOR VALUES FROM ('2020-12-04 00:00:00-05') TO ('2020-12-05 00:00:00-05'),
            original_table_p2020_12_05 FOR VALUES FROM ('2020-12-05 00:00:00-05') TO ('2020-12-06 00:00:00-05'),
            original_table_p2020_12_06 FOR VALUES FROM ('2020-12-06 00:00:00-05') TO ('2020-12-07 00:00:00-05'),
            original_table_p2020_12_07 FOR VALUES FROM ('2020-12-07 00:00:00-05') TO ('2020-12-08 00:00:00-05'),
            original_table_p2020_12_08 FOR VALUES FROM ('2020-12-08 00:00:00-05') TO ('2020-12-09 00:00:00-05'),
            original_table_p2020_12_09 FOR VALUES FROM ('2020-12-09 00:00:00-05') TO ('2020-12-10 00:00:00-05'),
            original_table_p2020_12_11 FOR VALUES FROM ('2020-12-11 00:00:00-05') TO ('2020-12-12 00:00:00-05'),
            original_table_p2020_12_12 FOR VALUES FROM ('2020-12-12 00:00:00-05') TO ('2020-12-13 00:00:00-05'),
            original_table_p2020_12_13 FOR VALUES FROM ('2020-12-13 00:00:00-05') TO ('2020-12-14 00:00:00-05'),
            original_table_default DEFAULT

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

SELECT * FROM partman.check_default(p_exact_count := true);

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

Вы также заметите, что в указанном выше наборе отсутствует дочерняя таблица (10 декабря 2020 года). Это произошло потому, что мы установили начало таблицы секций на 2 дня вперед, и у нас не было данных для этой даты в исходной таблице. Вы можете исправить это одним из двух способов:

  1. Дождитесь, пока данные за этот период времени будут вставлены, и как только вы убедитесь, что интервал завершен, разделите данные из DEFAULT таким же образом, как мы делали ранее.

  2. Запустите функцию partition_gap_fill() для немедленного заполнения любых пропусков:

SELECT * FROM partman.partition_gap_fill('public.original_table');
 partition_gap_fill 
--------------------
                  1
(1 row)

\d+ original_table;
                                              Partitioned table "public.original_table"
 Column |           Type           | Collation | Nullable |             Default              | Storage  | Stats target | Description 
--------+--------------------------+-----------+----------+----------------------------------+----------+--------------+-------------
 col1   | bigint                   |           | not null | generated by default as identity | plain    |              | 
 col2   | text                     |           | not null |                                  | extended |              | 
 col3   | timestamp with time zone |           | not null | now()                            | plain    |              | 
 col4   | text                     |           |          |                                  | extended |              | 
Partition key: RANGE (col3)
Indexes:
    "new_partitioned_table_col3_idx" btree (col3)
Partitions: original_table_p2020_12_02 FOR VALUES FROM ('2020-12-02 00:00:00-05') TO ('2020-12-03 00:00:00-05'),
            original_table_p2020_12_03 FOR VALUES FROM ('2020-12-03 00:00:00-05') TO ('2020-12-04 00:00:00-05'),
            original_table_p2020_12_04 FOR VALUES FROM ('2020-12-04 00:00:00-05') TO ('2020-12-05 00:00:00-05'),
            original_table_p2020_12_05 FOR VALUES FROM ('2020-12-05 00:00:00-05') TO ('2020-12-06 00:00:00-05'),
            original_table_p2020_12_06 FOR VALUES FROM ('2020-12-06 00:00:00-05') TO ('2020-12-07 00:00:00-05'),
            original_table_p2020_12_07 FOR VALUES FROM ('2020-12-07 00:00:00-05') TO ('2020-12-08 00:00:00-05'),
            original_table_p2020_12_08 FOR VALUES FROM ('2020-12-08 00:00:00-05') TO ('2020-12-09 00:00:00-05'),
            original_table_p2020_12_09 FOR VALUES FROM ('2020-12-09 00:00:00-05') TO ('2020-12-10 00:00:00-05'),
            original_table_p2020_12_10 FOR VALUES FROM ('2020-12-10 00:00:00-05') TO ('2020-12-11 00:00:00-05'),
            original_table_p2020_12_11 FOR VALUES FROM ('2020-12-11 00:00:00-05') TO ('2020-12-12 00:00:00-05'),
            original_table_p2020_12_12 FOR VALUES FROM ('2020-12-12 00:00:00-05') TO ('2020-12-13 00:00:00-05'),
            original_table_p2020_12_13 FOR VALUES FROM ('2020-12-13 00:00:00-05') TO ('2020-12-14 00:00:00-05'),
            original_table_default DEFAULT

Вы можете видеть, что создалась отсутствующая таблица для 10 декабря.

На этом этапе ваша новая разделенная таблица уже должна была использоваться и работать без каких-либо проблем!

INSERT INTO original_table (col2, col3, col4) VALUES ('newstuff', now(), 'newstuff');
INSERT INTO original_table (col2, col3, col4) VALUES ('newstuff', now(), 'newstuff');
SELECT * FROM original_table ORDER BY col1 DESC limit 5;

F.41.11.4. Отмена нативного партицирования

Так же как обычная таблица не может быть преобразована в нативно разделенную таблицу, то же самое верно и в обратном случае. Чтобы отменить нативное партицирование, необходимо переместить данные в новую таблицу. Возможно, есть способ сделать это в онлайн-режиме, но на данный момент у нас нет такого метода. Если кто-то может предложить метод или хочет, чтобы я изучил вопрос подробнее, пожалуйста, создайте запрос на Github. Ниже приведен метод, показывающий как отменить ежедневное партицирование, приведенное в примере выше, включая обработку столбца IDENTITY, при необходимости.

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

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

CREATE TABLE public.new_regular_table (
    col1 bigint not null GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
    , col2 text not null
    , col3 timestamptz DEFAULT now() not null
    , col4 text);

CREATE INDEX ON public.new_regular_table (col3);

Теперь мы можем использовать процедуру undo_partition_proc() для перемещения данных из нашей разделенной таблицы в обычную таблицу. Мы даже можем выбрать меньший размер интервала для этого, чтобы уменьшить время выполнения транзакции для каждой партии. Размер партии по умолчанию равен 1, что означает выполнение заданного интервала один раз. Мы хотим отменить все с одним вызовом, поэтому передаем число, достаточно большое, чтобы пройти через все партии. Оно остановится, когда все данные будут перемещены, даже если вы передали больший номер партии. Нам также не нужно сохранять старые дочерние таблицы, когда они пусты, поэтому это установлено как false. Смотрите документацию для получения дополнительной информации о других вариантах функций/процедур отмены.

CALL partman.undo_partition_proc('public.original_table', p_interval := '1 hour'::text, p_batch := 500, p_target_table := 'public.new_regular_table', p_keep_table := false);

NOTICE:  Moved 13 row(s) to the target table. Removed 1 partitions.
NOTICE:  Batch: 1, Partitions undone this batch: 1, Rows undone this batch: 13
NOTICE:  Moved 13 row(s) to the target table. Removed 0 partitions.
NOTICE:  Batch: 2, Partitions undone this batch: 0, Rows undone this batch: 13
NOTICE:  Moved 13 row(s) to the target table. Removed 0 partitions.
NOTICE:  Batch: 3, Partitions undone this batch: 0, Rows undone this batch: 13
[...]
NOTICE:  Batch: 160, Partitions undone this batch: 0, Rows undone this batch: 13
NOTICE:  Moved 5 row(s) to the target table. Removed 1 partitions.
NOTICE:  Batch: 161, Partitions undone this batch: 1, Rows undone this batch: 5
NOTICE:  Moved 0 row(s) to the target table. Removed 4 partitions.
NOTICE:  Total partitions undone: 13, Total rows moved: 2017
NOTICE:  Ensure to VACUUM ANALYZE the old parent & target table after undo has finished
CALL
Time: 163465.195 ms (02:43.465)

VACUUM ANALYZE original_table;
VACUUM
Time: 20.706 ms
VACUUM ANALYZE new_regular_table;
VACUUM
Time: 20.375 ms

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

SELECT max(col1) FROM public.original_table;

ALTER TABLE original_table RENAME TO old_partitioned_table;
ALTER SEQUENCE original_table_col1_seq RENAME TO old_partitioned_table_col1_seq;

ALTER TABLE new_regular_table RENAME TO original_table;
ALTER SEQUENCE new_regular_table_col1_seq RENAME TO original_table_col1_seq;
ALTER TABLE public.original_table ALTER col1 RESTART WITH <<<VALUE OBTAINED ABOVE>>>;
ALTER TABLE public.original_table ALTER col1 SET GENERATED ALWAYS;
INSERT INTO original_table (col2, col3, col4) VALUES ('newstuff', now(), 'newstuff');
INSERT INTO original_table (col2, col3, col4) VALUES ('newstuff', now(), 'newstuff');
SELECT * FROM original_table ORDER BY col1 DESC limit 2;

F.41.12. Миграция существующего набора секций в PG Partition Manager

Этот документ помогает при переходе на использование pg_partman для уже существующего набора разделенных таблиц. Обратите внимание, что в настоящее время это руководство предназначено только для не-нативного, основанного на триггерах партицирования. Документация для нативного партицирования находится в процессе разработки, но она будет в основном сосредоточена на PostgreSQL 11, поскольку версия 10 имела очень ограниченную поддержку партицирования. Пока что самый простой способ перейти на нативное партицирование таблиц - это создать совершенно новую таблицу и скопировать/переместить данные.

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

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

Следующие таблицы будут использоваться в примерах:

                                  Table "tracking.hits"
 Column |            Type             | Modifiers | Storage | Stats target | Description 
--------+-----------------------------+-----------+---------+--------------+-------------
 id     | integer                     | not null  | plain   |              | 
 start  | timestamp without time zone | not null  | plain   |              | 
Child tables: tracking.hits20160103,
              tracking.hits20160110,
              tracking.hits20160117

insert into tracking.hits20160103 values (1, generate_series('2016-01-03 01:00:00'::timestamptz, '2016-01-09 23:00:00'::timestamptz, '1 hour'::interval));
insert into tracking.hits20160110 values (1, generate_series('2016-01-10 01:00:00'::timestamptz, '2016-01-16 23:00:00'::timestamptz, '1 hour'::interval));
insert into tracking.hits20160117 values (1, generate_series('2016-01-17 01:00:00'::timestamptz, '2016-01-23 23:00:00'::timestamptz, '1 hour'::interval));

                                  Table "tracking.hits"
 Column |            Type             | Modifiers | Storage | Stats target | Description 
--------+-----------------------------+-----------+---------+--------------+-------------
 id     | integer                     | not null  | plain   |              | 
 start  | timestamp without time zone | not null  | plain   |              | 
Child tables: tracking.hits1000,
              tracking.hits2000,
              tracking.hits3000

insert into tracking.hits1000 values (generate_series(1000,1999), now());
insert into tracking.hits2000 values (generate_series(2000,2999), now());
insert into tracking.hits3000 values (generate_series(3000,3999), now());

                                  Table "tracking.hits"
 Column |            Type             | Modifiers | Storage | Stats target | Description 
--------+-----------------------------+-----------+---------+--------------+-------------
 id     | integer                     | not null  | plain   |              | 
 start  | timestamp without time zone | not null  | plain   |              | 
Child tables: tracking.hits_aa,
              tracking.hits_bb,
              tracking.hits_cc

Data depends on partitioning type. See below.

F.41.12.1. Шаг 1

Отключить вызовы run_maintenance()

Если у вас есть какие-либо секции, которые в настоящее время поддерживаются pg_partman, вы можете уже вызывать это для них. Они должны быть в порядке на протяжении времени, когда выполняется это преобразование. Это сделано для избежания любых проблем с существованием только частичной конфигурации во время преобразования. Если вы используете фоновый рабочий процесс, закомментирование параметра pg_partman_bgw.dbname в postgresql.conf и затем перезагрузка (SELECT pg_reload_conf();) должны быть достаточными, чтобы остановить его работу. Если вы запускаете pg_partman на нескольких базах данных в кластере и вы не хотите останавливать их все, вы также можете просто удалить ту, на которой вы выполняете миграцию, из того же параметра.

F.41.12.2. Шаг 2

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

F.41.12.3. Шаг 3

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

_pYYYY                - Yearly (and any custom time greater than this)
_pYYYY"q"Q            - Quarterly (double quotes required to add another string value inside a date/time format string)
_pYYYY_MM             - Monthly (and all custom time intervals between yearly and monthly)
_pIYYY"w"IW           - Weekly (ISO Year and ISO Week)
_pYYYY_MM_DD          - Daily (and all custom time intervals between monthly and daily)
_pYYYY_MM_DD_HH24MI   - Hourly, Half-Hourly, Quarter-Hourly (and all custom time intervals between daily and hourly)
_pYYYY_MM_DD_HH24MISS - Only used with custom time if interval is less than 1 minute (cannot be less than 1 second)
_p#####               - Serial/ID partition has a suffix that is the value of the lowest possible entry in that table (Ex: _p10, _p20000, etc)

F.41.12.4. Шаг 3a

Для преобразования наборов секций на основе времени или серий, если у вас уже есть нижнее граничное значение в качестве части имени секции, тогда это просто вопрос переименования с некоторым форматированием подстроки, поскольку это шаблон, который использует pg_partman. Предположим, ваша таблица была разделена по неделям, и ваш оригинальный формат просто имел первый день недели (воскресенье) для имени секции (как в примере выше). Вы можете видеть ниже, что у нас было 3 секции со старым шаблоном именования YYYYMMDD. Глядя на список выше, вы можете видеть новый недельный шаблон, который использует pg_partman.

Примечание о квартальном разделении... функция to_timestamp() не распознает строку формата Q, как это делает to_char(). Почему? Вы можете посмотреть в исходный код postgres и увидеть причину, но для меня это не важно. Я делаю это внутри функции show_partition_info(), если вам нужен способ партицирования.

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

select 'ALTER TABLE '||n.nspname||'.'||c.relname||' RENAME TO '||substring(c.relname from 1 for 4)||'_p'||to_char(to_timestamp(substring(c.relname from 5), 'YYYYMMDD'), 'IYYY')||'w'||to_char(to_timestamp(substring(c.relname from 5), 'YYYYMMDD'), 'IW')||';'
        from pg_inherits h
        join pg_class c on h.inhrelid = c.oid
        join pg_namespace n on c.relnamespace = n.oid
        where h.inhparent::regclass = 'tracking.hits'::regclass
        order by c.relname;

Который выводит:

ALTER TABLE tracking.hits20160103 RENAME TO hits_p2015w53;
ALTER TABLE tracking.hits20160110 RENAME TO hits_p2016w01;
ALTER TABLE tracking.hits20160117 RENAME TO hits_p2016w02;

Запуск этого должен переименовать ваши таблицы, чтобы они теперь выглядели так:

   tablename   | schemaname 
---------------+------------
 hits          | tracking
 hits_p2015w53 | tracking
 hits_p2016w01 | tracking
 hits_p2016w02 | tracking

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

select 'ALTER TABLE '||n.nspname||'.'||c.relname||' RENAME TO '||substring(c.relname from 1 for 4)||'_p'||substring(c.relname from 5)||';'
    from pg_inherits h
    join pg_class c on h.inhrelid = c.oid
    join pg_namespace n on c.relnamespace = n.oid
    where h.inhparent::regclass = 'tracking.hits'::regclass
    order by c.relname;

ALTER TABLE tracking.hits1000 RENAME TO hits_p1000;
ALTER TABLE tracking.hits2000 RENAME TO hits_p2000;
ALTER TABLE tracking.hits3000 RENAME TO hits_p3000;

 tablename | schemaname 
-----------+------------
 hits      | tracking
 hits1000  | tracking
 hits2000  | tracking
 hits3000  | tracking

F.41.12.5. Шаг 3b

Если ваши разделенные наборы названы таким образом, что они относятся к содержащимся в них данным иначе, или вообще не относятся, вам придется переименовывать их на основе наименьшего значения в управляющем столбце. Я буду использовать пример выше с суффиксами _aa, _bb, & _cc.

Если это разделено по времени, предположим, что следующие данные существуют в дочерних таблицах:

insert into tracking.hits_aa values (1, generate_series('2016-01-03 01:00:00'::timestamptz, '2016-01-09 23:00:00'::timestamptz, '1 hour'::interval));
insert into tracking.hits_bb values (2, generate_series('2016-01-10 01:00:00'::timestamptz, '2016-01-16 23:00:00'::timestamptz, '1 hour'::interval));
insert into tracking.hits_cc values (3, generate_series('2016-01-17 01:00:00'::timestamptz, '2016-01-23 23:00:00'::timestamptz, '1 hour'::interval));

Этот следующий шаг использует анонимные блоки кода. Это в основном написание кода функции pl/pgsql без создания фактической функции. Просто запустите этот блок кода, корректируя значения по мере необходимости, прямо в сеансе psql.

DO $rename$
DECLARE 
    v_min_val           timestamp;
    v_row               record;
    v_sql               text;
BEGIN

-- Adjust your parent table name in the for loop query
FOR v_row IN
    SELECT n.nspname AS child_schema, c.relname AS child_table
        FROM pg_inherits h
        JOIN pg_class c ON h.inhrelid = c.oid
        JOIN pg_namespace n ON c.relnamespace = n.oid
        WHERE h.inhparent::regclass = 'tracking.hits'::regclass
        ORDER BY c.relname
LOOP
    -- Substitute your control column's name here in the min() function
    v_sql := format('SELECT min(start) FROM %I.%I', v_row.child_schema, v_row.child_table);
    EXECUTE v_sql INTO v_min_val;

    -- Adjust the date_trunc here to account for whatever your partitioning interval is. 
    v_min_val := date_trunc('week', v_min_val);

    -- Build the sql statement to rename the child table
    -- Use the appropriate date/time string from the list above for your interval
    v_sql := format('ALTER TABLE %I.%I RENAME TO %I' 
            , v_row.child_schema 
            , v_row.child_table
            , substring(v_row.child_table from 1 for 4)||'_p'||to_char(v_min_val, 'IYYY"w"IW'));

    -- I just have it outputting the ALTER statement for review. If you'd like this code to actually run it, uncomment the EXECUTE below.
    RAISE NOTICE '%', v_sql;
    -- EXECUTE v_sql;
END LOOP;

END
$rename$;

Это выведет что-то вроде этого:

NOTICE:  ALTER TABLE tracking.hits_aa RENAME TO hits_p2015w53
NOTICE:  ALTER TABLE tracking.hits_bb RENAME TO hits_p2016w01
NOTICE:  ALTER TABLE tracking.hits_cc RENAME TO hits_p2016w02

Я бы рекомендовал запустить его хотя бы один раз с закомментированным окончательным EXECUTE для проверки того, что он генерирует. Если все выглядит хорошо, вы можете раскомментировать EXECUTE и переименовать свои таблицы!

Если у вас есть набор секций serial/id, то правильное значение суффикса можно вычислить, используя арифметику модулей. Предположим, что в том же примере набора секций, что и ранее, используются следующие значения:

insert into tracking.hits_aa values (generate_series(1100,1294), now());
insert into tracking.hits_bb values (generate_series(2400,2991), now());
insert into tracking.hits_cc values (generate_series(3602,3843), now());

Мы снова будем разделять на 1000, и вы можете видеть, что ни одно из минимальных значений не является таким четким.

DO $rename$
DECLARE 
    v_min_val           bigint;
    v_row               record;
    v_sql               text;
BEGIN

-- Adjust your parent table name in the for loop query
FOR v_row IN
    SELECT n.nspname AS child_schema, c.relname AS child_table
        FROM pg_inherits h
        JOIN pg_class c ON h.inhrelid = c.oid
        JOIN pg_namespace n ON c.relnamespace = n.oid
        WHERE h.inhparent::regclass = 'tracking.hits'::regclass
        ORDER BY c.relname
LOOP
    -- Substitute your control column's name here in the min() function
    v_sql := format('SELECT min(id) FROM %I.%I', v_row.child_schema, v_row.child_table);
    EXECUTE v_sql INTO v_min_val;

    -- Adjust the numerical value after the % to account for whatever your partitioning interval is. 
    v_min_val := v_min_val - (v_min_val % 1000);

    -- Build the sql statement to rename the child table
    v_sql := format('ALTER TABLE %I.%I RENAME TO %I' 
            , v_row.child_schema 
            , v_row.child_table
            , substring(v_row.child_table from 1 for 4)||'_p'||v_min_val::text);

    -- I just have it outputting the ALTER statement for review. If you'd like this code to actually run it, uncomment the EXECUTE below.
    RAISE NOTICE '%', v_sql;
    -- EXECUTE v_sql;
END LOOP;

END
$rename$;

Вы можете видеть, что это создает красивые равномерные имена секций:

NOTICE:  ALTER TABLE tracking.hits_aa RENAME TO hits_p1000
NOTICE:  ALTER TABLE tracking.hits_bb RENAME TO hits_p2000
NOTICE:  ALTER TABLE tracking.hits_cc RENAME TO hits_p3000

F.41.12.6. Шаг 4

Фактическая настройка и смена триггера

Я упоминал в начале, что если у вас идут постоянные записи, практически все, начиная со второго шага и далее, должно быть выполнено в одной транзакции. Даже если вам не нужно беспокоиться о записях, я настоятельно рекомендую выполнять шаги 4a и 4b в одной транзакции, чтобы избежать странных конфликтов триггеров.

F.41.12.7. Шаг 4a

Отмените текущий триггер партицирования после начала транзакции

BEGIN;
DROP TRIGGER myoldtrigger ON tracking.hits;

F.41.12.8. Шаг 4b

Настройте pg_partman для управления вашим набором секций.

SELECT partman.create_parent('tracking.hits', 'start', 'partman', 'weekly');
COMMIT;

Этот единственный вызов функции добавит ваш старый набор секций в конфигурацию pg_partman, создаст новый триггер и, возможно, создаст некоторые новые дочерние таблицы. pg_partman всегда сохраняет минимальное количество предварительно созданных будущих секций (на основе значения premake в конфигурационной таблице или в качестве параметра для функции create_parent()), поэтому, если у вас их еще нет, этот шаг также позаботится об этом. Настройте параметры по мере необходимости и ознакомьтесь с документацией для дополнительных доступных опций. Этот вызов соответствует временному разделу, используемому в примере до сих пор.

\d+ tracking.hits
                                  Table "tracking.hits"
 Column |            Type             | Modifiers | Storage | Stats target | Description 
--------+-----------------------------+-----------+---------+--------------+-------------
 id     | integer                     | not null  | plain   |              | 
 start  | timestamp without time zone | not null  | plain   |              | 
Triggers:
    hits_part_trig BEFORE INSERT ON tracking.hits FOR EACH ROW EXECUTE PROCEDURE tracking.hits_part_trig_func()
Child tables: tracking.hits_p2015w53,
              tracking.hits_p2016w01,
              tracking.hits_p2016w02,
              tracking.hits_p2016w03,
              tracking.hits_p2016w04,
              tracking.hits_p2016w05,
              tracking.hits_p2016w06,
              tracking.hits_p2016w07,
              tracking.hits_p2016w08,
              tracking.hits_p2016w09

Обратите внимание, что я запустил эту функцию create_parent() 6 февраля 2016 года. Это 5-я неделя года. По умолчанию значение premake равно 4, поэтому она создала 4 недели в будущем. И поскольку это было начальное создание, она также создает 4 таблицы в прошлом. Некоторые из этих таблиц уже существовали и, поскольку их шаблон именования совпадал с pg_partman’s, он обработал это нормально.

Этот последний шаг абсолютно одинаков, независимо от типа партицирования или интервала, поэтому, когда вы дойдете до этого момента, выполните COMMIT и вы закончили!

Запланируйте выполнение функции run_maintenance() (либо через cron, либо через BGW), и будущее обслуживание секций будет выполнено за вас. Ознакомьтесь с документацией pg_partman.md для дополнительных параметров настройки.

Если у вас возникли проблемы с этим документом по миграции, пожалуйста, создайте проблему на Github.

F.41.13. Миграция с триггерного партицирования на нативное

Этот документ расскажет о том, как перенести набор секций, используя старый метод триггеров/наследования/ограничений, на набор секций, используя встроенные функции, найденные в PostgreSQL 11+. Обратите внимание, что эти инструкции не охватывают переход на PG10, поскольку некоторые ключевые функции, которые облегчают эту миграцию, еще не были реализованы. Настоятельно рекомендуется перейти на PG11 или выше, если вы хотите перенести существующие наборы секций.

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

Для пользователей pg_partman требуется как минимум версия 4.3.0. Функция show_partition_info() была исправлена в этой версии для предоставления правильных граничных значений, необходимых для более простой миграции.

Мы будем использовать набор секций, сгенерированный триггерным test/test-time-daily.sql pgtap тестом. Вот как выглядит наш набор секций перед миграцией:

keith@keith=# \d+ partman_test.time_taptest_table
                                  Table "partman_test.time_taptest_table"
 Column |           Type           | Collation | Nullable | Default | Storage  | Stats target | Description 
--------+--------------------------+-----------+----------+---------+----------+--------------+-------------
 col1   | integer                  |           | not null |         | plain    |              | 
 col2   | text                     |           |          |         | extended |              | 
 col3   | timestamp with time zone |           | not null | now()   | plain    |              | 
Indexes:
    "time_taptest_table_pkey" PRIMARY KEY, btree (col1)
Triggers:
    time_taptest_table_part_trig BEFORE INSERT ON partman_test.time_taptest_table FOR EACH ROW EXECUTE FUNCTION partman_test.time_taptest_table_part_trig_func()
Child tables: partman_test.time_taptest_table_p2020_01_27,
              partman_test.time_taptest_table_p2020_01_28,
              partman_test.time_taptest_table_p2020_01_29,
              partman_test.time_taptest_table_p2020_01_30,
              partman_test.time_taptest_table_p2020_01_31,
              partman_test.time_taptest_table_p2020_02_01,
              partman_test.time_taptest_table_p2020_02_02,
              partman_test.time_taptest_table_p2020_02_03,
              partman_test.time_taptest_table_p2020_02_04,
              partman_test.time_taptest_table_p2020_02_05,
              partman_test.time_taptest_table_p2020_02_06,
              partman_test.time_taptest_table_p2020_02_07,
              partman_test.time_taptest_table_p2020_02_08,
              partman_test.time_taptest_table_p2020_02_09,
              partman_test.time_taptest_table_p2020_02_10,
              partman_test.time_taptest_table_p2020_02_11,
              partman_test.time_taptest_table_p2020_02_12
Access method: heap

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

UPDATE partman.part_config SET automatic_maintenance = 'off' WHERE parent_table = 'partman_test.time_taptest_table';

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

UPDATE partman.part_config_sub SET sub_automatic_maintenance = 'off' WHERE sub_parent = 'partman_test.time_taptest_table';
UPDATE partman.part_config_sub SET sub_automatic_maintenance = 'off' WHERE sub_parent = 'partman_test.time_taptest_table_p2019_12_08';
[...]

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

Пожалуйста, обратитесь к разделу Child Table Property Inheritance в docs/pg_partman.md, чтобы узнать, какие свойства можно установить на родительскую таблицу, и какие должны управляться через шаблонную таблицу, поскольку они варьируются в разных версиях PG. Сюда входят такие вещи, как индексы, внешние ключи и другие свойства таблицы.

CREATE TABLE partman_test.time_taptest_table_native 
    (col1 int, col2 text default 'stuff', col3 timestamptz NOT NULL DEFAULT now()) 
    PARTITION BY RANGE (col3);

CREATE INDEX ON partman_test.time_taptest_table_native (col3);

Далее проверьте, каковы были права собственности и привилегии на вашей исходной таблице, и убедитесь, что они существуют на новой родительской таблице. Это обеспечит тот же доступ к таблице после миграции. По умолчанию при нативной разбивке привилегии больше не предоставляются на дочерние таблицы для обеспечения прямого доступа к ним. Если вы хотите сохранить это поведение, установите столбец inherit_privileges в part_config (и part_config_sub, если необходимо) в значение true.

\dt partman_test.time_taptest_table
                     List of relations
    Schema    |        Name        | Type  |     Owner     
--------------+--------------------+-------+---------------
 partman_test | time_taptest_table | table | partman_owner
(1 row)

\dp+ partman_test.time_taptest_table
                                               Access privileges
    Schema    |        Name        | Type  |          Access privileges          | Column privileges | Policies 
--------------+--------------------+-------+-------------------------------------+-------------------+----------
 partman_test | time_taptest_table | table | partman_owner=arwdDxt/partman_owner+|                   | 
              |                    |       | partman_basic=arwd/partman_owner   +|                   | 
              |                    |       | testing=r/partman_owner             |                   | 
(1 row)
ALTER TABLE partman_test.time_taptest_table_native OWNER TO partman_owner;
GRANT SELECT, INSERT, UPDATE, DELETE ON partman_test.time_taptest_table_native TO partman_basic;
GRANT SELECT ON partman_test.time_taptest_table_native TO testing;

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

BEGIN;
LOCK TABLE partman_test.time_taptest_table IN ACCESS EXCLUSIVE MODE NOWAIT;

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

Первым важным шагом в этом процессе миграции теперь является отмена наследования всех дочерних таблиц от старого родителя. Вы можете использовать запрос, подобный приведенному ниже, чтобы сгенерировать операторы ALTER TABLE для отмены наследования всех дочерних таблиц от данной родительской таблицы. Лучше всего использовать сгенерированный таким образом SQL, чтобы избежать опечаток, особенно при работе с очень большими наборами секций:

SELECT 'ALTER TABLE '||inhrelid::regclass||' NO INHERIT '||inhparent::regclass||';' FROM pg_inherits WHERE inhparent::regclass = 'partman_test.time_taptest_table'::regclass;

                                              ?column?                                               
-----------------------------------------------------------------------------------------------------
 ALTER TABLE partman_test.time_taptest_table_p2019_12_08 NO INHERIT partman_test.time_taptest_table;
 ALTER TABLE partman_test.time_taptest_table_p2019_12_09 NO INHERIT partman_test.time_taptest_table;
 ALTER TABLE partman_test.time_taptest_table_p2019_12_10 NO INHERIT partman_test.time_taptest_table;
 ALTER TABLE partman_test.time_taptest_table_p2019_12_11 NO INHERIT partman_test.time_taptest_table;
 ALTER TABLE partman_test.time_taptest_table_p2019_12_12 NO INHERIT partman_test.time_taptest_table;
 ALTER TABLE partman_test.time_taptest_table_p2019_12_13 NO INHERIT partman_test.time_taptest_table;
 ALTER TABLE partman_test.time_taptest_table_p2019_12_14 NO INHERIT partman_test.time_taptest_table;
 ALTER TABLE partman_test.time_taptest_table_p2019_12_15 NO INHERIT partman_test.time_taptest_table;
 ALTER TABLE partman_test.time_taptest_table_p2019_12_16 NO INHERIT partman_test.time_taptest_table;
 ALTER TABLE partman_test.time_taptest_table_p2019_12_17 NO INHERIT partman_test.time_taptest_table;
 ALTER TABLE partman_test.time_taptest_table_p2019_12_18 NO INHERIT partman_test.time_taptest_table;
 ALTER TABLE partman_test.time_taptest_table_p2019_12_19 NO INHERIT partman_test.time_taptest_table;
 ALTER TABLE partman_test.time_taptest_table_p2019_12_20 NO INHERIT partman_test.time_taptest_table;
 ALTER TABLE partman_test.time_taptest_table_p2019_12_21 NO INHERIT partman_test.time_taptest_table;
 ALTER TABLE partman_test.time_taptest_table_p2019_12_22 NO INHERIT partman_test.time_taptest_table;
 ALTER TABLE partman_test.time_taptest_table_p2019_12_23 NO INHERIT partman_test.time_taptest_table;
 ALTER TABLE partman_test.time_taptest_table_p2019_12_24 NO INHERIT partman_test.time_taptest_table;

НЕ ЗАПУСКАЙТЕ ЭТИ УТВЕРЖДЕНИЯ ПОКА. Следующий запрос не будет работать, если дочерние таблицы больше не являются частью набора наследования.

Для любых наборов секций, даже тех, которые не управляются pg_partman, следующим шагом является определение граничных значений ваших существующих дочерних таблиц и передача этих значений команде ATTACH PARTITION, используемой в нативном разделении. Поскольку pg_partman использует установленные шаблоны именования для всех типов секций, которыми он управляет, есть встроенная функция (show_partition_info()), которая может возвращать граничные значения на основе имени дочерней таблицы.

Для наборов секций, не относящихся к pg_partman, вам придется использовать какой-то другой метод для определения этих дочерних границ.

Снова мы можем использовать некоторые sql-выражения для генерации операторов, чтобы повторно присоединить дочерние элементы к новому родителю. Как упоминалось ранее, show_partition_info() облегчает это для наборов секций, управляемых pg_partman:

SELECT (
    SELECT 'ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION '||x.child_tablename||' FOR VALUES FROM ('||quote_literal(y.child_start_time)||') TO ('||quote_literal(y.child_end_time)||');' 
    FROM partman.show_partition_info(x.child_tablename, p_parent_table := 'partman_test.time_taptest_table') y ) 
FROM (SELECT inhrelid::regclass::text AS child_tablename FROM pg_inherits WHERE inhparent::regclass = 'partman_test.time_taptest_table'::regclass) x;


 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_01_27 FOR VALUES FROM ('2020-01-27 00:00:00-05') TO ('2020-01-28 00:00:00-05');
 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_01_28 FOR VALUES FROM ('2020-01-28 00:00:00-05') TO ('2020-01-29 00:00:00-05');
 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_01_29 FOR VALUES FROM ('2020-01-29 00:00:00-05') TO ('2020-01-30 00:00:00-05');
 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_01_30 FOR VALUES FROM ('2020-01-30 00:00:00-05') TO ('2020-01-31 00:00:00-05');
 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_01_31 FOR VALUES FROM ('2020-01-31 00:00:00-05') TO ('2020-02-01 00:00:00-05');
 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_02_01 FOR VALUES FROM ('2020-02-01 00:00:00-05') TO ('2020-02-02 00:00:00-05');
 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_02_02 FOR VALUES FROM ('2020-02-02 00:00:00-05') TO ('2020-02-03 00:00:00-05');
 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_02_03 FOR VALUES FROM ('2020-02-03 00:00:00-05') TO ('2020-02-04 00:00:00-05');
 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_02_04 FOR VALUES FROM ('2020-02-04 00:00:00-05') TO ('2020-02-05 00:00:00-05');
 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_02_05 FOR VALUES FROM ('2020-02-05 00:00:00-05') TO ('2020-02-06 00:00:00-05');
 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_02_06 FOR VALUES FROM ('2020-02-06 00:00:00-05') TO ('2020-02-07 00:00:00-05');
 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_02_07 FOR VALUES FROM ('2020-02-07 00:00:00-05') TO ('2020-02-08 00:00:00-05');
 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_02_08 FOR VALUES FROM ('2020-02-08 00:00:00-05') TO ('2020-02-09 00:00:00-05');
 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_02_09 FOR VALUES FROM ('2020-02-09 00:00:00-05') TO ('2020-02-10 00:00:00-05');
 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_02_10 FOR VALUES FROM ('2020-02-10 00:00:00-05') TO ('2020-02-11 00:00:00-05');
 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_02_11 FOR VALUES FROM ('2020-02-11 00:00:00-05') TO ('2020-02-12 00:00:00-05');
 ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2020_02_12 FOR VALUES FROM ('2020-02-12 00:00:00-05') TO ('2020-02-13 00:00:00-05');

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

keith@keith=# \d+ partman_test.time_taptest_table
                                  Table "partman_test.time_taptest_table"
 Column |           Type           | Collation | Nullable | Default | Storage  | Stats target | Description 
--------+--------------------------+-----------+----------+---------+----------+--------------+-------------
 col1   | integer                  |           | not null |         | plain    |              | 
 col2   | text                     |           |          |         | extended |              | 
 col3   | timestamp with time zone |           | not null | now()   | plain    |              | 
Indexes:
    "time_taptest_table_pkey" PRIMARY KEY, btree (col1)
Triggers:
    time_taptest_table_part_trig BEFORE INSERT ON partman_test.time_taptest_table FOR EACH ROW EXECUTE FUNCTION partman_test.time_taptest_table_part_trig_func()
Access method: heap

И наш новый нативной родитель теперь должен был принять всех своих новых детей:

keith@keith=# \d+ partman_test.time_taptest_table_native
                            Partitioned table "partman_test.time_taptest_table_native"
 Column |           Type           | Collation | Nullable |    Default    | Storage  | Stats target | Description 
--------+--------------------------+-----------+----------+---------------+----------+--------------+-------------
 col1   | integer                  |           |          |               | plain    |              | 
 col2   | text                     |           |          | 'stuff'::text | extended |              | 
 col3   | timestamp with time zone |           | not null | now()         | plain    |              | 
Partition key: RANGE (col3)
Indexes:
    "time_taptest_table_native_col3_idx" btree (col3)
Partitions: partman_test.time_taptest_table_p2020_01_27 FOR VALUES FROM ('2020-01-27 00:00:00-05') TO ('2020-01-28 00:00:00-05'),
            partman_test.time_taptest_table_p2020_01_28 FOR VALUES FROM ('2020-01-28 00:00:00-05') TO ('2020-01-29 00:00:00-05'),
            partman_test.time_taptest_table_p2020_01_29 FOR VALUES FROM ('2020-01-29 00:00:00-05') TO ('2020-01-30 00:00:00-05'),
            partman_test.time_taptest_table_p2020_01_30 FOR VALUES FROM ('2020-01-30 00:00:00-05') TO ('2020-01-31 00:00:00-05'),
            partman_test.time_taptest_table_p2020_01_31 FOR VALUES FROM ('2020-01-31 00:00:00-05') TO ('2020-02-01 00:00:00-05'),
            partman_test.time_taptest_table_p2020_02_01 FOR VALUES FROM ('2020-02-01 00:00:00-05') TO ('2020-02-02 00:00:00-05'),
            partman_test.time_taptest_table_p2020_02_02 FOR VALUES FROM ('2020-02-02 00:00:00-05') TO ('2020-02-03 00:00:00-05'),
            partman_test.time_taptest_table_p2020_02_03 FOR VALUES FROM ('2020-02-03 00:00:00-05') TO ('2020-02-04 00:00:00-05'),
            partman_test.time_taptest_table_p2020_02_04 FOR VALUES FROM ('2020-02-04 00:00:00-05') TO ('2020-02-05 00:00:00-05'),
            partman_test.time_taptest_table_p2020_02_05 FOR VALUES FROM ('2020-02-05 00:00:00-05') TO ('2020-02-06 00:00:00-05'),
            partman_test.time_taptest_table_p2020_02_06 FOR VALUES FROM ('2020-02-06 00:00:00-05') TO ('2020-02-07 00:00:00-05'),
            partman_test.time_taptest_table_p2020_02_07 FOR VALUES FROM ('2020-02-07 00:00:00-05') TO ('2020-02-08 00:00:00-05'),
            partman_test.time_taptest_table_p2020_02_08 FOR VALUES FROM ('2020-02-08 00:00:00-05') TO ('2020-02-09 00:00:00-05'),
            partman_test.time_taptest_table_p2020_02_09 FOR VALUES FROM ('2020-02-09 00:00:00-05') TO ('2020-02-10 00:00:00-05'),
            partman_test.time_taptest_table_p2020_02_10 FOR VALUES FROM ('2020-02-10 00:00:00-05') TO ('2020-02-11 00:00:00-05'),
            partman_test.time_taptest_table_p2020_02_11 FOR VALUES FROM ('2020-02-11 00:00:00-05') TO ('2020-02-12 00:00:00-05'),
            partman_test.time_taptest_table_p2020_02_12 FOR VALUES FROM ('2020-02-12 00:00:00-05') TO ('2020-02-13 00:00:00-05')

Далее следует поменять местами имена вашего старого родителя на основе триггера и нового родительского элемента.

ALTER TABLE partman_test.time_taptest_table RENAME TO time_taptest_table_old;
ALTER TABLE partman_test.time_taptest_table_native RENAME TO time_taptest_table;

PG11+ поддерживает функцию секции по умолчанию для перехвата любых данных, которые не имеют соответствующего дочернего элемента. pg_partman создает этот раздел по умолчанию для вас при настройке новых наборов секций, но поскольку мы проводим миграцию, нам придется создать его вручную, если это необходимо. Если названия ваших таблиц особенно длинные, убедитесь, что добавление суффикса _default не обрезается неожиданно. Суффикс не требуется для функциональности, но дает хорошее представление о назначении таблицы, поэтому лучше сократить само название таблицы, чтобы поместить суффикс.

CREATE TABLE partman_test.time_taptest_table_default (LIKE partman_test.time_taptest_table INCLUDING ALL);
ALTER TABLE partman_test.time_taptest_table ATTACH PARTITION partman_test.time_taptest_table_default DEFAULT;

Для pg_partman используется шаблонная таблица для обработки определенных свойств наследования, поэтому эту таблицу нужно создать. Эта таблица может быть расположена в любой схеме и иметь любое имя, но по умолчанию она создается в той же схеме, в которую был установлен pg_partman, и просто добавляет template_ к имени текущей таблицы, включая ее схему. Также хорошо установить владельца таким же, как у родительской таблицы. Опять же, если имена ваших таблиц особенно длинные, убедитесь, что вы учли любое усечение имени, если оно происходит.

Как упоминалось выше, просмотрите раздел документации Child Table Property Inheritance для того, чтобы узнать, какие свойства управляются через этот шаблон таблицы в зависимости от вашей версии Tantor SE.

CREATE TABLE partman.partman_test_time_taptest_table (LIKE partman_test.time_taptest_table);
ALTER TABLE partman.partman_test_time_taptest_table OWNER TO partman_owner;

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

ALTER TABLE partman.partman_test_time_taptest_table ADD PRIMARY KEY (col1);

И, наконец, для pg_partman, вам потребуется обновить part_configpart_config_sub, если используются подсекции) таблицы, чтобы учесть теперь нативное разделение. Значение для template_table должно соответствовать имени таблицы, которая была создана выше.

UPDATE partman.part_config SET partition_type = 'native', template_table = 'partman.partman_test_time_taptest_table' WHERE parent_table = 'partman_test.time_taptest_table';

Если вы запустили этот процесс внутри транзакции, обязательно подтвердите свою работу сейчас:

COMMIT;

Это должно завершить процесс миграции. Если вы хотите, чтобы общие вызовы run_maintenance() с pg_partman снова работали с этим набором секций, обязательно обновите таблицу part_config, чтобы вернуть automatic_maintenance в состояние on.

F.41.14. Добавить отсутствующие процедуры в обновленный экземпляр Tantor SE

Если pg_partman был установлен на вашем экземпляре базы данных до того, как он был обновлен до PostgreSQL 11 или более поздней версии, то, вероятно, в нем отсутствуют некоторые или все новые PROCEDURE, которые были добавлены со временем. Возможно, некоторые из них есть, если вы обновили pg_partman до более поздней версии, но при этом были установлены только те PROCEDURE, которые случайно были частью этого обновления. Возможно, некоторые из них все еще отсутствуют.

Лучший способ исправить это и гарантировать, что все PROCEDURE были установлены, - это удалить и заново создать расширение, когда вы находитесь на PG11 или более поздней версии. Рекомендуется протестировать приведенные ниже шаги в разработке/тестировании перед запуском на любых производственных системах, чтобы вы были уверены, что результат работает как ожидается.

ВАЖНЫЕ ЗАМЕЧАНИЯ: 1. Если вы установили pg_partman впервые на PG11 или позже, вам НЕ НУЖНО выполнять какие-либо шаги в этом руководстве. 2. Поскольку вся расширение будет удалено и создано заново, вы потеряете все привилегии, которые были предоставлены для любых объектов расширения, и могут быть восстановлены отозванные привилегии по умолчанию. Пожалуйста, запишите пользователей, которые ранее управляли обслуживанием секций, и убедитесь, что их привилегии восстановлены. 3. Если вы все еще используете партицирование на основе триггеров, вам придется прервать работу всех таблиц на основе триггеров, поскольку объекты, которые используют триггеры, будут удалены и восстановлены. Настоятельно рекомендуется перейти от партицирования на основе триггеров, если это возможно. Это связано как с проблемами производительности, так и с подготовкой к будущему, поскольку партицирование на основе триггеров может быть упразднено в будущей версии. 4. Та же версия pg_partman, которая была удалена, ДОЛЖНА быть переустановлена для восстановления конфигурации. Рекомендуется установить последнюю доступную версию перед началом этого обновления.

F.41.15. Шаги Обновления

  1. Выполните pg_dump данных из таблиц конфигурации pg_partman. Обратите внимание, что содержимое этого дампа будет содержать только данные, а не определения таблиц. Определения являются частью шага CREATE EXTENSION. Это просто создание дампа в текстовом формате, чтобы облегчить просмотр содержимого, если это необходимо. Обратите внимание, что следующая команда предполагает что pg_partman был установлен в схеме partman.

pg_dump -d mydbname -Fp -a -f partman_update_procedures.sql -t partman.part_config -t partman.part_config_sub
  1. Удалите расширение pg_partman. Если оно было установлено в определенной схеме, обратите внимание на это и переустановите его в эту же схему

\dx pg_partman
                             List of installed extensions
    Name    | Version | Schema  |                     Description                      
------------+---------+---------+------------------------------------------------------
 pg_partman | 4.7.0   | partman | Extension to manage partitioned tables by time or ID
DROP EXTENSION pg_partman;
  1. Переустановите pg_partman в ту же схему

CREATE EXTENSION pg_partman SCHEMA partman;
  1. Перезагрузите данные обратно в таблицы конфигурации расширения

psql -d mydbname -i partman_update_procedures.sql
  1. Восстановите привилегии для объектов pg_partman, если это необходимо

Теперь у вас должны быть доступны все отсутствующие PROCEDURE, а также ваша исходная конфигурация pg_partman.