F.54. pg_partman — создание и управление наборами секционирования таблиц на основе времени и количества#
F.54. pg_partman — создание и управление наборами секционирования таблиц на основе времени и количества #
- F.54.1. Обзор
- F.54.2. Установка
- F.54.3. Обновление
- F.54.4. Примеры
- F.54.5. Расширение для управления секциями PostgreSQL
(
pg_partman) - F.54.6. Пример руководства по настройке нативного секционирования
- F.54.7. Миграция существующего набора секций в PG Partition Manager
- F.54.8. Миграция с триггерного разбиения на нативное декларативное разбиение
- F.54.9. Руководство по обновлению до pg_partman 5.0.1
Версия: 5.4.1
F.54.1. Обзор #
pg_partman является расширением для создания и управления наборами таблиц с секционированием по времени и числам. Начиная с версии 5.0.1, поддерживается только встроенное декларативное разделение, а старые методы на основе триггеров устарели.
Встроенное декларативное секционирование в Tantor SE предоставляет команды для создания секционированной таблицы и ее дочерних элементов. pg_partman использует встроенные декларативные функции, которые предоставляет Tantor SE, и дополняет их дополнительными функциями и улучшениями, чтобы упростить управление секциями. Один из ключевых способов, которым pg_partman расширяет секционирование в Postgres, заключается в предоставлении средств для автоматизации обслуживания дочерних таблиц со временем (например, добавление новых дочерних таблиц, удаление старых на основе политики хранения). pg_partman также имеет функции для преобразования существующей таблицы в секционированную таблицу или наоборот.
В большинстве случаев фоновый рабочий процесс (BGW) автоматически выполняется для обслуживания секций без необходимости внешнего планировщика (cron и т.д.).
F.54.2. Установка #
Рекомендуется:
pg_jobmon (>=v1.4.0). PG Job Monitor будет автоматически использоваться, если он установлен и настроен должным образом.
Фоновый рабочий процесс должен быть загружен при запуске базы данных путем добавления
библиотеки в 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;
pg_partman не требует прав суперпользователя для работы, но в настоящее время
все еще требует их для установки. Если не используется суперпользователь,
рекомендуется создать выделенную роль для выполнения
функций pg_partman и быть владельцем всех наборов секций,
которые поддерживает pg_partman. Как минимум, эта роль должна иметь
следующие привилегии (предполагая, что pg_partman установлен в схему
partman и что выделенная роль называется
partman_user):
CREATE ROLE partman_user WITH LOGIN; GRANT ALL ON SCHEMA partman TO partman_user; GRANT ALL ON ALL TABLES IN SCHEMA partman TO partman_user; GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA partman TO partman_user; GRANT EXECUTE ON ALL PROCEDURES IN SCHEMA partman TO partman_user; GRANT ALL ON SCHEMA my_partition_schema TO partman_user; GRANT TEMPORARY ON DATABASE mydb to partman_user; -- allow creation of temp tables to move data out of default
Если вам нужно, чтобы роль также могла создавать схемы, вам потребуется предоставить права на создание в базе данных. В общем, это не должно требоваться, если вы предоставите указанной роли права на создание в любых существующих схемах, которые будут содержать наборы секций.
GRANT CREATE ON DATABASE mydb TO partman_user;
F.54.3. Обновление #
Обновите соответствующие пакеты, чтобы разместить новые файлы скриптов и библиотеки. Затем выполните следующее непосредственно в Tantor SE:
ALTER EXTENSION pg_partman UPDATE TO '<latest version>';
Если вы выполняете
pg_dump/pg_restore и
вы обновили pg_partman на месте с предыдущих версий, рекомендуется использовать опцию --column-inserts
при дампе и/или восстановлении конфигурационных таблиц pg_partman.
Это связано с тем, что порядок конфигурационных столбцов может
быть разным (обновления просто добавляют столбцы в конец,
тогда как по умолчанию при новой установке он может быть другим).
Если вы обновляетесь между любыми основными версиями pg_partman (4.x -> 5.x и т.д.), пожалуйста, внимательно прочитайте все промежуточные заметки о версиях в CHANGELOG, особенно те, которые касаются основной версии. Часто есть дополнительные инструкции и другие важные соображения для обновлений. Особые соображения необходимы, если вы обновляетесь до версии 5+ с любой версии ниже 5.0.0. Пожалуйста, смотрите Раздел F.54.9.
Предупреждение
Некоторые обновления pg_partman должны удалить и воссоздать собственные объекты базы данных. Если вы отзываете привилегии PUBLIC у функций/процедур, они могут быть добавлены обратно к объектам, которые воссоздаются в рамках обновления. Если желательны ограничения на использование PUBLIC для pg_partman, рекомендуется установить его в собственную схему, как показано выше, и отозвать нежелательный доступ к этой схеме. В противном случае вам, возможно, придется добавить дополнительный шаг к процедурам обновления расширения, чтобы снова отозвать доступ PUBLIC.
F.54.4. Примеры #
Для настройки секционирования с помощью pg_partman на совершенно новой таблице или для миграции существующей обычной таблицы на секционированную, см. главу Раздел F.54.6.
Для миграции таблицы с секционированием на основе триггеров к декларативному секционированию с использованием pg_partman, см. Раздел F.54.8. Обратите внимание, что если вы планируете миграцию на pg_partman, вам сначала нужно будет мигрировать на декларативную таблицу с секционированием, прежде чем она сможет управляться pg_partman.
См. Раздел F.54.5 для получения полной информации обо всех командах и параметрах pg_partman.
F.54.5. Расширение для управления секциями PostgreSQL
(pg_partman) #
F.54.5.1. О продукте #
PostgreSQL Partition Manager - это расширение, которое помогает упростить управление секционированием таблиц на основе времени или чисел/идентификаторов. Оно имеет множество опций, но обычно требуется только несколько из них, поэтому его гораздо проще использовать, чем может показаться на первый взгляд (и определенно проще, чем реализовывать это самостоятельно).
Начиная с версии 5.0.1, минимально требуемая версия PostgreSQL — 14, и секционирование на основе триггеров больше не поддерживается. Всё секционирование выполняется с использованием встроенного декларативного секционирования. Диапазонное секционирование поддерживается для интервалов, основанных на времени и числах. Секционирование по списку поддерживается для числового секционирования, когда интервал равен 1. Начиная с версии 5.1, ДОПОЛНИТЕЛЬНО доступна ЭКСПЕРИМЕНТАЛЬНАЯ поддержка числовых/десятичных значений для числового секционирования, однако интервал всё равно должен быть целым числом. Обратная связь по числовому секционированию, как положительная, так и отрицательная, приветствуется для ускорения выхода из экспериментального режима.
Версия 4.x pg_partman, которая всё ещё поддерживает разделение на основе триггеров, больше не находится в активной разработке и будет получать только критические исправления ошибок до тех пор, пока PostgreSQL 14 не станет последней поддерживаемой версией. Если секционирование является важной частью вашей инфраструктуры, пожалуйста, планируйте обновление в самом ближайшем будущем.
Автоматически создается секция по умолчанию для обработки данных, выходящих за пределы существующих дочерних границ, для всех наборов секций. Функция check_default() обеспечивает мониторинг любых данных, вставляемых в таблицу по умолчанию, и набор функций partition_data_* может легко разделить эти данные для вас, если это допустимые данные. Это гораздо проще, чем автоматически создавать новые дочерние таблицы по требованию и очищать потенциально сотни или тысячи ненужных секций. И также лучше, чем выдавать ошибку и терять данные!
Обратите внимание, что создание будущих дочерних таблиц основано на данных,
которые в настоящее время находятся в наборе секций и, по умолчанию, игнорирует данные в
секции по умолчанию. Рекомендуется установить значение
premake достаточно высоким, чтобы охватить ожидаемый диапазон данных, которые будут вставлены. См. ниже для дальнейших
объяснений этих значений конфигурации.
Если у вас есть существующий набор секций, и вы хотите перенести его в pg_partman, пожалуйста, смотрите Раздел F.54.5.
F.54.5.1.1. Наследование свойств дочерней таблицы #
Для этого расширения большинство атрибутов дочерних секций получены от родительской таблицы. С декларативным разбиением, некоторые функции не могут быть унаследованы от родителя в зависимости от версии Tantor SE. Поэтому pg_partman использует таблицу-шаблон вместо этого. Следующая матрица таблицы показывает, как определенные наследования свойств управляются с помощью pg_partman. Указанное число - это версия Tantor SE. Если свойство не указано здесь, то предполагается, что оно управляется через родителя.
| Функция | Наследование родителя | Наследование шаблона |
|---|---|---|
| несекционный столбец первичного ключа | 14+ | |
| уникальный индекс для несекционного столбца | 14+ | |
| несекционный столбец уникального индекса табличного пространства таблиц | 14+ | |
| параметры, специфичные для отношения (autovac и др.) | 14+ | |
| незарегистрированная таблица state* | 14+ | |
| неуникальные индексы | 14+ | |
| привилегии/владение | 14+ | |
| СТОЛБЕЦ GENERATED ALWAYS AS IDENTITY | 17+ |
Если свойство управляется через шаблонную таблицу, оно, скорее всего, не будет автоматически применяться ко всем существующим дочерним таблицам при изменении этого свойства. Оно будет применяться к любым новым созданным дочерним таблицам, но должно быть применено вручную к любым существующим дочерним таблицам.
Привилегии и владение НЕ наследуются по умолчанию. Если
включено pg_partman, обратите внимание, что это наследование происходит только при
создании дочерней таблицы и не применяется автоматически ретроактивно при
изменении (см. reapply_privileges()). Если вам не нужен прямой доступ к дочерним таблицам, это не должно быть
необходимо. Вы можете установить опцию inherit_privileges, если это необходимо (см. информацию о таблице конфигурации ниже).
Если вы используете функцию IDENTITY для последовательностей, автоматическая генерация новых значений последовательности с использованием этой функции поддерживается только при вставке данных через родительскую таблицу, а не напрямую в дочерние таблицы.
Предостережение
Функция шаблонной таблицы является лишь временным решением для ускорения принятия декларативного разбиения. По мере того как эти задачи будут лучше решаться в ядре, использование шаблонной таблицы будет быстро прекращено в pg_partman. Если функция, управляемая шаблоном, будет поддерживаться в ядре в будущем, она в конечном итоге будет удалена из управления шаблонами в pg_partman, поэтому, пожалуйста, планируйте это при обновлении до основной версии, если это относится к вам.
Если вам нужно использовать свойство REPLICA IDENTITY для логической репликации с предложением USING INDEX, обратите внимание, что это поддерживается только в том случае, если необходимый индекс был создан непосредственно на родительской таблице, а НЕ на шаблоне. Поскольку нет способа ограничить создание REPLICA IDENTITY только на родительской таблице или только на шаблоне, невозможно определить, какая из них является "правильной" идентичностью. Поэтому в качестве источника была выбрана только родительская таблица, чтобы обеспечить согласованность с другими методами наследования идентичности (FULL и NONE).
Статус UNLOGGED управляется через шаблон pg_partman, поскольку начиная с PostgreSQL 18 больше невозможно установить флаг unlogged для родительской таблицы набора секций. До версии 18 это не обрабатывалось последовательно. При обновлении до PG18 флаг unlogged будет удалён из любой родительской таблицы в наборе секций, но его всё ещё можно установить для дочерних таблиц самого низкого уровня.
F.54.5.1.2. Часовые пояса #
Важно убедиться, что часовые пояса всех систем, на которых будут выполняться операции по обслуживанию pg_partman, согласованы, особенно при выполнении секционирования по времени. Вызовы функций pg_partman будут использовать часовой пояс, установленный клиентом на момент вызова функций. Это согласуется с общим принципом работы клиентов libpq.
Настоятельно рекомендуется всегда запускать вашу систему баз данных во времени UTC. Это значительно упрощает обработку любых вопросов, связанных со временем, и особенно помогает избежать проблем, которые невозможно решить из-за перехода на летнее время (DST). Кроме того, убедитесь, что клиент, который будет создавать наборы секций и выполнять задачи обслуживания, также настроен на UTC. Например, попытка секционирования по часам приведет к сбоям при смене времени или к пропуску создания дочерней таблицы. Кроме того, убедитесь, что клиент, который будет создавать наборы секций и выполнять задачи обслуживания, также настроен на UTC.
F.54.5.1.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) напрямую и достаточно часто, чтобы
создавать будущие секции. Вы можете использовать процедуру run_maintenance_proc() вместо базовой
функции, чтобы уменьшить проблемы с конкуренцией, так как она
автоматически фиксирует после обслуживания каждого набора секций.
ПУБЛИКАЦИЯ/ПОДПИСКА для логической репликации НЕ поддерживается с субпартиционированием.
См. функции create_sub_partition() и
run_maintenance() ниже для получения
дополнительной информации.
F.54.5.1.4. Удержание #
Если вам не нужно сохранять данные в старых секциях, доступна система хранения, которая автоматически удаляет ненужные дочерние секции. По умолчанию они только теряют наследование/отсоединяются, но не удаляются полностью, однако это можно настроить при необходимости. Также доступен метод для выгрузки таблиц, если их больше не нужно хранить в базе данных, но требуется сохранить.
Чтобы установить политику хранения, введите либо интервал, либо целое значение в столбец retention таблицы part_config. Для секционирования по времени значение интервала определяет, что любые секции, содержащие только данные старше этого интервала, будут удалены (включая безопасную обработку случаев, когда интервал хранения не является кратным размеру секции). Это значение должно соответствовать типу данных interval в PostgreSQL. Обратите внимание, что без указания единицы измерения числовое значение будет преобразовано в секунды. Поэтому ваше значение должно быть, например, 1 day, 1 month, 2 years и т.д.
Для разделения по идентификатору, целочисленное значение задаёт, что любые секции с значением id меньше текущего максимального значения id минус значение хранения будут удалены. Например, если текущий максимальный id равен 100, а значение хранения равно 30, то все секции с id меньше 70 будут удалены. Всегда используется текущее максимальное значение id на момент выполнения функции удаления.
Имейте в виду, что для наборов подпартиций, когда у родительской таблицы удаляется дочерняя таблица, если эта дочерняя таблица также разбита на партиции, удаление происходит с использованием CASCADE, и ВСЕ дочерние таблицы по всей иерархии наследования будут удалены. Также обратите внимание, что набор партиций, управляемый pg_partman, всегда должен содержать как минимум одну дочернюю таблицу, поэтому политика хранения никогда не удалит последнюю дочернюю таблицу в наборе.
F.54.5.1.5. Исключение по ограничению #
Одним из больших преимуществ секционирования является функция, называемая исключение по ограничению (см. документацию для объяснения функциональности и примеров Раздел 5.12.5). Однако проблема большинства схем секционирования заключается в том, что это будет использоваться только для управляющего столбца секционирования. Если вы используете условие WHERE по любому другому столбцу в наборе секций, будет происходить сканирование всех дочерних таблиц, если только для этих столбцов также не заданы ограничения. А предсказать, какими будут значения столбца, чтобы заранее создать ограничения, может быть очень сложно или невозможно. pg_partman имеет функцию применения ограничений к старым таблицам в наборе секций, в которые больше не вносятся изменения (“старыми” считаются те, что старше значения конфигурации optimize_constraint). Она проверяет текущие минимальные и максимальные значения в указанных столбцах и затем применяет ограничение к этой дочерней таблице. Это может позволить функции исключения по ограничению потенциально исключить сканирование старых дочерних таблиц, когда в условиях WHERE используются другие столбцы. Имейте в виду, что это ограничивает возможность редактирования этих столбцов, но в ситуациях, где это применимо, может значительно повысить производительность запросов для очень больших наборов секций. Поэтому, если вы только вставляете новые данные, это может быть очень полезно, но если данные регулярно вставляются/обновляются во всём наборе секций, польза будет ограниченной. Также доступны функции для простого воссоздания ограничений, если данные всё же потребуется редактировать в этих старых секциях. Обратите внимание, что ограничения, управляемые PG Partman, НЕ ДОЛЖНЫ переименовываться, чтобы расширение могло корректно управлять ими для вас.
Добавление этих ограничений может потенциально вызвать конфликт с данными, содержащимися в этих таблицах, а также сделать обслуживание pg_partman длительным. В таблице part_config(_sub) есть столбец “constraint_valid”, чтобы установить, должны ли эти ограничения быть установлены как NOT VALID при создании. Хотя это может сделать создание ограничения(ий) почти мгновенным, исключение ограничений не может быть использовано до тех пор, пока оно не будет проверено. Вот почему ограничения по умолчанию добавляются как действительные.
NOTE: Это может не работать с субпартиционированием. Это будет работать на первом уровне партиционирования, но не гарантируется правильная работа на дальнейших наборах субпартиций в зависимости от комбинаций интервалов и значения optimize_constraint. Например: Еженедельно -> Ежедневно с ежедневным optimize_constraint, равным 7, не будет работать как ожидалось. Еженедельные ограничения будут созданы, но ежедневные субпартиционные, вероятно, не будут.
F.54.5.1.6. Соображения по интервалу времени #
Самый маленький поддерживаемый временной интервал составляет 1 секунду, а верхний предел ограничен минимальными и максимальными значениями временных меток, которые поддерживает Tantor SE Раздел 8.5.
Когда create_partition() запускается впервые для создания набора секций, интервалы меньше одного дня округляются вниз при определении, какая первая секция будет создана. Интервалы меньше 24 часов, но больше 1 минуты используют ближайший час, округленный вниз. Интервалы меньше 1 минуты используют ближайшую минуту, округленную вниз. Однако будет создано достаточно секций, чтобы поддерживать актуальное текущее время. Это означает, что при запуске create_partition() может быть создано больше предыдущих секций, чем ожидалось, и не все будущие секции могут быть созданы. Первый запуск run_maintenance() исправит отсутствие будущих секций. Это происходит из-за необходимости поддержки пользовательских временных интервалов. Любые интервалы, равные или превышающие 24 часа, должны настраиваться так, как ожидается.
Имейте в виду, что для интервалов, равных или превышающих 100 лет, расширение будет использовать реальное начало века или тысячелетия для определения имени секции и правил ограничения. Например, 21-й век и 3-е тысячелетие начались 1 января 2001 года (а не 2000). Это также означает, что года “0” не существует.
Для еженедельных секций обратите внимание, что значение по умолчанию “начала” недели будет определяться днем недели, в который вы вызываете create_partition(). Например, если вы запустите её во вторник или пятницу, границы времени дочерних таблиц будут начинаться соответственно с этих дней, а не с ожидаемого понедельника или воскресенья. Самый простой способ решить эту задачу — использовать функцию date_trunc(), чтобы недели начинались с понедельника, передав соответствующее значение через параметр p_start_partition функции create_partition(). Начинать недели с воскресенья, вероятно, тоже возможно, но это сложнее и выходит за рамки данной документации.
SELECT partman.create_partition('public.time_table', 'col3', '1 week', p_start_partition := to_char(date_trunc('week',CURRENT_TIMESTAMP), 'YYYY-MM-DD HH24:MI:SS'));
F.54.5.1.7. Ограничения на длину имен #
Tantor SE имеет ограничение на длину имен объектов в 63 байта (НЕ символов). Если вы попытаетесь создать объект с более длинным именем, оно будет усечено до этого предела. Это может вызвать очевидные проблемы с именами секций, которые зависят от конкретного суффикса. PG Partman автоматически обрабатывает это для всех имен дочерних таблиц. Он обрежет существующее имя родительской таблицы, чтобы соответствовать требуемому суффиксу. Имейте в виду, что если у вас есть таблицы с очень длинными, похожими именами, можно столкнуться с конфликтами имен, если они являются частью отдельных наборов секций. При секционировании на основе чисел, имейте в виду, что со временем имя таблицы будет все больше и больше усечься, чтобы соответствовать более длинному суффиксу секции. Поэтому, хотя расширение и пытается справиться с этим краевым случаем за вас, рекомендуется держать имена таблиц, которые будут секционированы, как можно короче.
F.54.5.1.8. Ограничения уникальности #
Наследование таблиц в PostgreSQL не позволяет создавать первичный ключ или уникальный индекс/ограничение на родительской таблице, если он не содержит столбец ключа партиционирования. pg_partman позволяет это для любого столбца, применяя ограничение к шаблонной таблице. Однако ограничение применяется к каждой отдельной таблице, а не ко всему набору партиций в целом. Например, это означает, что неосторожное использование может привести к дублированию значения первичного ключа в наборе партиций. В то же время, вместе с pg_partman включён python-скрипт, который может осуществлять мониторинг, чтобы отсутствие этой функции не привело к долгосрочным проблемам. См. check_unique_constraint.py в разделе Scripts.
F.54.5.2. Фоновые рабочие процессы #
BGW расширения pg_partman по сути является просто планировщиком, который запускает процедуру run_maintenance_proc() за вас, чтобы вам не приходилось использовать внешний планировщик (cron и т.д.). В настоящее время он не делает ничего отличного от прямого вызова run_maintenance_proc(), но это может измениться в будущем. Смотрите главу Раздел F.54.2 для инструкций по установке. Если вам нужно вызвать run_maintenance() напрямую для каких-либо конкретных наборов секций, это всё равно придётся делать вручную с помощью внешнего планировщика. Этот механизм обслуживает только те наборы секций, у которых параметр automatic_maintenance в **part_config** установлен в значение true. Сообщения LOG выводятся в обычный файл журнала Tantor SE для указания времени запуска BGW. Дополнительные сообщения журналирования доступны, если параметр log_min_messages установлен в “DEBUG1”.
Предостережение
В ваших библиотеках shared_preload_libraries должна находиться pg_partman_bgw (требуется перезагрузка).
Следующие параметры конфигурации доступны для добавления в файл postgresql.conf для управления процессом BGW:
pg_partman_bgw.dbnameОбязательно. База данных (или базы данных), на которых будет выполняться обслуживание. Если их несколько, используйте список, разделённый запятыми. Если не указано, BGW ничего делать не будет.
pg_partman_bgw.intervalКоличество секунд между вызовами обслуживания. По умолчанию — 3600 (1 час).
См. дополнительную документацию ниже о рекомендуемых значениях для этого на основе типов секций и используемых интервалов.
pg_partman_bgw.roleРоль, от имени которой будет выполняться обслуживание. По умолчанию — “postgres”. Разрешено указывать только одно имя роли.
pg_partman_bgw.analyzeТа же цель, что и у аргумента p_analyze для
run_maintenance(). См. ниже для более подробной информации. Установите значение ‘on’ для TRUE. Установите значение ‘off’ для FALSE (По умолчанию ‘off’).
pg_partman_bgw.jobmonТа же цель, что и у аргумента p_jobmon в
run_maintenance(). См. ниже для более подробной информации. Установите значение ‘on’ для TRUE. Установите значение ‘off’ для FALSE. По умолчанию установлено значение ‘on’.
Если по какой-либо причине основной процесс фонового рабочего процесса завершается аварийно, он будет пытаться перезапуститься каждые 10 минут. Проверьте журналы postgres на наличие проблем, если фоновый рабочий процесс не запускается.
F.54.5.3. Расширяемые объекты #
Требование суперпользователя для использования pg_partman является полностью необязательным. Для работы без суперпользователя, роль(и), которые выполняют функции и обслуживание pg_partman, должны владеть всеми наборами секций, которые они управляют, и иметь разрешения на создание объектов в любой схеме, которая будет содержать наборы секций, которыми он управляет. Для удобства использования и управления привилегиями рекомендуется создать роль, посвященную управлению секциями. Пожалуйста, смотрите главу Раздел F.54.2 для инструкций по настройке ролей и привилегий.
В качестве примечания для тех, кто не знал: вы можете указывать имена аргументов
при вызове функций, чтобы упростить их использование и избежать
путаницы, когда существует много возможных аргументов. Если для значения
указан параметр по умолчанию, то передавать значение для этого
аргумента не требуется. Например:
SELECT create_partition('schema.table', 'col1', '1 day', p_start_partition := '2023-03-20');
F.54.5.3.1. Создание объектов #
create_partition(
p_parent_table text
, p_control text
, p_interval text
, p_type text DEFAULT 'range'
, p_epoch text DEFAULT 'none'
, p_premake int DEFAULT 4
, p_start_partition text DEFAULT NULL
, p_default_table boolean DEFAULT true
, p_automatic_maintenance text DEFAULT 'on'
, p_constraint_cols text[] DEFAULT NULL
, p_template_table text DEFAULT NULL
, p_jobmon boolean DEFAULT true
, p_date_trunc_interval text DEFAULT NULL
, p_control_not_null boolean DEFAULT true
, p_time_encoder text DEFAULT NULL
, p_time_decoder text DEFAULT NULL
)
RETURNS boolean
Основная функция для создания набора секций с одной родительской таблицей и унаследованными дочерними таблицами. Родительская таблица должна уже существовать и быть объявлена как секционированная перед вызовом этой функции. Все параметры, переданные этой функции, должны соответствовать этому определению. Пожалуйста, примените все значения по умолчанию, индексы, ограничения, привилегии и владение к родительской таблице, чтобы они распространялись на дочерние таблицы. См. примечания выше о обработке уникальных индексов и других свойств таблицы.
Во время выполнения этой функции на родительскую таблицу накладывается блокировка ACCESS EXCLUSIVE. во время выполнения этой функции. При выполнении этой функции данные не перемещаются при выполнении этой функции данные не перемещаются, поэтому блокировка должна быть кратковременной.
Раздел по умолчанию и шаблонная таблица создаются по умолчанию, если не указано иное
p_parent_table- существующая родительская таблица. ДОЛЖНА быть указана с учетом схемы, даже если находится в публичной схемеp_control- столбец, на основе которого будет выполняться партиционирование. Должен быть столбцом типа время, целое число, текст или uuid. Если control имеет тип text/uuid, необходимо установить p_time_encoder и p_time_decoder.p_interval- временной или целочисленный диапазон интервала для каждой партиции. Независимо от типа партиционирования, значение должно быть указано в виде текста.interval- Любое допустимое значение для типа данных interval. Не приводите значение параметра к другому типу, просто оставьте его как текст.integer- Для секционирования по идентификатору указывается диапазон целых значений идентификатора, который должен быть установлен для каждого раздела. Введите это значение как целое число в текстовом формате (‘100’, а не 100). Если интервал >=2, тоp_typeдолжен бытьrange. Если интервал равен 1, тоp_typeдолжен бытьlist. Также обратите внимание, что хотя для секционирования по идентификатору поддерживаются числовые значения, интервал всё равно должен быть целым числом.
p_type- тип выполняемого партиционирования. В настоящее время поддерживаются только range и list. См. параметрp_intervalдля особых условий, касающихся типа.p_epoch- указываетpg_partman, что управляющий столбец имеет целочисленный тип, но фактически представляет собой значение времени эпохи. Допустимые значения для этой опции:seconds,milliseconds,microseconds,nanosecondsиnone. Значение по умолчанию —none. Все имена таблиц будут основаны на времени. В дополнение к обычному индексу по управляющему столбцу обязательно создайте функциональный, основанный на времени индекс по управляющему столбцу (to_timestamp(controlcolumn)), чтобы это работало эффективно.p_premake- это сколько дополнительных секций всегда должно быть впереди текущего раздела. Значение по умолчанию - 4. Это будет поддерживать минимум 5 созданных секций, включая текущий. Например, если сегодня 6 сентября, иpremakeустановлен на 4 для ежедневного раздела, то секции будут созданы для 6-го, а также 7-го, 8-го, 9-го и 10-го. Обратите внимание, что некоторые интервалы могут иногда вызывать создание дополнительного раздела или пропуск одного из-за високосных лет, различной длины месяцев и т.д. Обычно это не причиняет вреда и должно самоисправляться (см. раздел О, касающийся часовых поясов и не-UTC). Если разделение когда-либо отстанет от значенияpremake, нормальная работаrun_maintenance()и вставка данных должны автоматически наверстать упущенное.p_start_partition- позволяет указать первый раздел набора вместо его автоматического определения. Должно быть допустимое значение временной метки (для основанных на времени) или положительное целое число (для основанных на идентификаторах). Однако имейте в виду, что фактический тип данных параметра - текст. Для основанного на времени разбиения будут созданы все секции, начиная с указанной временной метки и до CURRENT_TIMESTAMP (плюсpremake). Для разбиения на основе идентификаторов будет создан только раздел, начинающийся с указанного значения (плюсpremake). Обратите внимание, что для субразбиения это применяется только во время начальной настройки и не во время текущего обслуживания.p_default_table- логический флаг для определения, создается ли таблица по умолчанию. По умолчанию true.p_automatic_maintenance— параметр для установки, будет ли обслуживание выполняться автоматически при вызовеrun_maintenance()без параметра таблицы или фоновым рабочим процессом. Допустимые значения:onиoff. По умолчанию —on. Если установлено значение off,run_maintenance()всё ещё может быть вызвана для отдельного набора секций, если передать его в качестве параметра функции. Подробнее см. run_maintenance в разделе Maintenance Objects ниже.p_constraint_cols- необязательный параметр-массив, который задаёт столбцы, для которых будут установлены дополнительные ограничения. Смотрите раздел Исключение по ограничению выше для получения дополнительной информации о том, как это работает, а также функцию apply_constraints() для примера использования.p_template_table- Если вы не передадите значение здесь, шаблонная таблица будет автоматически создана для вас в той же схеме, в которую был установлен pg_partman. Если вы предварительно создадите шаблонную таблицу и передадите ее имя здесь, то начальные дочерние таблицы немедленно получат эти свойства, обсуждаемые в разделе About.p_jobmon- позволяетp_jobmonиспользовать расширениеpg_jobmonдля контроля правильности работы секционирования. По умолчанию TRUE.p_date_trunc_interval- По умолчанию, при секционировании по времени pg_partman будет усекать начальные значения дочерних таблиц так, чтобы они совпадали с началом типичных границ (полночь для дневных, 1-е число для месячных, 1 января для годовых и т.д.). Если требуется интервал секционирования, который не совпадает с этими границами, эта опция может понадобиться для того, чтобы дочерняя таблица имела ожидаемые границы (особенно если вы также устанавливаетеp_start_partition). Допустимые значения для этого параметра — это значения интервала, поддерживаемые встроенной функцией PostgreSQLdate_trunc()(day, week, month и т.д.). Например, если вы задаёте интервал в 9 недель, по умолчанию pg_partman будет усекать таблицы по месяцам (так как интервал больше одного месяца, но меньше года) и в некоторых случаях неожиданно начинать с первого числа месяца. Установите значение этого параметра вweek, чтобы начальные значения дочерних таблиц корректно усекались по неделям и совпадали с 9-недельным интервалом. Если вы используете пользовательский временной интервал, поэкспериментируйте с этой опцией, чтобы получить желаемый набор дочерних таблиц, или используйте более типичный интервал секционирования для упрощения управления секциями.p_control_not_null- По умолчанию это значение истинно, и управляющий столбец должен быть установлен как NOT NULL. Установка этого параметра в значение false позволяет управляющему столбцу принимать значение NULL. Разрешать это не рекомендуется без очень тщательной проверки и явно определённого случая использования, так как это может привести к избыточному количеству данных в дочернем разделе по умолчанию (DEFAULT).p_time_encoder- имя функции, которая кодирует временную метку в строку, представляющую границы партиций. Установка этого параметра неявно включает партиционирование по времени и является обязательной для управляющих столбцов типа text/uuid. Это позволяет партиционировать таблицы с использованием идентификаторов, основанных на времени, таких как uuidv7, ulid, snowflake ids и другие. Функция должна безопасно обрабатывать входное значение NULL. Примеры использования см. в test-time-daily.sql и test-uuid-daily.p_time_decoder- имя функции, которая декодирует управляющее значение типа text/uuid в метку времени. Указание этой функции неявно включает секционирование по времени и обязательно для управляющих столбцов типов text/uuid. Это позволяет секционировать таблицы с использованием идентификаторов, основанных на времени, таких как uuidv7, ulid, snowflake ids и другие. Функция должна безопасно обрабатывать входное значение NULL, а также на данный момент должна принимать параметр типа TEXT. Примеры использования см. в test-time-daily.sql и test-uuid-daily.
create_sub_partition(
p_top_parent text
, p_control text
, p_interval text
, p_type text DEFAULT 'range'
, p_default_table boolean DEFAULT true
, p_declarative_check text DEFAULT NULL
, p_constraint_cols text[] DEFAULT NULL
, p_premake int DEFAULT 4
, p_start_partition text DEFAULT NULL
, p_epoch text DEFAULT 'none'
, p_jobmon boolean DEFAULT true
, p_date_trunc_interval text DEFAULT NULL
, p_control_not_null boolean DEFAULT true
, p_time_encoder text DEFAULT NULL
, p_time_decoder text DEFAULT NULL
)
RETURNS boolean
Создайте набор подсекций уже существующего секционированного набора. См. важные примечания о разделении на подсекции в разделе О.
p_top_parent- Этот параметр является родительской таблицей уже существующего набора секций. Он указываетpg_partmanпревратить все дочерние таблицы данного набора секций в их собственные родительские таблицы собственных наборов секций, используя остальные параметры для этой функции.p_declarative_check- Превращение существующего набора секций в набор с подсекциями является разрушающим процессом. Таблица должна быть объявлена секционированной при создании и не может быть изменена позже. Поэтому существующие дочерние таблицы должны быть удалены и воссозданы как родительские таблицы с секциями. Этот флаг здесь для того, чтобы гарантировать, что эта функция не будет запущена без предварительного согласия на то, что все данные в наборе секций будут уничтожены в процессе создания. Он должен быть установлен в “yes”, чтобы продолжить с разделением на подсекции.Все остальные параметры этой функции имеют точно такое же назначение, как и у
create_partition(), но вместо этого используются для того, чтобы указатьpg_partman, как каждая дочерняя таблица будет разбиваться на партиции.Например, если у вас уже есть набор секций по годам, и нужно разделить каждый из секций по годам по дням, вы должны использовать эту функцию.
Рекомендуется использовать короткие имена таблиц для наборов подсекций, если вы планируете полагаться на имена таблиц для организации. Суффикс, добавляемый в конец имени таблицы, всегда гарантированно присутствует для любого типа раздела, активного для этого набора. Более длинные имена таблиц могут привести к усечению оригинальных имен родительских таблиц и, возможно, к усечению суффикса верхнего уровня секционирования. Это невозможно контролировать и гарантирует, что суффикс самого нижнего уровня секционирования сохранится.
Обратите внимание, что для первого уровня подпартиционирования аргумент
p_parent_table, который вы изначально передали вcreate_partition(), будет точно таким же значением, которое вы передаёте вcreate_sub_partition(). Если потребуется дальнейшее подпартиционирование, тогда вы начнёте передавать вcreate_sub_partition()другое значение (дочерние таблицы набора партиций верхнего уровня).Шаблонная таблица, которая уже установлена для данного 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
, p_override_system_value boolean DEFAULT false
, p_ignore_infinity boolean DEFAULT false
)
RETURNS bigint
Эта функция используется для секционирования данных, которые могли существовать до настройки родительской таблицы как набора временных секций. Она также исправляет данные, которые вставляются в таблицу по умолчанию.
Если требуемая секция не существует, он будет автоматически создан. Если требуемая секция уже существует, данные будут перемещены туда.
Если вы пытаетесь автоматически разделить большой объем данных, рекомендуется использовать процедуру
partition_data_procдля фиксации данных небольшими партиями. Это значительно уменьшит проблемы, вызванные длительными транзакциями и конфликтами данных.Для наборов с подпартиционированием необходимо начинать партиционирование данных с самого высокого уровня и постепенно спускаться на каждый уровень ниже. Это означает, что сначала нужно выполнить эту функцию, прежде чем запускать create_sub_partition() для создания дополнительных уровней партиционирования. Затем продолжайте запускать эту функцию для каждого нового под-родителя после их создания. Полный пример смотрите в главе Раздел F.54.6.
Предостережение
Будьте ОЧЕНЬ осторожны с наборами подпартиций и использованием этой функции, поскольку подпартиционирование может быть разрушительной операцией. См. create_sub_partition().
p_parent_table- существующая родительская таблица. ДОЛЖНА быть указана с учетом схемы, даже если находится в публичной схеме.p_batch_count- необязательный аргумент, сколько раз выполнитьbatch_intervalв одном вызове этой функции. Значение по умолчанию - 1. В настоящее время устанавливает, сколько дочерних таблиц будет обработано за один запуск, но когда p_batch_interval снова заработает, будет явно указывать, сколько пакетов запускать.p_batch_interval- необязательный аргумент, задает интервал данных, которые будут перемещаться в каждой партии. По умолчанию используется настроенный интервал секционирования, если не указан или если вы указываете интервал, превышающий интервал секционирования.Предостережение
Это не может быть установлено меньше, чем интервал секционирования, если данные перемещаются из таблицы по умолчанию. Ведутся работы, чтобы это стало возможным, но с некоторыми ограничениями. Если вы перемещаете данные из исходной таблицы, которая не является таблицей по умолчанию набора секций, можно установить этот интервал меньше, чем интервал секционирования, чтобы избежать перемещения большого объема данных в долго выполняющихся транзакциях.
p_lock_wait- необязательный аргумент, устанавливает, как долго в секундах ждать разблокировки строки перед истечением времени ожидания. По умолчанию ожидание продолжается бесконечно.p_order- необязательный аргумент, по умолчанию данные мигрируются в порядке возрастания (ASC). Позволяет изменить на порядок убывания (DESC).p_analyze- необязательный аргумент, по умолчанию при создании новой дочерней таблицы выполняется анализ родительской таблицы набора секций для обеспечения исключения по ограничению. Этот анализ можно пропустить, установив значение false, что поможет увеличить скорость перемещения больших объемов данных. Если это установлено в false, настоятельно рекомендуется вручную выполнить анализ набора секций после завершения, чтобы обновить статистику.p_source_table- Этот параметр можно использовать, когда необходимо переместить данные в секционированную таблицу. Передайте имя таблицы с указанием схемы в этот параметр, и любые данные в этой таблице будут ПЕРЕМЕЩЕНЫ в набор секций, указанный p_parent_table, создавая любые дочерние таблицы по мере необходимости.p_ignored_columns- Этот параметр позволяет исключать определенные столбцы при перемещении данных из таблицы по умолчанию/источника в целевую дочернюю таблицу(ы). Это обычно требуется только при использовании столбцов с значением GENERATED ALWAYS, так как прямая вставка значения приведет к ошибке при перемещении данных. Значение представляет собой текстовый массив имен столбцов.p_override_system_value— При перемещении данных из таблицы по умолчанию или другой исходной таблицы в набор секций, содержащий столбцы с параметром GENERATED ALWAYS, может потребоваться сохранить значения из источника вместо генерации новых значений. Этот параметр позволяет установить флагOVERRIDING SYSTEM VALUEпри вставке данных. Обратите внимание, что при переопределении вставляемых данных может потребоваться сбросить базовую последовательность для целевых сгенерированных столбцов.p_ignore_infinity- Булевый параметр, определяющий, следует ли игнорировать значенияinfinityи-infinityв таблице по умолчанию. Если эти значения необходимы, таблица по умолчанию — единственная таблица, о которой pg_partman знает, что она может содержать эти значения. Можно создать другие неограниченные дочерние таблицы для хранения этих значений, но это выходит за рамки обслуживания pg_partman и может привести к конфликтам с таблицами, которые pg_partman попытается создать. ВАЖНО: Чрезмерное количество строк в таблице по умолчанию или любой неограниченной таблице может значительно повлиять на производительность обслуживания секций. Если при использовании этого флага наблюдаются длительные времена обслуживания, пожалуйста, проверьте количество строк в таблице по умолчанию. По умолчанию — false.Возвращает количество строк, которые были перемещены из родительской таблицы в секции. Возвращает ноль, когда исходная таблица пуста и разбиение завершено.
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
, p_override_system_value boolean DEFAULT false
)
RETURNS bigint
Эта функция используется для секционирования данных, которые могли существовать до настройки родительской таблицы как набора секций на основе чисел. Она также исправляет данные, которые вставляются по умолчанию.
Если требуемая секция не существует, он будет автоматически создан. Если требуемая секция уже существует, данные будут перемещены туда.
Если вы пытаетесь автоматически разделить большой объем данных, рекомендуется использовать процедуру
partition_data_procдля фиксации данных небольшими партиями. Это значительно уменьшит проблемы, вызванные длительными транзакциями и конфликтами данных.Для наборов с подпартиционированием необходимо начинать партиционирование данных с самого высокого уровня и постепенно спускаться на каждый следующий уровень. Это означает, что сначала нужно выполнить эту функцию, прежде чем запускать create_sub_partition() для создания дополнительных уровней партиционирования. Затем продолжайте запускать эту функцию для каждого нового под-родителя после их создания. Полный пример смотрите в главе Раздел F.54.6.
Предостережение
Будьте ОЧЕНЬ осторожны с наборами подпартиций и использованием этой функции, поскольку подпартиционирование может быть разрушительной операцией. См. create_sub_partition().
p_parent_table- существующая родительская таблица. ДОЛЖНА быть указана с учетом схемы, даже если находится в публичной схеме.p_batch_count- необязательный аргумент, сколько раз выполнитьbatch_intervalв одном вызове этой функции. Значение по умолчанию - 1. Это определяет, сколько дочерних таблиц будет обработано за один запуск.p_batch_interval- необязательный аргумент, задает интервал данных, которые будут перемещаться в каждой партии. По умолчанию используется настроенный интервал секционирования, если не указан или если вы указываете интервал, превышающий интервал секционирования.Предостережение
Это не может быть установлено меньше, чем интервал секционирования, если данные перемещаются из таблицы по умолчанию. Ведутся работы, чтобы это стало возможным, но с некоторыми ограничениями. Если вы перемещаете данные из исходной таблицы, которая не является таблицей по умолчанию набора секций, можно установить этот интервал меньше, чем интервал секционирования, чтобы избежать перемещения большого объема данных в долго выполняющихся транзакциях.
p_lock_wait- необязательный аргумент, устанавливает, как долго в секундах ждать разблокировки строки перед истечением времени ожидания. По умолчанию ожидание продолжается бесконечно.p_order- необязательный аргумент, по умолчанию данные перемещаются из родительской таблицы в порядке возрастания (ASC). Позволяет изменить порядок на убывающий (DESC).p_analyze- необязательный аргумент, по умолчанию при создании новой дочерней таблицы выполняется анализ родительской таблицы набора секций для обеспечения исключения по ограничению. Этот анализ можно пропустить, установив значение false, что поможет увеличить скорость перемещения больших объемов данных. Если это установлено в false, настоятельно рекомендуется вручную выполнить анализ набора секций после завершения, чтобы обновить статистику.p_source_table- Этот параметр можно использовать, когда необходимо переместить данные в секционированную таблицу. Передайте имя таблицы с указанием схемы в этот параметр, и любые данные в этой таблице будут ПЕРЕМЕЩЕНЫ в набор секций, указанный p_parent_table, создавая любые дочерние таблицы по мере необходимости.p_ignored_columns- Этот параметр позволяет исключать определенные столбцы при перемещении данных из таблицы по умолчанию/источника в целевую дочернюю таблицу(ы). Это обычно требуется только при использовании столбцов с значением GENERATED ALWAYS, так как прямая вставка значения приведет к ошибке при перемещении данных. Значение представляет собой текстовый массив имен столбцов.p_override_system_value— При перемещении данных из таблицы по умолчанию или другой исходной таблицы в набор секций, содержащий столбцы с параметром GENERATED ALWAYS, может потребоваться сохранить значения из источника вместо генерации новых значений. Этот параметр позволяет установить флагOVERRIDING SYSTEM VALUEпри вставке данных. Обратите внимание, что при переопределении вставляемых данных может потребоваться сбросить базовую последовательность для целевых сгенерированных столбцов.Возвращает количество строк, которые были перемещены из родительской таблицы в секции. Возвращает ноль, когда исходная таблица пуста и разбиение завершено.
partition_data_proc (
p_parent_table text
, p_loop_count int DEFAULT NULL
, p_interval text DEFAULT NULL
, p_lock_wait int DEFAULT 0
, p_lock_wait_tries int DEFAULT 10
, p_wait int DEFAULT 1
, p_order text DEFAULT 'ASC'
, p_source_table text DEFAULT NULL
, p_ignored_columns text[] DEFAULT NULL
, p_quiet boolean DEFAULT false
, p_ignore_infinity boolean DEFAULT false
)
Процедура, которая может разделять данные на отдельные партии коммитов, чтобы избежать длительных транзакций и проблем с конкуренцией данных.
Вызывает либо partition_data_time(), либо partition_data_id() в цикле в зависимости от типа секционирования.
p_parent_table- Родительская таблица уже созданного набора секций.p_loop_count- Сколько раз выполнять цикл по значению, заданному для p_interval. Если p_interval не установлен, будет использоваться интервал секционирования по умолчанию, и будет создано не более этого количества секций. Процедура выполняет фиксацию (commit) в конце каждого цикла (НЕ передаётся как p_batch_count в функцию секционирования). Если не задано, все данные в таблице по умолчанию/исходной таблице будут секционированы за один запуск процедуры.p_interval- Параметр, который передается в функцию разбиения в качестве аргумента p_batch_interval. См. основные функции для дальнейших объяснений.p_lock_wait- Параметр, передаваемый непосредственно в основную функцию partition_data_*(). Количество секунд ожидания строк, которые могут быть заблокированы другой транзакцией. По умолчанию ожидать вечно (0).p_lock_wait_tries- Параметр, устанавливающий, сколько раз процедура будет пытаться ожидать заданное время для p_lock_wait. По умолчанию 10 попыток.p_wait- Заставляет процедуру ожидать заданное количество секунд между фиксацией (партиями), чтобы снизить нагрузку на запись.p_order- То же самое, что и параметр p_order в вызываемой функции разбиенияp_source_table- То же самое, что и опция p_source_table в вызываемой функции секционирования.p_ignored_columns- Эта опция позволяет фильтровать определенные столбцы при перемещении данных из таблицы по умолчанию/родительской в соответствующую дочернюю таблицу(ы). Это обычно требуется только при использовании столбцов со значением GENERATED ALWAYS, поскольку прямая вставка значения приведет к ошибке при перемещении данных. Значение представляет собой массив текстовых имен столбцов.p_quiet- Процедуры не могут возвращать значения, поэтому по умолчанию они выводят УВЕДОМЛЕНИЯ, чтобы показать прогресс. Установите эту опцию, чтобы подавить эти уведомления.p_ignore_infinity- Булевый параметр, определяющий, следует ли игнорировать значенияinfinityи-infinityв таблице по умолчанию. Если эти значения необходимы, таблица по умолчанию — единственная таблица, о которой pg_partman знает, что она может содержать эти значения. Можно создать другие неограниченные дочерние таблицы для хранения этих значений, но это выходит за рамки обслуживания pg_partman и может привести к конфликтам с таблицами, которые pg_partman попытается создать. ВАЖНО: Чрезмерное количество строк в таблице по умолчанию или любой неограниченной таблице может значительно повлиять на производительность обслуживания секций. Если при использовании этого флага наблюдаются длительные времена обслуживания, пожалуйста, проверьте количество строк в таблице по умолчанию. По умолчанию — false.
partition_data_async (
p_parent_table text
, p_loop_count int DEFAULT NULL
, p_interval text DEFAULT NULL
, p_lock_wait int DEFAULT 0
, p_lock_wait_tries int DEFAULT 10
, p_wait int DEFAULT 1
, p_order text DEFAULT 'ASC'
, p_ignored_columns text[] DEFAULT NULL
, p_quiet boolean DEFAULT false
)
Примечание: Эта процедура в настоящее время работает только с разделением по времени начиная с версии pg_partman 5.3.0. Поддержка целых чисел/идентификаторов находится в разработке.
Процедура, предназначенная для помощи в перемещении данных из раздела по умолчанию небольшими пакетами строк на одну зафиксированную транзакцию, чем интервал разделения.
Эта процедура предназначена ТОЛЬКО для перемещения данных из секции по умолчанию. Если вы перемещаете данные из другого исходного источника в секционированную таблицу, вы уже можете использовать меньшие размеры пакетов, чем интервал секционирования, с помощью процедуры
partition_data_proc()(или стандартных функцийpartition_data_time/id()).Процедуру
partition_data_proc()по-прежнему можно использовать для миграции данных из таблицы по умолчанию, но размер интервала транзакции никогда не может быть меньше интервала секционирования, поскольку новая дочерняя таблица не может быть создана до тех пор, пока все соответствующие данные не будут перемещены из таблицы по умолчанию.ВАЖНОЕ ЗАМЕЧАНИЕ: Эта процедура работает путем первоначального перемещения всех данных из целевой дочерней таблицы в другую реальную, рабочую таблицу. Меньшие пакеты данных фиксируются (commit), поэтому данные, находящиеся в процессе перемещения до их переноса в целевую дочернюю таблицу, НЕ ВИДНЫ пользователям таблицы. Если вы не можете позволить себе, чтобы данные временно исчезали для пользователей таблицы, то этот асинхронный метод НЕ ДАСТ желаемого результата. В этом случае вы должны использовать размер пакета, равный размеру интервала набора секций, и вы можете использовать
partition_data_proc().Как работает эта процедура:
Размер интервала — это объем данных, который перемещается при каждом подтверждении (commit).
Фиксации выполняются, когда данные перемещаются как во временное место хранения, так и в конечную дочернюю таблицу.
Итак, значение счетчика цикла для перемещения всех данных одной дочерней таблицы равно интервалу набора секций, делённому на размер интервала, переданный в эту функцию, умноженному на 2.
Например: Набору ежедневных секций (24 часа) для этой асинхронной процедуры задается интервал в 6 часов, чтобы она выполняла фиксацию после перемещения каждого блока данных за 6 часов. Это означает, что будет 4 пакета данных, которые сначала перемещаются во временную рабочую таблицу, а затем в конечную дочернюю таблицу, всего выполняется 8 фиксаций. Таким образом, p_loop_count будет равен 8 для перемещения всех данных в одну дочернюю таблицу этого набора секций ( (24 / 6) * 2).
Умножьте это значение на количество дочерних таблиц, которые вы планируете переместить.
Если количество повторений не указано, вся таблица по умолчанию будет опустошена с использованием заданного интервала пакетной обработки.
Реальная (не временная) таблица создаётся по мере необходимости для хранения промежуточных данных во время их перемещения. Эта таблица будет удалена, как только дочерняя таблица будет создана и все данные будут перемещены в неё.
Паттерн именования рабочей таблицы следующий:
originalschema.partman_tmp_storage_originaltablename
Пока данные мигрируются, столбец
async_partitioning_in_progressв таблицеpart_configбудет содержать значение, относящееся к последнему набору данных, который был перемещён. Пока этот столбец содержит значение, и во время выполнения этой процедуры, всё обслуживание для этого набора секций будет пропускаться (в журналах PostgreSQL оставляется предупреждение). Чтобы возобновить обычное обслуживание, этот столбец должен быть NULL. Это значение будет автоматически установлено в NULL после завершения обработки каждой дочерней таблицы.Поскольку для миграции данных используется реальная таблица, состояние миграции сохраняется между несколькими запусками этой процедуры. Но, как указано в предыдущем пункте, все обычные операции по обслуживанию набора секций будут пропущены, пока набор секций находится в состоянии, когда все данные для заданной дочерней таблицы еще не были полностью перемещены в целевую дочернюю таблицу.
p_parent_table- Родительская таблица уже созданного набора секций.p_loop_count- Сколько раз выполнять цикл с использованием значения, заданного для p_interval. См. выше важную информацию о том, что на самом деле означает этот счетчик циклов при использовании данной процедуры. Если не задано, все данные в таблице по умолчанию будут секционированы за один запуск процедуры.p_interval- Параметр, который задаёт размер интервала, определяющий, сколько строк будет зафиксировано в одной транзакции. Смотрите пояснения в пунктах выше для получения дополнительной информации о том, как используется этот параметр.p_lock_wait- Параметр, который передаётся напрямую в базовую функцию partition_data_*(). Количество секунд ожидания строк, которые могут быть заблокированы другой транзакцией. По умолчанию ожидание бесконечно (0).p_lock_wait_tries- Параметр, определяющий, сколько раз процедура попытается ожидать время, заданное для p_lock_wait. По умолчанию — 10 попыток.p_wait- Заставляет процедуру делать паузу на заданное количество секунд между фиксациями (пакетами), чтобы уменьшить нагрузку на записьp_order- То же, что и параметр p_order в вызываемой функции разбиения на разделыp_source_table- То же, что и параметр p_source_table в вызываемой функции партиционированияp_ignored_columns- Эта опция позволяет отфильтровывать определённые столбцы при перемещении данных из таблицы по умолчанию/родительской таблицы в соответствующую дочернюю таблицу(ы). Обычно это требуется только при использовании столбцов с параметром GENERATED ALWAYS, поскольку прямая вставка значения приведёт к ошибке при перемещении данных. Значение — это текстовый массив имён столбцов.p_quiet- Процедуры не могут возвращать значения, поэтому по умолчанию они выводят NOTICE для отображения хода выполнения. Установите эту опцию, чтобы отключить эти уведомления.
create_partition_time(
p_parent_table text
, p_partition_times timestamptz[]
, p_start_partition text DEFAULT NULL
)
RETURNS boolean
Эта функция используется для создания дочерних секций для заданной родительской таблицы.
Обычно эта функция никогда не вызывается вручную, так как создание секций управляется run_maintenance(). Но если вам нужно принудительно создать определенные дочерние таблицы вне нормального обслуживания, эта функция упрощает эту задачу.
p_parent_table- родительская таблица для создания новой дочерней таблицы(таблиц).p_partition_times- Массив значений timestamptz для создания дочерних таблиц. Если дочерняя таблица не существует, она будет создана. Если она уже существует, будет использована существующая таблица, и функция завершится корректно. Учтите, что указанное значение будет использовано как нижняя граница для дочерней таблицы и также повлияет на имя, присвоенное дочерней таблице. Поэтому убедитесь, что указанное значение временной метки согласуется с другими дочерними таблицами, иначе вы можете столкнуться с разрывом в покрытии значений.p_start_partition- При разделении на подсекции можно передавать начальное значение секции дочерним таблицам подсекции.Возвращает TRUE, если для заданных значений timestamptz были созданы дочерние таблицы. Возвращает false, если дочерние таблицы не были созданы.
create_partition_id(
p_parent_table text
, p_partition_ids bigint[]
, p_start_partition text DEFAULT NULL
)
RETURNS boolean
Эта функция используется для создания дочерних секций для заданной родительской таблицы.
Обычно эта функция никогда не вызывается вручную, так как создание секций управляется run_maintenance(). Но если вам нужно принудительно создать определенные дочерние таблицы вне нормального обслуживания, эта функция может упростить эту задачу.
p_parent_table- родительская таблица для создания новой дочерней таблицы(таблиц).p_partition_ids- Массив целочисленных значений для создания дочерних таблиц. Если дочерняя таблица не существует, она будет создана. Если она уже существует, будет использована существующая таблица, и функция завершится корректно. Учтите, что указанное значение будет использоваться как нижняя граница для дочерней таблицы и также повлияет на имя, присвоенное дочерней таблице. Поэтому убедитесь, что указанное целочисленное значение согласуется с другими дочерними таблицами, иначе вы можете столкнуться с разрывом в покрытии значений.p_start_partition- При разделении на подсекции можно передавать начальное значение секции дочерним таблицам подсекции.Возвращает TRUE, если для заданных значений integer были созданы дочерние таблицы. Возвращает false, если дочерние таблицы не были созданы.
F.54.5.3.2. Объекты обслуживания #
run_maintenance(
p_parent_table text DEFAULT NULL
, p_analyze boolean DEFAULT false
, 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, поэтому вы можете запускать эту функцию чаще, чем требуется, не боясь бесполезного создания дополнительных секций.
p_parent_table- необязательный параметр, который, если передан, приведет к тому, чтоrun_maintenance()будет выполнена ТОЛЬКО для указанной таблицы, независимо от того, чему установлено значение automatic_maintenance. Таблицы с высокой скоростью транзакций могут вызывать конкуренцию ресурсов при одновременном выполнении обслуживания для многих таблиц, поэтому этот параметр позволяет более точно контролировать, когда обслуживание секций выполняется для конкретных таблиц. Обратите внимание, что это также приведет к запуску системы хранения только для указанной таблицы.p_analyze- По умолчанию анализ не выполняется после создания новых дочерних таблиц. Для больших наборов секций анализ может занять некоторое время, и еслиrun_maintenance()управляет несколькими секциями за один запуск, это может вызвать конкуренцию, пока анализ не завершится. Однако для полной эффективности исключения по ограничению или отсечения секций анализ должен быть выполнен на уровне родительской таблицы в какой-то момент. Установите это значение в true, чтобы выполнить анализ на любых наборах секций, в которых создана хотя бы одна новая дочерняя таблица. Если в наборе секций не создаются новые дочерние таблицы, анализ не будет выполнен, даже если это значение установлено в true.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
)
Эта процедура может быть вызвана вместо функции
run_maintenance()для того, чтобы заставить Tantor SE делать коммит после завершения обслуживания каждого набора секций. Это значительно снижает проблемы с конкуренцией при долгих транзакциях, когда есть много наборов секций для обслуживания.p_wait- Сколько секунд ждать между запусками обслуживания каждого набора секций. По умолчанию 0.p_analyze- См. параметр p_analyze в функции run_maintenance.
check_default(
p_exact_count boolean DEFAULT true
, p_ignore_infinity boolean DEFAULT false
)
Запустите эту функцию, чтобы отслеживать, вставляются ли строки в таблицы по умолчанию наборов секций, которыми управляет
pg_partman.Возвращает строку для каждой родительской/таблицы по умолчанию вместе с количеством строк, которые она содержит. Возвращает ноль строк, если ничего не найдено.
partition_data_time()иpartition_data_id()можно использовать для перемещения данных из этих родительских/таблиц по умолчанию в соответствующие дочерние таблицы.p_exact_count позволит функции вернуть точное количество строк в каждом родительском элементе, если таковые найдены. Это значение по умолчанию, если параметр не указан. Если вам не важно точное количество, можно установить его в false, и функция вернет результат, если найдена хотя бы одна строка в любом родительском элементе. Это может значительно ускорить проверку, если в родительском элементе содержится большое количество данных или если управляется много секций.
p_ignore_infinity- Булевый параметр, определяющий, следует ли игнорировать значенияinfinityи-infinityв таблице по умолчанию. Если эти значения необходимы, таблица по умолчанию — единственная таблица, о которой pg_partman знает, что она может содержать эти значения. Можно создать другие неограниченные дочерние таблицы для хранения этих значений, но это выходит за рамки обслуживания pg_partman и может привести к конфликтам с таблицами, которые pg_partman попытается создать. ВАЖНО: Чрезмерное количество строк в таблице по умолчанию или любой неограниченной таблице может значительно повлиять на производительность обслуживания секций. Если при использовании этого флага наблюдаются длительные времена обслуживания, пожалуйста, проверьте количество строк в таблице по умолчанию. По умолчанию — 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. Каждая дочерняя таблица возвращается в виде отдельной строки.
Таблицы возвращаются в порядке, который логически имеет смысл для интервала секционирования, а не по локальной сортировке их имен.
Секция по умолчанию также может быть возвращен в этом наборе результатов, если
p_include_defaultустановлено в true. По умолчанию это значение false, так как это гораздо более распространено во внутреннем коде.p_order- необязательный параметр для установки порядка возврата дочерних таблиц. По умолчанию используется порядок возрастания (ASCending). Установите значение ‘DESC’ для возврата в порядке убывания. Если используется значение по умолчанию, оно всегда указывается первым.
show_partition_name(
p_parent_table text
, p_value text
, OUT partition_schema text
, OUT partition_table text
, OUT suffix_timestamp timestamptz
, OUT suffix_id bigint
, OUT table_exists boolean
)
RETURNS record
Учитывая таблицу-родителя с указанной схемой, управляемую pg_partman (p_parent_table), и соответствующее значение (время или id, но в текстовой форме для p_value), вернуть имя дочерней секции, в котором это значение будет существовать.
Если используется секционирование времени по эпохе, укажите значение временной метки, а НЕ целочисленное значение эпохи (используйте функцию to_timestamp() для преобразования значения эпохи).
Возвращает имя дочерней таблицы, независимо от того, существует она или нет
Также возвращает необработанное значение (suffix_timestamp или suffix_id) для суффикса секции для указанной дочерней таблицы.
Также возвращает логическое значение (table_exists), чтобы указать, существует ли на самом деле этот дочерний стол.
show_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" (например, “20230324” ИЛИ “920000”). Полезно для создания собственных суффиксов для партицирования, аналогично тому, как это делает pg_partman.
dump_partitioned_table_definition( p_parent_table text, p_ignore_template_table boolean DEFAULT false ) RETURNS text
Функция для возврата необходимых команд для воссоздания набора секций в pg_partman для заданной родительской таблицы (p_parent_table).
Возвращает как вызов
create_partition(), так и оператор UPDATE для установки дополнительных параметров, хранящихся в part_config.NOTE: В настоящее время это работает только с одноуровневыми наборами секций. Ищем вкладчиков для добавления поддержки наборов подсекций
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_”).
Обратите внимание, что для поддержания пользовательских ограничений это не требует вызова вручную. Создание новых секций автоматически управляет добавлением ограничений к старым дочерним таблицам.
Столбцы, для которых должны быть заданы ограничения, указываются в массиве столбца constraint_cols таблицы part_config или при создании с помощью параметра функции
create_partition().Если ограничения
pg_partmanуже существуют на дочерней таблице, функция пропустит их и не создаст дубликаты.Если столбец (-ы), указанные, содержат только значения NULL, то ограничение не будет установлено.
Если задан параметр дочерней таблицы, то только для этой дочерней таблицы будут применены ограничения.
Если параметр дочерней таблицы НЕ указан, ограничения накладываются на последнюю дочернюю таблицу, которая старше значения
optimize_constraint. Например, если значение optimize_constraint равно 30, то ограничения будут наложены на дочернюю таблицу, которая находится на 31 позиции от текущей секции (при условии, что предварительное создание секции было выполнено своевременно).Если вам нужно применить ограничения ко всем старым дочерним таблицам, используйте процедуру
reapply_constraints_proc. Этот метод имеет опции, которые упрощают применение ограничений с минимальным воздействием на производительность.Параметр p_job_id является необязательным. Он предназначен для внутреннего использования и позволяет объединить ведение журнала заданий в исходное задание, которое вызвало эту функцию, если это применимо.
drop_constraints(
p_parent_table text
, p_child_table text
, p_debug boolean DEFAULT false
)
RETURNS void
Удалите ограничения, созданные
pg_partmanдля столбцов, настроенных в part_config. Это упрощает очистку ограничений, если требуется редактировать старые данные, и ограничения не позволяют это сделать.Будут удалены только ограничения, которые начинаются с
partmanconstr_*для указанной дочерней таблицы и настроенных столбцов.Если вам нужно удалить ограничения на всех дочерних таблицах, используйте процедуру
reapply_constraints_proc. Она имеет опции, которые упрощают удаление ограничений с минимальным воздействием на производительность.Параметр отладки покажет вам оператор сброса ограничения, который был использован.
reapply_constraints_proc(
p_parent_table text
, p_drop_constraints boolean DEFAULT false
, p_apply_constraints boolean DEFAULT false
, p_analyze boolean DEFAULT true
, p_wait int DEFAULT 0
, p_dryrun boolean DEFAULT false
)
Процедура повторного применения дополнительных ограничений, управляемых pg_partman (см. раздел “Исключение ограничений” в разделе “О программе” выше).
Вызывает
drop_constraints()и/илиapply_constraint()в цикле, фиксируя после каждого удаления или добавления объекта. Это помогает избежать длительных транзакций и конфликтов при выполнении этого на больших наборах секций.Типичное использование будет заключаться в том, чтобы сначала удалить ограничения, отредактировать данные по мере необходимости, а затем снова применить ограничения.
p_parent_table- Родительская таблица уже созданного набора секций.p_drop_constraints- Удалить все ограничения, управляемые pg_partman. Удаляет ограничения на всех дочерних таблицах, включая текущие и будущие таблицы.p_apply_constraints- Применить ограничения на настроенные столбцы ко всем дочерним таблицам, которые старше значения optimize_constraint.p_analyze- логический параметр, управляющий выполнением ANALYZE для набора секций после завершения задания ограничений. По умолчанию true.p_wait- Подождите указанное количество секунд после того, как у таблицы были удалены или применены ограничения, прежде чем переходить к следующей.p_dryrun- Не применять команды удаления/применения ограничений при выполнении этой процедуры. Просто выводит, к каким таблицам будут применены команды, в виде уведомлений (NOTICE).
reapply_privileges(
p_parent_table text
)
RETURNS void
Эта функция используется для повторного применения прав собственности и грантов на все дочерние таблицы на основе того, что установлено для родительской таблицы.
Привилегии, которыми обладает родительская таблица, будут предоставлены всем дочерним таблицам, а привилегии, которыми родительская таблица не обладает, будут отозваны (с параметром CASCADE).
Проверяемые привилегии: SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES и TRIGGER.
Имейте в виду, что для больших наборов секций это может быть очень длительная операция, и именно поэтому она была сделана отдельной функцией для независимого выполнения. Применяются только те привилегии, которые отличаются между родительской и дочерней секциями, но все равно необходимо выполнять запросы к системному каталогу и сравнения для каждого отдельной дочерней секции и всех индивидуальных привилегий на каждом.
p_parent_table- родительская таблица набора секций. Должна быть указана с указанием схемы и соответствовать уже настроенному имени родительской таблицы вpg_partman.
stop_sub_partition(
p_parent_table text
, p_jobmon boolean DEFAULT true
)
RETURNS boolean
По умолчанию, если вы отменяете дочернюю таблицу, которая также разбита на секции, это не остановит разделение на подсекции дочерних таблиц родительского набора секций, пока родительский набор также не будет отменен. Чтобы справиться с этой ситуацией, когда вы не удаляете родительский набор, но не хотите, чтобы чтобы дополнительные дочерние таблицы разделялись на подсекции, можно использовать эту функцию.
Эта функция просто удаляет запись parent_table из таблицы part_config_sub. Но это предоставляет предсказуемый, программный способ сделать это и также обеспечивает ведение журнала jobmon для операции.
F.54.5.3.3. Объекты уничтожения #
undo_partition(
p_parent_table text
, p_target_table text
, p_loop_count int DEFAULT 1
, p_batch_interval text DEFAULT NULL
, p_keep_table boolean DEFAULT true
, p_lock_wait numeric DEFAULT 0
, 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_proc()для выполнения той же задачи. Это значительно уменьшит проблемы, вызванные длительными транзакциями и конкуренцией за данные.При выполнении этой функции, столбец
undo_in_progressв таблице конфигурации устанавливается в значение true. Это приводит к остановке создания всех секций и управления их сохранением.По умолчанию разделы не удаляются (DROPPED), они отсоединяются (DETACHed). Это оставляет бывшие дочерние таблицы пустыми, независимыми таблицами.
Без ручной установки любого из аргументов batch, каждый запуск функции будет перемещать все данные из одной секции в целевой.
Когда все дочерние таблицы были отсоединены/удалены, конфигурационные данные автоматически удаляются из
pg_partman.Для подсекционированных таблиц вам может потребоваться начать с самой низкоуровневой родительской таблицы и отменить изменения оттуда, затем двигаться вверх.
p_parent_table- родительская таблица набора секций. Должна быть указана с указанием схемы и соответствовать уже настроенному имени родительской таблицы вpg_partman.p_target_table- Схемно-квалифицированная таблица, в которую будут перемещены данные старой секционированной таблицы. Обязательно, так как секционированная таблица не может быть преобразована в не секционированную таблицу. Схема может отличаться от исходной таблицы.p_loop_count- необязательный аргумент, который задает, сколько раз перемещать объем данных, равный аргументуp_batch_interval(или интервалу секционирования по умолчанию, если не задан) за один запуск функции. По умолчанию 1.p_batch_interval- необязательный аргумент. Интервал времени или идентификатор того, сколько данных перемещать. Этот интервал может быть меньше интервала секционирования, что позволяет разбивать очень большие секции на меньшие партии коммитов. По умолчанию используется настроенный интервал секционирования, если не указан или если указан интервал больше, чем интервал секционирования. Обратите внимание, что значение должно быть передано этому параметру в виде текста.p_keep_table- необязательный аргумент, установка которого в false приведет к удалению старой дочерней таблицы вместо отсоединения после перемещения всех ее данных. Обратите внимание, что для фактического удаления таблицы из набора требуется как минимум два пакета.p_lock_wait- необязательный аргумент, устанавливает, как долго в секундах ждать разблокировки строки или таблицы перед истечением времени ожидания. По умолчанию ожидание продолжается бесконечно.p_ignored_columns- Эта опция позволяет фильтровать определенные столбцы при перемещении данных из дочерних таблиц в целевую таблицу. Это обычно требуется только при использовании столбцов со значением GENERATED ALWAYS, поскольку прямая вставка значения приведет к ошибке при перемещении данных. Значение представляет собой массив текстовых имен столбцов.p_drop_cascade- Разрешить отмену наборов подпартиций от родительских таблиц выше в дереве наследования. Применяется только, когдаp_keep_tablesустановлено в false. Обратите внимание, что это приводит к удалению всех дочерних таблиц ниже родительской подпартиции, когда эта родительская таблица удаляется.Возвращает количество отмененных секций и количество строк, перемещенных в родительскую таблицу. Значение отмененных секций равно -1, если возникает проблема.
undo_partition_proc(
p_parent_table text
, p_target_table text DEFAULT NULL
, p_loop_count int DEFAULT NULL
, p_interval text DEFAULT NULL
, p_keep_table boolean DEFAULT true
, p_lock_wait int DEFAULT 0
, p_lock_wait_tries int DEFAULT 10
, p_wait int DEFAULT 1
, p_ignored_columns text[] DEFAULT NULL
, p_drop_cascade boolean DEFAULT false
, p_quiet boolean DEFAULT false
)
Процедура, которая может отменить секционирование данных в определенных партиях коммитов, чтобы избежать длительных транзакций и проблем с конфликтом данных.
Вызывает функцию undo_partition() в цикле, выполняя фиксацию по мере необходимости.
p_parent_table- Родительская таблица уже созданного набора секций.p_target_table- То же самое, что и опция p_target_table в функции undo_partition().p_loop_count- Сколько раз повторить значение, заданное для p_interval. Если p_interval не установлен, будет использоваться интервал секционирования по умолчанию и отменяться не более этого количества секций. Процедура фиксирует изменения в конце каждого цикла (НЕ передается как p_batch_count в функцию секционирования). Если не установлено, все данные в наборе секций будут перемещены за один запуск процедуры.p_interval- Значение, передаваемое в функцию undo_partition в качестве аргумента p_batch_interval. Используйте это значение, чтобы установить интервал, меньший, чем интервал секции, для фиксации данных пакетами меньшего размера. Если значение не указано, по умолчанию используется интервал секции.p_keep_table- То же самое, что и опция p_keep_table в функции undo_partition().p_lock_wait- Параметр, передаваемый напрямую в функцию undo_partition(). Количество секунд ожидания строк, которые могут быть заблокированы другой транзакцией. По умолчанию ожидание бесконечно (0).p_lock_wait_tries- Параметр, устанавливающий, сколько раз процедура будет пытаться ожидать заданное время для p_lock_wait. По умолчанию 10 попыток.p_wait- Заставляет процедуру ожидать заданное количество секунд между фиксацией (партиями), чтобы снизить нагрузку на запись.p_ignored_columns- Эта опция позволяет фильтровать определенные столбцы при перемещении данных из дочерних таблиц в целевую таблицу. Это обычно требуется только при использовании столбцов со значением GENERATED ALWAYS, поскольку прямая вставка значения приведет к ошибке при перемещении данных. Значение представляет собой массив текстовых имен столбцов.p_drop_cascade- Разрешить отмену наборов подпартиций от родительских таблиц выше в дереве наследования. Применяется только, когдаp_keep_tablesустановлено в false. Обратите внимание, что это приводит к удалению всех дочерних таблиц ниже родительской подпартиции, когда эта родительская таблица удаляется.p_quiet- Процедуры не могут возвращать значения, поэтому по умолчанию они выводят УВЕДОМЛЕНИЯ, чтобы показать прогресс. Установите эту опцию, чтобы подавить эти уведомления.
config_cleanup(
p_parent_table text
, p_config_table boolean DEFAULT true
, p_config_sub_table boolean DEFAULT true
, p_template_table boolean DEFAULT true
)
RETURNS void
Эта функция может быть использована для удаления заданного набора секций из-под управления pg_partman. Она удаляет все конфигурационные данные из соответствующих таблиц конфигурации и удаляет таблицу-шаблон. Сам набор секций не затрагивается и может продолжать использоваться как обычно и управляться независимо от pg_partman.
p_parent_table text- Имя родительской таблицы набора секций для очистки, с указанием схемыp_config_table boolean- Булевый параметр, определяющий, следует ли удалить информацию о заданном наборе секций из таблицыpart_config. По умолчанию true.p_config_sub_table- Булевый параметр, определяющий, следует ли удалить информацию о данном наборе секций из таблицыpart_config_sub. По умолчанию true.p_template_table- Булевый параметр, определяющий, следует ли удалять шаблонную таблицу набора секций, используемую pg_partman. Имя шаблонной таблицы можно найти в таблицеpart_config. По умолчанию true.
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
, p_reference_timestamp timestamptz DEFAULT CURRENT_TIMESTAMP
)
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.p_retention_schema- необязательный параметр, чтобы указать partman переместить таблицу в другую схему вместо удаления её. Установите это значение в схему, в которую вы хотите переместить таблицу. Эта функция будет использовать значение, настроенное вpart_config, если явно не указано. Если этот параметр установлен, параметр удержанияp_keep_tableигнорируется.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
Эта функция используется для удаления дочерних таблиц из набора секций на основе целочисленных значений в соответствии с политикой хранения. По умолчанию таблица просто теряет наследование, но фактически не удаляется. Для автоматического удаления старых таблиц рекомендуется использовать функцию
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.p_retention_schema- необязательный параметр, чтобы указать partman переместить таблицу в другую схему вместо удаления её. Установите это значение в схему, в которую вы хотите переместить таблицу. Эта функция будет использовать значение, настроенное вpart_config, если явно не указано. Если этот параметр установлен, параметр удержанияp_keep_tableигнорируется.Возвращает количество затронутых секций.
F.54.5.3.4. Таблицы конфигурации #
part_config
Хранит все данные конфигурации для наборов секций, управляемых расширением.
parent_table text NOT NULL , control text NOT NULL , partition_interval text NOT NULL , partition_type text NOT NULL , premake int NOT NULL DEFAULT 4 , automatic_maintenance text NOT NULL DEFAULT 'on' , template_table text , retention text , retention_schema text , retention_keep_index boolean NOT NULL DEFAULT true , retention_keep_table boolean NOT NULL DEFAULT true , epoch text NOT NULL DEFAULT 'none' , constraint_cols text[] , optimize_constraint int NOT NULL DEFAULT 30 , infinite_time_partitions boolean NOT NULL DEFAULT false , datetime_string text , jobmon boolean NOT NULL DEFAULT true , sub_partition_set_full boolean NOT NULL DEFAULT false , undo_in_progress boolean NOT NULL DEFAULT false , inherit_privileges boolean DEFAULT false , constraint_valid boolean DEFAULT true NOT NULL , ignore_default_data boolean NOT NULL DEFAULT true , maintenance_order int DEFAULT NULL , retention_keep_publication boolean NOT NULL DEFAULT false , maintenance_last_run timestamptz , async_partitioning_in_progress text
parent_tableРодительская таблица набора секций
controlСтолбец, используемый в качестве контроля для ограничений секционирования. Должен быть столбцом, основанным на времени или целочисленным.
partition_intervalТекстовый тип значения, определяющий интервал для каждой секции.
Должно быть значение, которое может быть приведено к типам данных интервал или bigint.
partition_typeТип секционирования. Должен быть одним из типов, упомянутых выше в информации о
create_partition().
premakeСколько секций нужно заранее создать перед текущей секцией. По умолчанию - 4.
automatic_maintenanceФлаг для установки автоматического управления обслуживанием, когда вызывается
run_maintenance()без параметра таблицы или фоновым рабочим процессом.Текущие допустимые значения: “вкл” и “выкл”. По умолчанию - “вкл”.
Когда установлено значение "выключено",
run_maintenance()все равно может быть вызван для отдельного набора секций, передав его в качестве параметра функции.
template_tableСхема-квалифицированное имя таблицы, используемой в качестве шаблона для применения любых опций наследования, не обрабатываемых основными опциями партиционирования Tantor SE в PG.
retentionТекстовое значение, определяющее, насколько старыми могут быть данные в дочерней секции, прежде чем они будут удалены.
Должно быть значением, которое может быть приведено либо к типу данных interval (для секционирования по времени), либо к bigint (для секционирования по числам).
Оставьте эту колонку NULL (по умолчанию), чтобы всегда сохранять все дочерние секции. Смотрите О секции для получения дополнительной информации.
retention_schemaСхема для перемещения таблиц в рамках системы хранения вместо их удаления. Переопределяет параметр retention_keep_table.
retention_keep_indexЛогическое значение для определения, удаляются ли индексы для дочерних таблиц, которые отсоединены.
По умолчанию TRUE. Установите FALSE, чтобы индексы дочерней таблицы были удалены при её отсоединении.
retention_keep_tableЛогическое значение для определения, будут ли удаленные дочерние таблицы только отсоединены или фактически удалены.
По умолчанию значение TRUE сохраняет таблицу и только отменяет наследование от нее. Установите значение FALSE, чтобы полностью удалить дочерние таблицы из базы данных.
retention_keep_publicationЕсли
retention_keep_tableустановлен в значение true, определяет, следует ли удалять таблицу из всех публикаций, в которых она может состоять.Логическое значение, по умолчанию имеющее значение false, что означает, что по умолчанию таблицы, которые не были полностью удалены в рамках хранения, удаляются из их публикаций.
epochОтметить таблицу для секционирования по времени с использованием целочисленного значения эпохи вместо метки времени. См. функцию
create_partition()для получения дополнительной информации. По умолчанию — ‘none’.
constraint_colsМассивная колонка, которая перечисляет колонки для применения дополнительных ограничений. Смотрите раздел "Об этом" для получения дополнительной информации о том, как работает эта функция.
optimize_constraintУправляет тем, на какие старые таблицы будут наложены дополнительные ограничения, если это настроено. Это значение определяет количество дочерних таблиц, отсчитываемых назад от самой новой дочерней таблицы, содержащей данные. Значение по умолчанию 30 означает, что ограничения будут созданы на дочерней таблице, которая находится на 30 позиций позади самой новой дочерней таблицы, содержащей данные. Дополнительную информацию смотрите в разделе О разделе.
infinite_time_partitionsПо умолчанию, новые секции в наборе, основанном на времени, не будут создаваться, если новые данные не будут вставлены, чтобы не создавать бесконечное количество пустых таблиц.
Если вы все еще хотите создать новые секции, несмотря на отсутствие новых данных, установите это значение на TRUE.
По умолчанию установлено значение false.
datetime_stringДля секционирования по времени используется строка формата даты и времени при именовании дочерних секций.
jobmonБулево значение, определяющее, используется ли расширение
pg_jobmonдля ведения журнала/мониторинга обслуживания секций. По умолчанию установлено значение true.
sub_partition_set_fullЛогическое значение, указывающее, что последняя партиция для набора субпартиций была создана. Позволяет run_maintenance() работать более эффективно, когда имеется большое количество наборов субпартиций.
undo_in_progressУстанавливается функциями undo_partition при каждом их запуске. Если значение true, то это приводит к остановке всех операций создания и управления хранением секций функцией
run_maintenance()). По умолчанию значение false.
inherit_privilegesУстанавливает, наследовать ли права собственности/привилегии родительской таблицы для всех дочерних таблиц. По умолчанию false и должно быть необходимо только в том случае, если вам нужен прямой доступ к дочерним таблицам, минуя родительскую таблицу.
constraint_validЛогическое значение, которое позволяет создавать дополнительные ограничения, которые pg_partman может управлять за вас, как НЕДЕЙСТВИТЕЛЬНЫЕ. См. раздел "Исключение по ограничению" в начале для получения более подробной информации об этих ограничениях. Это может позволить выполнять обслуживание намного быстрее на больших наборах секций, поскольку существующие данные не проверяются перед добавлением ограничения. Новые вставленные данные проверяются, поэтому это полностью безопасный вариант для обеспечения целостности данных. Обратите внимание, что исключение по ограничению НЕ БУДЕТ работать, пока ограничения не будут проверены. По умолчанию установлено значение true, чтобы ограничения создавались как ДЕЙСТВИТЕЛЬНЫЕ (VALID). Установите значение false, чтобы новые ограничения создавались как НЕДЕЙСТВИТЕЛЬНЫЕ (NOT VALID).
ignore_default_dataПо умолчанию обслуживание игнорирует данные в таблице по умолчанию при определении необходимости создания новой дочерней таблицы. Это означает, что если данные находятся в таблице по умолчанию и новая дочерняя таблица будет содержать эти данные, будет выдана ошибка. Если необходимо, чтобы обслуживание учитывало данные в таблице по умолчанию для устранения проблемы обслуживания, этот параметр можно установить в значение false. Обратите внимание, что это может привести к появлению пробелов в покрытии дочерних таблиц, что может усугубить ситуацию с попаданием данных в таблицу по умолчанию, поэтому не следует оставлять этот параметр включённым после устранения проблем обслуживания.
maintenance_orderЦелочисленное значение, определяющее порядок, в котором обслуживание будет выполняться для наборов секций. Наборы будут выполняться в порядке возрастания числового значения.
Значение по умолчанию — NULL. Все наборы разделов, установленные в NULL, будут выполняться после наборов разделов с определённым значением. Наборы разделов с NULL выполняются в неопределённом порядке.
Для наборов с подчинённым секционированием дочерние таблицы по умолчанию наследуют порядок своих родительских таблиц. Дочерние родительские таблицы будут выполняться в логическом порядке при запуске обслуживания их родительской таблицы, если оставить значение по умолчанию.
retention_keep_publication
Если
retention_keep_tableустановлен в значение true, чтобы таблицы не удалялись полностью во время хранения, по умолчанию они будут удалены из любой публикации, частью которой они являются. Если вы хотите сохранить отсоединённые таблицы в публикации старого набора секций, установите это значение в true.Значение по умолчанию — false
maintenance_last_run
Временная метка последнего успешного выполнения обслуживания для этого набора секций. Может быть полезна в качестве метрики мониторинга, чтобы убедиться, что обслуживание секций выполняется корректно.
async_partitioning_in_progress
Этот столбец используется для отслеживания того, был ли запущен асинхронный процесс секционирования. Это текстовое поле, которое содержит значение, связанное с последним обработанным блоком данных. Если значение НЕ NULL, все регулярные операции обслуживания для этой таблицы будут приостановлены до успешного завершения асинхронного процесса секционирования. Для получения дополнительной информации смотрите
partition_data_async().
part_config_sub
Хранит все данные конфигурации для наборов подсекций, управляемых
pg_partman.Столбец
sub_parentявляется родительской таблицей набора подсекций, и все остальные столбцы определяют, как дочерние элементы этого родителя разделяются на подсекции.Все остальные столбцы работают точно так же, как их аналоги в таблице
part_configили как параметры, передаваемые вcreate_partition().
F.54.5.3.5. Скрипты #
Если расширение было установлено с использованием make, следующие скриптовые файлы должны были быть установлены в бинарный каталог Tantor SE.
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.--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.
Выполняет очистку (vacuum) всех дочерних таблиц в заданном наборе секций, у которых возраст (age(relfrozenxid)) превышает
vacuum_freeze_min_age, включая родительскую таблицу.Настоятельно рекомендуется регулярно запускать этот скрипт с опцией
--freeze, если у вас есть дочерние таблицы, в которые не происходит записей после определённого периода времени.--parent (-p): Родительская таблица уже созданного набора секций. (Обязательно)--connection (-c): Строка подключения для использования с psycopg. По умолчанию “host=” (локальный сокет).--freeze (-z): Устанавливает опцию FREEZE для команды VACUUM.--full (-f): Устанавливает опцию FULL для команды VACUUM. Обратите внимание, что--freezeне требуется, если вы установили эту опцию. Рекомендуется просмотреть--dryrunперед запуском, так как эта опция заблокирует все таблицы, к которым применяется, возможно, включая родительскую таблицу.--vacuum_freeze_min_age (-a): По умолчанию скрипт получает это значение из системных каталогов. Установив этот параметр, вы можете переопределить значение, полученное из базы данных. Обратите внимание, что это не изменяет значение в базе данных, а только значение, используемое этим скриптом.--noparent: Обычно родительская таблица включается в список таблиц для очистки VACUUM, если значение age(relfrozenxid) превышает vacuum_freeze_min_age. Установите этот параметр, чтобы принудительно исключить родительскую таблицу, даже если она соответствует этому критерию.--dryrun: Показывает, что будет делать скрипт, без фактического выполнения его в базе данных. Настоятельно рекомендуется просмотреть это перед первым запуском.--quiet (-q): Отключает вывод всех сообщений.--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.54.6. Пример руководства по настройке нативного секционирования #
В этом руководство HowTo показано несколько примеров того, как настроить простое, одноуровневое секционирование. Оно также покажет вам несколько методов секционирования данных из таблицы, которая уже содержит данные (см. Разделение существующей таблицы) и отмены секционирования существующего набора секций (см. Отмена нативного секционирования). Для получения более подробной информации о том, что делает каждая функция и о дополнительных возможностях этого расширения, пожалуйста, см. главу Раздел F.54.5.
Примеры в этом документе предполагают, что вы используете как минимум 5.0.1 pg_partman с Tantor SE 14 или выше.
F.54.6.1. Простое разделение по времени: 1 секция в день #
Для нативного секционирования необходимо начать с родительской таблицы, которая уже настроена для секционирования нужного типа. В настоящее время pg_partman поддерживает только тип секционирования RANGE (как для времени, так и для идентификатора). Невозможно превратить несекционированную таблицу в родительскую таблицу набора секционированных таблиц, что может усложнить миграцию. В этом документе будут показаны некоторые методы управления этим процессом позже. Сейчас мы начнем с совершенно новой таблицы в этом примере. Любые неуникальные индексы также могут быть добавлены в родительскую таблицу в 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 | Compression | 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 помогает управлять этим, используя шаблонную таблицу для управления свойствами, которые в настоящее время не поддерживаются нативным секционированием. Обратите внимание, что это не решает проблему ограничения не применяемого ко всему набору секций. См. Раздел F.54.5.1.1 чтобы увидеть, какие свойства управляются шаблоном.
В этом примере мы сначала вручную создадим таблицу-шаблон, чтобы при запуске create_partition() изначально создаваемые дочерние таблицы имели первичный ключ. Если вы не предоставите таблицу-шаблон для 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_partition(
p_parent_table := 'partman_test.time_taptest_table'
, p_control := 'col3'
, p_interval := '1 day'
, 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 | Compression | 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_p20230324 FOR VALUES FROM ('2023-03-24 00:00:00-07') TO ('2023-03-25 00:00:00-07'),
partman_test.time_taptest_table_p20230325 FOR VALUES FROM ('2023-03-25 00:00:00-07') TO ('2023-03-26 00:00:00-07'),
partman_test.time_taptest_table_p20230326 FOR VALUES FROM ('2023-03-26 00:00:00-07') TO ('2023-03-27 00:00:00-07'),
partman_test.time_taptest_table_p20230327 FOR VALUES FROM ('2023-03-27 00:00:00-07') TO ('2023-03-28 00:00:00-07'),
partman_test.time_taptest_table_p20230328 FOR VALUES FROM ('2023-03-28 00:00:00-07') TO ('2023-03-29 00:00:00-07'),
partman_test.time_taptest_table_p20230329 FOR VALUES FROM ('2023-03-29 00:00:00-07') TO ('2023-03-30 00:00:00-07'),
partman_test.time_taptest_table_p20230330 FOR VALUES FROM ('2023-03-30 00:00:00-07') TO ('2023-03-31 00:00:00-07'),
partman_test.time_taptest_table_p20230331 FOR VALUES FROM ('2023-03-31 00:00:00-07') TO ('2023-04-01 00:00:00-07'),
partman_test.time_taptest_table_p20230401 FOR VALUES FROM ('2023-04-01 00:00:00-07') TO ('2023-04-02 00:00:00-07'),
partman_test.time_taptest_table_default DEFAULT
\d+ partman_test.time_taptest_table_p20230324
Table "partman_test.time_taptest_table_p20230324"
Column | Type | Collation | Nullable | Default | Storage | Compression | 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 ('2023-03-24 00:00:00-07') TO ('2023-03-25 00:00:00-07')
Partition constraint: ((col3 IS NOT NULL) AND (col3 >= '2023-03-24 00:00:00-07'::timestamp with time zone) AND (col3 < '2023-03-25 00:00:00-07'::timestamp with time zone))
Indexes:
"time_taptest_table_p20230324_pkey" PRIMARY KEY, btree (col1)
"time_taptest_table_p20230324_col3_idx" btree (col3)
Access method: heap
F.54.6.2. Простое разделение по времени с типом UUIDv7: 1 раздел в день #
Это похоже на простое разделение по времени, но с использованием идентификаторов UUIDv7.
CREATE SCHEMA IF NOT EXISTS partman_test;
CREATE TABLE partman_test.time_taptest_table
(col1 int,
col2 text default 'stuff',
col3 uuid PRIMARY KEY)
PARTITION BY RANGE (col3);
\d+ partman_test.time_taptest_table
Partitioned table "partman_test.time_taptest_table"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+---------+-----------+----------+---------------+----------+-------------+--------------+-------------
col1 | integer | | | | plain | | |
col2 | text | | | 'stuff'::text | extended | | |
col3 | uuid | | not null | | plain | | |
Partition key: RANGE (col3)
Indexes:
"time_taptest_table_pkey" PRIMARY KEY, btree (col3)
Number of partitions: 0
В этом примере мы используем col3 в качестве ключа секционирования и встроенные функции кодирования/декодирования uuidv7 для обеспечения секционирования по времени на col3. Поскольку col3 является ключом секционирования, индекс первичного ключа автоматически наследуется дочерними секциями.
SELECT partman.create_partition(
p_parent_table := 'partman_test.time_taptest_table'
, p_control := 'col3'
, p_interval := '1 day'
, p_time_encoder := 'partman.uuid7_time_encoder'
, p_time_decoder := 'partman.uuid7_time_decoder'
);
create_parent
---------------
t
(1 row)
\d+ partman_test.time_taptest_table
--------+---------+-----------+----------+---------------+----------+-------------+--------------+-------------
col1 | integer | | | | plain | | |
col2 | text | | | 'stuff'::text | extended | | |
col3 | uuid | | not null | | plain | | |
Partition key: RANGE (col3)
Indexes:
"time_taptest_table_pkey" PRIMARY KEY, btree (col3)
Partitions: partman_test.time_taptest_table_p20240813 FOR VALUES FROM ('019147da-b040-0000-0000-000000000000') TO ('01914d01-0c40-0000-0000-000000000000'),
partman_test.time_taptest_table_p20240814 FOR VALUES FROM ('01914d01-0c40-0000-0000-000000000000') TO ('01915227-6840-0000-0000-000000000000'),
partman_test.time_taptest_table_p20240815 FOR VALUES FROM ('01915227-6840-0000-0000-000000000000') TO ('0191574d-c440-0000-0000-000000000000'),
partman_test.time_taptest_table_p20240816 FOR VALUES FROM ('0191574d-c440-0000-0000-000000000000') TO ('01915c74-2040-0000-0000-000000000000'),
partman_test.time_taptest_table_p20240817 FOR VALUES FROM ('01915c74-2040-0000-0000-000000000000') TO ('0191619a-7c40-0000-0000-000000000000'),
partman_test.time_taptest_table_p20240818 FOR VALUES FROM ('0191619a-7c40-0000-0000-000000000000') TO ('019166c0-d840-0000-0000-000000000000'),
partman_test.time_taptest_table_p20240819 FOR VALUES FROM ('019166c0-d840-0000-0000-000000000000') TO ('01916be7-3440-0000-0000-000000000000'),
partman_test.time_taptest_table_p20240820 FOR VALUES FROM ('01916be7-3440-0000-0000-000000000000') TO ('0191710d-9040-0000-0000-000000000000'),
partman_test.time_taptest_table_p20240821 FOR VALUES FROM ('0191710d-9040-0000-0000-000000000000') TO ('01917633-ec40-0000-0000-000000000000'),
partman_test.time_taptest_table_default DEFAULT
\d+ partman_test.time_taptest_table_p20240813
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+---------+-----------+----------+---------------+----------+-------------+--------------+-------------
col1 | integer | | | | plain | | |
col2 | text | | | 'stuff'::text | extended | | |
col3 | uuid | | not null | | plain | | |
Partition of: partman_test.time_taptest_table FOR VALUES FROM ('019147da-b040-0000-0000-000000000000') TO ('01914d01-0c40-0000-0000-000000000000')
Partition constraint: ((col3 IS NOT NULL) AND (col3 >= '019147da-b040-0000-0000-000000000000'::uuid) AND (col3 < '01914d01-0c40-0000-0000-000000000000'::uuid))
Indexes:
"time_taptest_table_p20240813_pkey" PRIMARY KEY, btree (col3)
Access method: heap
F.54.6.3. Простое разделение по времени с типом text: 1 раздел в день #
Это похоже на простое разделение по времени, но с использованием управляющего столбца типа text.
В этом примере мы будем считать, что col3 содержит идентификаторы в формате INVYYYYMMDD, где INV — это статический префикс, определяемый приложением, а остальная часть — компонент метки времени.
CREATE SCHEMA IF NOT EXISTS partman_test;
CREATE TABLE partman_test.time_taptest_table
(col1 int,
col2 text default 'stuff',
col3 text PRIMARY KEY)
PARTITION BY RANGE (col3);
\d+ partman_test.time_taptest_table
Partitioned table "partman_test.time_taptest_table"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+---------+-----------+----------+---------------+----------+-------------+--------------+-------------
col1 | integer | | | | plain | | |
col2 | text | | | 'stuff'::text | extended | | |
col3 | text | | not null | | extended | | |
Partition key: RANGE (col3)
Indexes:
"time_taptest_table_pkey" PRIMARY KEY, btree (col3)
Number of partitions: 0
Для того чтобы использовать col3 в качестве управляющего столбца для временного партиционирования, необходимо определить функции кодирования/декодирования, которые укажут pg_partman, как извлекать временную информацию из идентификатора.
CREATE FUNCTION public.encode_timestamp(p_timestamp timestamptz, OUT encoded text)
RETURNS text
LANGUAGE plpgsql STABLE
AS $$
BEGIN
SELECT concat('INV', to_char(p_timestamp, 'YYYYMMDD')) INTO encoded;
END
$$;
CREATE FUNCTION public.decode_timestamp(p_str text, OUT ts timestamptz)
RETURNS TIMESTAMPTZ
LANGUAGE plpgsql STABLE
AS $$
BEGIN
SELECT substr(p_str, 4) INTO ts;
END
$$;
SELECT partman.create_partition(
p_parent_table := 'partman_test.time_taptest_table'
, p_control := 'col3'
, p_interval := '1 day'
, p_time_encoder := 'public.encode_timestamp'
, p_time_decoder := 'public.decode_timestamp'
);
create_parent
---------------
t
(1 row)
Partitioned table "partman_test.time_taptest_table"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+---------+-----------+----------+---------------+----------+-------------+--------------+-------------
col1 | integer | | | | plain | | |
col2 | text | | | 'stuff'::text | extended | | |
col3 | text | | not null | | extended | | |
Partition key: RANGE (col3)
Indexes:
"time_taptest_table_pkey" PRIMARY KEY, btree (col3)
Partitions: time_taptest_table_p20240815 FOR VALUES FROM ('INV20240815') TO ('INV20240816'),
time_taptest_table_p20240816 FOR VALUES FROM ('INV20240816') TO ('INV20240817'),
time_taptest_table_p20240817 FOR VALUES FROM ('INV20240817') TO ('INV20240818'),
time_taptest_table_p20240818 FOR VALUES FROM ('INV20240818') TO ('INV20240819'),
time_taptest_table_p20240819 FOR VALUES FROM ('INV20240819') TO ('INV20240820'),
time_taptest_table_p20240820 FOR VALUES FROM ('INV20240820') TO ('INV20240821'),
time_taptest_table_p20240821 FOR VALUES FROM ('INV20240821') TO ('INV20240822'),
time_taptest_table_p20240822 FOR VALUES FROM ('INV20240822') TO ('INV20240823'),
time_taptest_table_p20240823 FOR VALUES FROM ('INV20240823') TO ('INV20240824'),
time_taptest_table_default DEFAULT
\d+ partman_test.time_taptest_table_p20240815
Table "partman_test.time_taptest_table_p20240815"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+---------+-----------+----------+---------------+----------+-------------+--------------+-------------
col1 | integer | | | | plain | | |
col2 | text | | | 'stuff'::text | extended | | |
col3 | text | | not null | | extended | | |
Partition of: time_taptest_table FOR VALUES FROM ('INV20240815') TO ('INV20240816')
Partition constraint: ((col3 IS NOT NULL) AND (col3 >= 'INV20240815'::text) AND (col3 < 'INV20240816'::text))
Indexes:
"time_taptest_table_p20240815_pkey" PRIMARY KEY, btree (col3)
Access method: heap
F.54.6.4. Простой серийный ID: 1 секция на 10 значений ID #
Для этого случая таблица-шаблон не создаётся вручную
перед вызовом create_partition(). Это показывает,
что если первичный или уникальный ключ добавляется позже, он не применяется
к уже существующим дочерним таблицам. Это придётся
делать вручную.
CREATE TABLE partman_test.id_taptest_table (
col1 bigint not null
, col2 text
, col3 timestamptz DEFAULT now() not null
, 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 | Compression | Stats target | Description
--------+--------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
col1 | bigint | | not null | | plain | | |
col2 | text | | | | extended | | |
col3 | timestamp with time zone | | not null | now() | plain | | |
col4 | text | | | | extended | | |
Partition key: RANGE (col1)
Indexes:
"id_taptest_table_col1_idx" btree (col1)
Number of partitions: 0
SELECT partman.create_partition(
p_parent_table := 'partman_test.id_taptest_table'
, p_control := 'col1'
, p_interval := '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 | Compression | Stats target | Description
--------+--------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
col1 | bigint | | not null | | plain | | |
col2 | text | | | | extended | | |
col3 | timestamp with time zone | | not null | 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
(1 row)
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 | Compression | Stats target | Description
--------+--------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
col1 | bigint | | not null | | plain | | |
col2 | text | | | | extended | | |
col3 | timestamp with time zone | | not null | 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 и ранее).
keith=# \d partman_test.id_taptest_table_p40
Table "partman_test.id_taptest_table_p40"
Column | Type | Collation | Nullable | Default
--------+--------------------------+-----------+----------+---------
col1 | bigint | | not null |
col2 | text | | |
col3 | timestamp with time zone | | not null | 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)
keith=# \d partman_test.id_taptest_table_p50
Table "partman_test.id_taptest_table_p50"
Column | Type | Collation | Nullable | Default
--------+--------------------------+-----------+----------+---------
col1 | bigint | | not null |
col2 | text | | not null |
col3 | timestamp with time zone | | not null | 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)
keith=# \d partman_test.id_taptest_table_p60
Table "partman_test.id_taptest_table_p60"
Column | Type | Collation | Nullable | Default
--------+--------------------------+-----------+----------+---------
col1 | bigint | | not null |
col2 | text | | not null |
col3 | timestamp with time zone | | not null | 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.54.6.5. Секционирование существующей таблицы #
Секционирование существующей таблицы с использованием нативного секционирования не так просто, как просто изменение таблицы. Как указано выше, вы не можете превратить уже существующую таблицу в родительскую таблицу нативного набора секций. Родительская таблица нативно секционированной таблицы должна быть объявлена секционированной в момент ее создания. Однако, существуют методы, позволяющие взять существующую таблицу и секционировать ее нативно. Два из них представлены ниже.
Один из советов, который может помочь ускорить процесс или снизить нагрузку при разбиении таблицы на секции, — это выполнить команду CLUSTER над исходной таблицей, используя индекс по ключу секционирования, незадолго до начала разбиения. Поскольку данные будут считываться последовательно из исходной таблицы, их упорядоченность может повысить эффективность ввода-вывода на очень больших таблицах. Учтите, что при этом на таблицу накладывается эксклюзивная блокировка доступа, однако вы можете выполнять эту операцию по одной дочерней таблице за раз. См. документацию по Tantor SE — CLUSTER.
F.54.6.5.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_partition(). На этот раз мы сделаем интервал немного больше. Также убедитесь, что вы применили ко
всей новой таблице те же исходные свойства, что были у старой таблицы: права доступа, ограничения, значения по умолчанию,
индексы и т.д. Особенно важно удостовериться, что права доступа совпадают, чтобы все пользователи таблицы могли продолжить
работу после преобразования.
Обратите внимание, что первичные ключи/уникальные индексы не могут быть применены к родительской секции, если ключ секционирования не является его частью. В этом случае это будет работать, однако, вероятно, это не намерение, так как это означало бы, что разрешена только одна строка на значение, и это означало бы, что в каждой дочерней таблице может существовать только 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_partition(
p_parent_table := 'public.original_table'
, p_control := 'col1'
, p_interval := '10000'
);
\d+ original_table;
Partitioned table "public.original_table"
Column | Type | Collation | Nullable | Default | Storage | Compression | 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)
"original_table_col1_idx2" 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 (что
мы увидим на следующем примере).
keith=# CALL partman.partition_data_proc(
p_parent_table := 'public.original_table'
, p_loop_count := 200
, p_interval := '1000'
, p_source_table := 'public.old_nonpartitioned_table'
);
NOTICE: Loop: 1, Rows moved: 1000
NOTICE: Loop: 2, Rows moved: 1000
NOTICE: Loop: 3, Rows moved: 1000
NOTICE: Loop: 4, Rows moved: 1000
NOTICE: Loop: 5, Rows moved: 1000
NOTICE: Loop: 6, Rows moved: 1000
NOTICE: Loop: 7, Rows moved: 1000
NOTICE: Loop: 8, Rows moved: 1000
NOTICE: Loop: 9, Rows moved: 1000
NOTICE: Loop: 10, Rows moved: 999
NOTICE: Loop: 11, Rows moved: 1000
NOTICE: Loop: 12, Rows moved: 1000
[...]
NOTICE: Loop: 99, Rows moved: 1000
NOTICE: Loop: 100, Rows moved: 1000
NOTICE: Loop: 101, Rows moved: 1
NOTICE: Total rows moved: 100000
NOTICE: Ensure to VACUUM ANALYZE the parent (and source table if used) after partitioning data
VACUUM ANALYZE public.original_table;
Снова, выполнение коммитов в меньших партиях, как это, может избежать транзакций с огромным количеством строк и длительным временем выполнения, когда вы разбиваете таблицу, которая может содержать миллиарды строк. Всегда рекомендуется избегать длительных транзакций, чтобы позволить процессу автovacуума Tantor SE работать эффективно для остальной части базы данных. Однако выполнение меньших партий за цикл может привести к тому, что процесс разбивки данных займет больше времени. Вам придется найти баланс между нагрузкой на вашу базу данных и требуемым временем.
Использование ПРОЦЕДУРЫ 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 | Compression | 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_idx" 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.54.6.5.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_partition(). Мы будем использовать
исходную таблицу в качестве основы и дадим ей похожее имя,
чтобы это имело смысл после последующей смены имён.
Еще одно важное замечание заключается в том, что мы изменили столбец 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_partition(), уже существующие дочерние таблицы не будут иметь этот индекс, и вам придётся добавить его вручную. Тем не менее, все новые дочерние таблицы, созданные после этого, будут иметь этот индекс.
Сложность здесь заключается в том, что мы пока не можем иметь дочерние таблицы в наборе секций, которые соответствуют данным, уже существующим в исходной таблице. Это связано с тем, что мы собираемся добавить старую таблицу в качестве таблицы по умолчанию (DEFAULT) в нашу новую секционированную таблицу. Если таблица по умолчанию содержит какие-либо данные, которые соответствуют ограничениям текущей дочерней таблицы, Tantor SE не позволит добавить такую таблицу. Поэтому в приведённом ниже вызове create_partition() мы собираемся начать набор секций с диапазона, значительно опережающего уже вставленные данные, и отключить автоматическое создание таблицы по умолчанию. В вашем случае вам нужно будет проанализировать ваш текущий набор данных и выбрать значение, значительно превышающее текущий рабочий набор данных, который может быть вставлен до того, как вы сможете выполнить процесс смены имени таблицы, описанный ниже. Мы также устанавливаем значение premake на низкое значение, чтобы избежать необходимости переименовывать слишком много дочерних таблиц позже. Позже мы увеличим premake обратно до значения по умолчанию (или вы можете установить его на любое необходимое вам значение).
SELECT min(col3), max(col3) FROM original_table;
min | max
-------------------------------+-------------------------------
2023-03-21 11:09:31.980586-07 | 2023-03-28 11:09:31.980586-07
SELECT partman.create_partition(
p_parent_table := 'public.new_partitioned_table'
, p_control := 'col3'
, p_interval := '1 day'
, p_template_table:= 'public.original_table_template'
, p_premake := 1
, p_start_partition := (CURRENT_TIMESTAMP+'2 days'::interval)::text
, p_default_table := false
);
Состояние новой секционированной таблицы теперь должно выглядеть примерно так. Текущая дата, когда был написан этот HowTo, дана для справки:
SELECT CURRENT_TIMESTAMP;
current_timestamp
-------------------------------
2023-03-28 11:23:55.971402-07
\d+ new_partitioned_table;
Partitioned table "public.new_partitioned_table"
Column | Type | Collation | Nullable | Default | Storage | Compression | 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_p20230330 FOR VALUES FROM ('2023-03-30 00:00:00-07') TO ('2023-03-31 00:00:00-07')
Вам нужно будет обновить таблицу 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
Следующий шаг, который на самом деле представляет собой несколько шагов в одной транзакции, - это единственное значительное прерывание, которое нужно предвидеть.
НАЧАЛО транзакции
Возьмите эксклюзивную блокировку на исходной таблице и новой таблице, чтобы гарантировать отсутствие промежутков, в которых данные могут быть неправильно направлены
Если используется столбец IDENTITY, получите исходное последнее значение
Переименуйте исходную таблицу в имя таблицы DEFAULT для набора секций
Если используется столбец IDENTITY, удалите IDENTITY из старой таблицы
Переименуйте новую таблицу в имя исходной таблицы и переименуйте дочерние таблицы и последовательность, чтобы они соответствовали.
Если используется столбец IDENTITY, сбросьте идентификатор новой таблицы до последнего значения, чтобы любые новые вставки продолжились с того места, где остановилась последовательность старой таблицы.
Добавьте исходную таблицу как DEFAULT для набора секций
COMMIT транзакция
Если вы используете столбец IDENTITY, важно получить его последнее значение, пока исходная таблица заблокирована и ПЕРЕД тем, как вы удалите старую идентичность. Затем используйте это возвращенное значение в операторе для СБРОСА столбца IDENTITY в новой таблице. Возможно, вам потребуется немного увеличить возвращенное значение, чтобы опередить текущее использование таблицы, поэтому это может быть немного сложно с очень загруженной таблицей, особенно если ваши значения последовательности должны оставаться непрерывными. Запрос для получения этого значения приведен в SQL-операторах ниже. Если вы не используете IDENTITY, вы можете просто игнорировать эти шаги.
Если в любой момент возникнет проблема с одним из этих мини-шагов, просто выполните ROLLBACK и вы сможете вернуться к предыдущему состоянию и ваша исходная таблица должна работать так, как раньше.
BEGIN; LOCK TABLE public.original_table IN ACCESS EXCLUSIVE MODE; LOCK TABLE public.new_partitioned_table IN ACCESS EXCLUSIVE MODE; SELECT max(col1)+1 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_p20230330 RENAME TO original_table_p20230330; -- 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_loop_count := 200);
NOTICE: Loop: 1, Rows moved: 134
NOTICE: Loop: 2, Rows moved: 288
NOTICE: Loop: 3, Rows moved: 288
NOTICE: Loop: 4, Rows moved: 288
NOTICE: Loop: 5, Rows moved: 288
NOTICE: Loop: 6, Rows moved: 288
NOTICE: Loop: 7, Rows moved: 288
NOTICE: Loop: 8, Rows moved: 155
NOTICE: Total rows moved: 2017
NOTICE: Ensure to VACUUM ANALYZE the parent (and source table if used) after partitioning data
VACUUM ANALYZE original_table;
VACUUM
Если вы ранее использовали столбец IDENTITY с GENERATED ALWAYS, вам потребуется изменить идентификатор в секционированной таблице обратно на тот, что был до текущего значения BY DEFAULT
ALTER TABLE public.original_table ALTER col1 SET GENERATED ALWAYS;
Теперь дважды проверьте, что создание дочерней таблицы было выполнено как ожидалось. Здесь не хватает одного, если вы внимательно посмотрите, но мы обсудим это ниже.
\d+ original_table
Partitioned table "public.original_table"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+--------------------------+-----------+----------+------------------------------+----------+-------------+--------------+-------------
col1 | bigint | | not null | generated always 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_p20230321 FOR VALUES FROM ('2023-03-21 00:00:00-07') TO ('2023-03-22 00:00:00-07'),
original_table_p20230322 FOR VALUES FROM ('2023-03-22 00:00:00-07') TO ('2023-03-23 00:00:00-07'),
original_table_p20230323 FOR VALUES FROM ('2023-03-23 00:00:00-07') TO ('2023-03-24 00:00:00-07'),
original_table_p20230324 FOR VALUES FROM ('2023-03-24 00:00:00-07') TO ('2023-03-25 00:00:00-07'),
original_table_p20230325 FOR VALUES FROM ('2023-03-25 00:00:00-07') TO ('2023-03-26 00:00:00-07'),
original_table_p20230326 FOR VALUES FROM ('2023-03-26 00:00:00-07') TO ('2023-03-27 00:00:00-07'),
original_table_p20230327 FOR VALUES FROM ('2023-03-27 00:00:00-07') TO ('2023-03-28 00:00:00-07'),
original_table_p20230328 FOR VALUES FROM ('2023-03-28 00:00:00-07') TO ('2023-03-29 00:00:00-07'),
original_table_p20230330 FOR VALUES FROM ('2023-03-30 00:00:00-07') TO ('2023-03-31 00:00:00-07'),
original_table_default DEFAULT
И теперь, чтобы гарантировать, что все новые данные поступают в соответствующие дочерние таблицы, а не по умолчанию, выполните обслуживание новой секционированной таблицы, чтобы убедиться, что текущие предварительно созданные секции созданы
SELECT partman.run_maintenance('public.original_table');
\d+ original_table
Partitioned table "public.original_table"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+--------------------------+-----------+----------+------------------------------+----------+-------------+--------------+-------------
col1 | bigint | | not null | generated always 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_p20230321 FOR VALUES FROM ('2023-03-21 00:00:00-07') TO ('2023-03-22 00:00:00-07'),
original_table_p20230322 FOR VALUES FROM ('2023-03-22 00:00:00-07') TO ('2023-03-23 00:00:00-07'),
original_table_p20230323 FOR VALUES FROM ('2023-03-23 00:00:00-07') TO ('2023-03-24 00:00:00-07'),
original_table_p20230324 FOR VALUES FROM ('2023-03-24 00:00:00-07') TO ('2023-03-25 00:00:00-07'),
original_table_p20230325 FOR VALUES FROM ('2023-03-25 00:00:00-07') TO ('2023-03-26 00:00:00-07'),
original_table_p20230326 FOR VALUES FROM ('2023-03-26 00:00:00-07') TO ('2023-03-27 00:00:00-07'),
original_table_p20230327 FOR VALUES FROM ('2023-03-27 00:00:00-07') TO ('2023-03-28 00:00:00-07'),
original_table_p20230328 FOR VALUES FROM ('2023-03-28 00:00:00-07') TO ('2023-03-29 00:00:00-07'),
original_table_p20230330 FOR VALUES FROM ('2023-03-30 00:00:00-07') TO ('2023-03-31 00:00:00-07'),
original_table_p20230331 FOR VALUES FROM ('2023-03-31 00:00:00-07') TO ('2023-04-01 00:00:00-07'),
original_table_p20230401 FOR VALUES FROM ('2023-04-01 00:00:00-07') TO ('2023-04-02 00:00:00-07'),
original_table_default DEFAULT
До этого, в зависимости от созданных дочерних таблиц и поступающих новых данных, некоторые данные могли все еще поступать по умолчанию. Вы можете проверить это с помощью функции, которая поставляется с pg_partman:
SELECT * FROM partman.check_default(p_exact_count := true);
Если вы не передаете “true” в функцию, она просто возвращает 1 или 0, чтобы указать, есть ли какие-либо данные в любом значении по умолчанию. Это удобно для мониторинга ситуаций и это также может быть быстрее, поскольку оно прекращает проверку, как только оно находит данные в любой дочерней таблице. Однако, в этом случае мы хотим видеть точно, в какой ситуации мы находимся, поэтому передача true даст нам точное количество оставшихся строк в значении по умолчанию.
Вы также заметите, что в приведенном выше наборе отсутствует дочерняя таблица (29 марта 2023 года). Это потому, что мы установили таблицу секций на 2 дня вперед, и у нас не было данных для этой даты в исходной таблице. Вы можете исправить это одним из двух способов:
Дождитесь, пока данные за этот период времени будут вставлены, и как только вы убедитесь, что интервал завершен, разделите данные из DEFAULT таким же образом, как мы делали ранее.
Запустите функцию
partition_gap_fill()для немедленного заполнения любых пропусков:
SELECT * FROM partman.partition_gap_fill('public.original_table');
partition_gap_fill
--------------------
1
(1 row)
keith=# \d+ original_table
Partitioned table "public.original_table"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+--------------------------+-----------+----------+------------------------------+----------+-------------+--------------+-------------
col1 | bigint | | not null | generated always 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_p20230321 FOR VALUES FROM ('2023-03-21 00:00:00-07') TO ('2023-03-22 00:00:00-07'),
original_table_p20230322 FOR VALUES FROM ('2023-03-22 00:00:00-07') TO ('2023-03-23 00:00:00-07'),
original_table_p20230323 FOR VALUES FROM ('2023-03-23 00:00:00-07') TO ('2023-03-24 00:00:00-07'),
original_table_p20230324 FOR VALUES FROM ('2023-03-24 00:00:00-07') TO ('2023-03-25 00:00:00-07'),
original_table_p20230325 FOR VALUES FROM ('2023-03-25 00:00:00-07') TO ('2023-03-26 00:00:00-07'),
original_table_p20230326 FOR VALUES FROM ('2023-03-26 00:00:00-07') TO ('2023-03-27 00:00:00-07'),
original_table_p20230327 FOR VALUES FROM ('2023-03-27 00:00:00-07') TO ('2023-03-28 00:00:00-07'),
original_table_p20230328 FOR VALUES FROM ('2023-03-28 00:00:00-07') TO ('2023-03-29 00:00:00-07'),
original_table_p20230329 FOR VALUES FROM ('2023-03-29 00:00:00-07') TO ('2023-03-30 00:00:00-07'),
original_table_p20230330 FOR VALUES FROM ('2023-03-30 00:00:00-07') TO ('2023-03-31 00:00:00-07'),
original_table_p20230331 FOR VALUES FROM ('2023-03-31 00:00:00-07') TO ('2023-04-01 00:00:00-07'),
original_table_p20230401 FOR VALUES FROM ('2023-04-01 00:00:00-07') TO ('2023-04-02 00:00:00-07'),
original_table_default DEFAULT
Вы можете видеть, что создана недостающая таблица для 29 марта.
На этом этапе ваша новая секционированная таблица уже должна была использоваться и работать без каких-либо проблем!
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.54.6.6. Отмена нативного секционирования #
Так же как обычная таблица не может быть преобразована в нативно секционированную таблицу, то же самое верно и в обратном случае. Чтобы отменить нативное секционирование, необходимо переместить данные в новую таблицу. Возможно, есть способ сделать это в онлайн-режиме, но на данный момент у нас нет такого метода. Если кто-то может предложить метод или хочет, чтобы я изучил вопрос подробнее, пожалуйста, создайте запрос на 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(
p_parent_table := 'public.original_table'
, p_target_table := 'public.new_regular_table'
, p_interval := '1 hour'::text
, p_loop_count := 500
, 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: Moved 13 row(s) to the target table. Removed 0 partitions.
[...]
NOTICE: Batch: 160, Partitions undone this batch: 0, Rows undone this batch: 13
NOTICE: Moved 12 row(s) to the target table. Removed 0 partitions.
NOTICE: Batch: 161, Partitions undone this batch: 0, Rows undone this batch: 12
NOTICE: Moved 2 row(s) to the target table. Removed 1 partitions.
NOTICE: Batch: 162, Partitions undone this batch: 1, Rows undone this batch: 2
NOTICE: Moved 0 row(s) to the target table. Removed 4 partitions.
NOTICE: Total partitions undone: 13, Total rows moved: 2019
NOTICE: Ensure to VACUUM ANALYZE the old parent and target table after undo has finished
VACUUM ANALYZE original_table;
VACUUM ANALYZE new_regular_table;
Теперь имена объектов могут быть переставлены, а последовательность идентификаторов сброшена и метод изменен при необходимости. Обязательно запомните исходное значение последовательности и используйте его при сбросе.
SELECT max(col1)+1 FROM public.new_regular_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 5;
F.54.7. Миграция существующего набора секций в PG Partition Manager #
Этот документ является пособием по миграции существующего набора таблиц с нативным секционированием для использования pg_partman. Для миграции набора секций на основе триггеров на нативное секционирование, см. главу Раздел F.54.8 для получения помощи в этом процессе. Затем вы можете вернуться к этому документу, чтобы управлять этим набором секций с помощью pg_partman.
Как всегда, настоятельно рекомендуется сначала протестировать эту миграцию на системе разработки. Полный набор данных для этого не требуется, достаточно только схемы с меньшим набором данных в каждом дочернем элементе, чтобы убедиться в его правильной работе.
Следующие таблицы являются примерами, которые мы будем использовать:
CREATE TABLE tracking.hits_time (id int GENERATED BY DEFAULT AS IDENTITY NOT NULL, start timestamptz DEFAULT now() NOT NULL) PARTITION BY RANGE (start);
CREATE INDEX ON tracking.hits_time (start);
CREATE TABLE tracking.hits_time2023_02_26 partition of tracking.hits_time FOR VALUES FROM ('2023-02-26'::timestamptz) TO ('2023-03-05'::timestamptz);
CREATE TABLE tracking.hits_time2023_03_05 partition of tracking.hits_time FOR VALUES FROM ('2023-03-05'::timestamptz) TO ('2023-03-12'::timestamptz);
CREATE TABLE tracking.hits_time2023_03_12 partition of tracking.hits_time FOR VALUES FROM ('2023-03-12'::timestamptz) TO ('2023-03-19'::timestamptz);
INSERT INTO tracking.hits_time VALUES (1, generate_series('2023-02-27 01:00:00'::timestamptz, '2023-03-04 23:00:00'::timestamptz, '1 hour'::interval));
INSERT INTO tracking.hits_time VALUES (2, generate_series('2023-03-06 01:00:00'::timestamptz, '2023-03-11 23:00:00'::timestamptz, '1 hour'::interval));
INSERT INTO tracking.hits_time VALUES (3, generate_series('2023-03-12 01:00:00'::timestamptz, '2023-03-18 23:00:00'::timestamptz, '1 hour'::interval));
\d+ tracking.hits_time
Partitioned table "tracking.hits_time"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+--------------------------+-----------+----------+----------------------------------+---------+-------------+--------------+-------------
id | integer | | not null | generated by default as identity | plain | | |
start | timestamp with time zone | | not null | now() | plain | | |
Partition key: RANGE (start)
Indexes:
"hits_time_start_idx" btree (start)
Partitions: tracking.hits_time20230226 FOR VALUES FROM ('2023-02-26 00:00:00-05') TO ('2023-03-05 00:00:00-05'),
tracking.hits_time20230305 FOR VALUES FROM ('2023-03-05 00:00:00-05') TO ('2023-03-12 00:00:00-05'),
tracking.hits_time20230312 FOR VALUES FROM ('2023-03-12 00:00:00-05') TO ('2023-03-19 00:00:00-04')
CREATE TABLE tracking.hits_id (id int GENERATED BY DEFAULT AS IDENTITY NOT NULL, start timestamptz DEFAULT now() NOT NULL) PARTITION BY RANGE (id);
CREATE INDEX ON tracking.hits_id (id);
CREATE TABLE tracking.hits_id1000 partition of tracking.hits_id FOR VALUES FROM (1000) TO (2000);
CREATE TABLE tracking.hits_id2000 partition of tracking.hits_id FOR VALUES FROM (2000) TO (3000);
CREATE TABLE tracking.hits_id3000 partition of tracking.hits_id FOR VALUES FROM (3000) TO (4000);
INSERT INTO tracking.hits_id VALUES (generate_series(1000,1999), now());
INSERT INTO tracking.hits_id VALUES (generate_series(2000,2999), now());
INSERT INTO tracking.hits_id VALUES (generate_series(3000,3999), now());
\d+ tracking.hits_id
Partitioned table "tracking.hits_id"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+--------------------------+-----------+----------+----------------------------------+---------+-------------+--------------+-------------
id | integer | | not null | generated by default as identity | plain | | |
start | timestamp with time zone | | not null | now() | plain | | |
Partition key: RANGE (id)
Indexes:
"hits_id_id_idx" btree (id)
Partitions: tracking.hits_id1000 FOR VALUES FROM (1000) TO (2000),
tracking.hits_id2000 FOR VALUES FROM (2000) TO (3000),
tracking.hits_id3000 FOR VALUES FROM (3000) TO (4000)
CREATE TABLE tracking.hits_stufftime (id int GENERATED BY DEFAULT AS IDENTITY NOT NULL, start timestamptz DEFAULT now() NOT NULL) PARTITION BY RANGE (start);
CREATE INDEX ON tracking.hits_stufftime (start);
CREATE TABLE tracking.hits_stufftimeaa partition of tracking.hits_stufftime FOR VALUES FROM ('2023-01-01'::timestamptz) TO ('2023-01-08'::timestamptz);
CREATE TABLE tracking.hits_stufftimebb partition of tracking.hits_stufftime FOR VALUES FROM ('2023-01-08'::timestamptz) TO ('2023-01-15'::timestamptz);
CREATE TABLE tracking.hits_stufftimecc partition of tracking.hits_stufftime FOR VALUES FROM ('2023-01-15'::timestamptz) TO ('2023-01-22'::timestamptz);
INSERT INTO tracking.hits_stufftime VALUES (1, generate_series('2023-01-02 01:00:00'::timestamptz, '2023-01-06 23:00:00'::timestamptz, '1 hour'::interval));
INSERT INTO tracking.hits_stufftime VALUES (2, generate_series('2023-01-09 01:00:00'::timestamptz, '2023-01-13 23:00:00'::timestamptz, '1 hour'::interval));
INSERT INTO tracking.hits_stufftime VALUES (3, generate_series('2023-01-15 01:00:00'::timestamptz, '2023-01-20 23:00:00'::timestamptz, '1 hour'::interval));
\d+ tracking.hits_stufftime
Partitioned table "tracking.hits_stufftime"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+--------------------------+-----------+----------+----------------------------------+---------+-------------+--------------+-------------
id | integer | | not null | generated by default as identity | plain | | |
start | timestamp with time zone | | not null | now() | plain | | |
Partition key: RANGE (start)
Indexes:
"hits_stufftime_start_idx" btree (start)
Partitions: tracking.hits_stufftimeaa FOR VALUES FROM ('2023-01-01 00:00:00-05') TO ('2023-01-08 00:00:00-05'),
tracking.hits_stufftimebb FOR VALUES FROM ('2023-01-08 00:00:00-05') TO ('2023-01-15 00:00:00-05'),
tracking.hits_stufftimecc FOR VALUES FROM ('2023-01-15 00:00:00-05') TO ('2023-01-22 00:00:00-05')
CREATE TABLE tracking.hits_stuffid (id int GENERATED BY DEFAULT AS IDENTITY NOT NULL, start timestamptz DEFAULT now() NOT NULL) PARTITION BY RANGE (id);
CREATE INDEX ON tracking.hits_stuffid (id);
CREATE TABLE tracking.hits_stuffidaa partition of tracking.hits_stuffid FOR VALUES FROM (1000) TO (2000);
CREATE TABLE tracking.hits_stuffidbb partition of tracking.hits_stuffid FOR VALUES FROM (2000) TO (3000);
CREATE TABLE tracking.hits_stuffidcc partition of tracking.hits_stuffid FOR VALUES FROM (3000) TO (4000);
See below for data inserted
\d+ tracking.hits_stuffid
Partitioned table "tracking.hits_stuffid"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+--------------------------+-----------+----------+----------------------------------+---------+-------------+--------------+-------------
id | integer | | not null | generated by default as identity | plain | | |
start | timestamp with time zone | | not null | now() | plain | | |
Partition key: RANGE (id)
Indexes:
"hits_stuffid_id_idx" btree (id)
Partitions: tracking.hits_stuffidaa FOR VALUES FROM (1000) TO (2000),
tracking.hits_stuffidbb FOR VALUES FROM (2000) TO (3000),
tracking.hits_stuffidcc FOR VALUES FROM (3000) TO (4000)
F.54.7.1. Шаг 1 #
Отключить вызовы run_maintenance()
Если у вас есть какие-либо секции, которые в настоящее время поддерживаются pg_partman, вы можете уже вызывать это для них. Они должны быть в порядке на протяжении времени, когда выполняется это преобразование. Это сделано для избежания любых проблем с существованием только частичной конфигурации во время преобразования. Если вы используете фоновый рабочий процесс, добавление комментария к параметру “pg_partman_bgw.dbname” в postgresql.conf и затем перезагрузка (SELECT pg_reload_conf();) должны остановить его работу. Если вы запускаете pg_partman на нескольких базах данных в кластере и вы не хотите останавливать их все, можно также просто удалить ту, на которой вы выполняете миграцию, из того же параметра.
F.54.7.2. Шаг 2 #
Остановите все записи в набор секций, который переносится, если это возможно. Если вы не можете сделать это на период времени, необходимый для выполнения конверсии, все следующие шаги должны быть выполнены в одной транзакции, чтобы избежать ошибок записи из-за изменения имен таблиц.
F.54.7.3. Шаг 3 #
Переименуйте существующие секции в соответствии с новой схемой именования. pg_partman использует статический шаблон суффиксов для всех секций, как временных, так и серийных. Все суффиксы начинаются со строки "_p" и перечислены здесь для справки.
_pYYYYMMDD - All time intervals greater than 1 day _pYYYYMMDD_HH24MISS - All time intervals less than 1 day _p##### - Serial/ID partition has a suffix that is the value of the lowest possible entry in that table (Ex: _p10, _p20000, etc)
Вы можете использовать пользовательские форматы datetime_string, чтобы изменить суффикс,
который будут получать дочерние элементы с pg_partman 5.0.1 и выше, но
это не рассматривается в этом руководстве. Здесь рассматривается только то,
как перейти к использованию суффиксов по умолчанию pg_partman, и pg_partman
всегда добавляет _p в начало, чтобы
отличить границу суффикса.
F.54.7.4. Шаг 3a #
Для преобразования наборов секций на основе времени или последовательности, если у вас уже есть нижнее граничное значение в имени раздела, то это просто вопрос переименования с некоторым форматированием подстроки, поскольку это тот шаблон, который использует сам pg_partman. Предположим, ваша таблица была разделена по неделям, и ваш исходный формат содержал только первый день недели (воскресенье) в имени раздела (как в приведенном выше примере). Ниже вы можете увидеть, что у нас было 3 раздела со старым шаблоном именования “YYYYMMDD” без префикса _p. Посмотрев на приведенный выше список, вы можете увидеть новый недельный шаблон, который использует pg_partman.
Таким образом, запрос, подобный следующему, который сначала извлекает оригинальное
имя, а затем форматирует суффикс, будет работать. Он фактически не
выполняет переименование, а просто генерирует все операторы ALTER TABLE
для вас для всех дочерних таблиц в наборе. Если по какой-то причине
у всех них нет одинакового шаблона, вы можете легко просто перезапустить это,
редактируя вещи по мере необходимости, и
фильтровать полученный список операторов ALTER TABLE соответственно.
Обратите внимание, что функция to_timestamp() получает
старый шаблон строки даты и времени, а функция to_char()
получает новый шаблон строки.
SELECT format(
'ALTER TABLE %I.%I RENAME TO %I;'
, n.nspname
, c.relname
, substring(c.relname from 1 for 9) || '_p' || to_char(to_timestamp(substring(c.relname from 10), 'YYYY_MM_DD'), 'YYYYMMDD')
)
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 = 'tracking.hits_time'::regclass
ORDER BY c.relname;
?column?
-----------------------------------------------------------------------
ALTER TABLE tracking.hits_time20230226 RENAME TO hits_time_p20230226;
ALTER TABLE tracking.hits_time20230305 RENAME TO hits_time_p20230305;
ALTER TABLE tracking.hits_time20230312 RENAME TO hits_time_p20230312;
Запуск этого должен переименовать ваши таблицы, чтобы они теперь выглядели так:
table_schema | table_name --------------+--------------------- tracking | hits_time_p20230226 tracking | hits_time_p20230305 tracking | hits_time_p20230312
Если вы переносите набор секций на основе serial/id, и также используете соглашение об именах с наименьшим возможным значением, вы сделаете что-то очень похожее. Все будет так же, как и в приведенном выше примере с временными рядами, за исключением того, что переименование будет немного отличаться. Используя мою вторую примерную таблицу выше, это будет что-то вроде этого.
SELECT format(
'ALTER TABLE %I.%I RENAME TO %I;'
, n.nspname
, c.relname
, substring(c.relname from 1 for 7) || '_p' || substring(c.relname from 8)
)
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 = 'tracking.hits_id'::regclass
ORDER by c.relname;
?column?
-----------------------------------------------------------
ALTER TABLE tracking.hits_id1000 RENAME TO hits_id_p1000;
ALTER TABLE tracking.hits_id2000 RENAME TO hits_id_p2000;
ALTER TABLE tracking.hits_id3000 RENAME TO hits_id_p3000;
table_schema | table_name --------------+--------------- tracking | hits_id tracking | hits_id_p1000 tracking | hits_id_p2000 tracking | hits_id_p3000
F.54.7.5. Шаг 3b #
Если ваши секционированные наборы названы таким образом, что они по-разному относятся к содержащимся данным или вообще не относятся, вам придется переименовывать их, основываясь на наименьшем значении в контрольной колонке. Я буду использовать пример выше с суффиксами _aa, _bb и _cc.
Мы будем использовать таблицу hits_stufftime в первом примере, которая имеет дочерние таблицы, не связанные с содержащимися данными.
Этот следующий шаг использует анонимные блоки кода. Это по сути написание кода функции pl/pgsql без создания фактической функции. Просто выполните этот блок кода, настроив значения по мере необходимости, прямо в сессии psql. Обратите внимание, что в Tantor SE недели начинаются с понедельника по умолчанию для функции date_trunc. Однако, допустим, мы хотим, чтобы они начинались с воскресенья, как это было в нашем другом примере секционирования времени, чтобы сохранить последовательность. В этом случае нам нужно сделать немного дополнительных вычислений с датами, чтобы получить этот результат.
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 = 'tracking.hits_stufftime'::regclass
ORDER BY c.relname
LOOP
v_min_val := NULL;
-- 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.
-- This is a trick to have the week begin on sunday since PG defaults to monday
v_min_val := date_trunc('week', v_min_val + '1 day'::interval) - '1 day'::interval;
-- 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 14)||'_p'||to_char(v_min_val, 'YYYYMMDD'));
-- 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_stufftimeaa RENAME TO hits_stufftime_p20230101 NOTICE: ALTER TABLE tracking.hits_stufftimebb RENAME TO hits_stufftime_p20230108 NOTICE: ALTER TABLE tracking.hits_stufftimecc RENAME TO hits_stufftime_p20230115
Я бы рекомендовал запустить его хотя бы один раз с закомментированным окончательным EXECUTE для проверки того, что он генерирует. Если все выглядит хорошо, вы можете раскомментировать EXECUTE и переименовать свои таблицы!
Если у вас есть набор секций serial/id, вычисление правильного значения суффикса можно выполнить, воспользовавшись арифметикой по модулю. Предположим, что в таблице tracking.hits_stuffid следующие значения:
INSERT INTO tracking.hits_stuffid VALUES (generate_series(1100,1294), now()); INSERT INTO tracking.hits_stuffid VALUES (generate_series(2400,2991), now()); INSERT INTO tracking.hits_stuffid 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 = 'tracking.hits_stuffid'::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 12)||'_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_stuffidaa RENAME TO hits_stuffid_p1000 NOTICE: ALTER TABLE tracking.hits_stuffidbb RENAME TO hits_stuffid_p2000 NOTICE: ALTER TABLE tracking.hits_stuffidcc RENAME TO hits_stuffid_p3000
F.54.7.6. Шаг 4 #
Настройте pg_partman для управления вашим набором секций.
Я упомянул в начале, что если у вас есть текущие записи, практически все, начиная с Шага 2, должно быть выполнено в одной транзакции. Даже если вам не нужно беспокоиться о записях, я все равно настоятельно рекомендую выполнить шаг 4 в той же транзакции.
Обратите внимание, что мы используем еженедельное секционирование, но pg_partman знает об этом интервале только как о периоде в 7 дней. Допустим, мы мигрируем эту родительскую таблицу 31 марта 2023 года с уже существующими дочерними таблицами. Если мы просто вызовем функцию create_partition() без указания момента начала набора секций, она предположит, что наша неделя начинается в пятницу, что приведет к неправильным именам дочерних таблиц и границам интервалов. Мы хотим убедиться, что наш набор секций настроен так, чтобы еженедельные секции начинались в воскресенье, поэтому используем параметр p_start_partition, чтобы указать это (26 марта — это воскресенье недели, в которую входит 31 марта).
Вам может понадобиться или не понадобиться устанавливать параметр начального раздела. Все зависит от используемого интервала, поэтому, пожалуйста, протестируйте все, чтобы убедиться, что оно работает как ожидалось, перед запуском в производственной среде.
SELECT partman.create_partition('tracking.hits_time', 'start', '1 week', p_start_partition := '2023-03-26 00:00:00');
COMMIT;
Этот единичный вызов функции добавит ваш старый набор секций в конфигурацию pg_partman и, возможно, также создаст некоторые новые дочерние таблицы. pg_partman всегда поддерживает минимальное количество заранее созданных будущих секций (на основе значения premake в таблице конфигурации или как параметр функции create_partition()), поэтому если у вас их ещё нет, этот шаг также позаботится об этом. При необходимости скорректируйте параметры и ознакомьтесь с документацией для получения информации о дополнительных доступных опциях. Этот вызов соответствует временному секционированию, использованному в приведённом выше примере.
\d+ tracking.hits_time
Partitioned table "tracking.hits_time"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+--------------------------+-----------+----------+----------------------------------+---------+-------------+--------------+-------------
id | integer | | not null | generated by default as identity | plain | | |
start | timestamp with time zone | | not null | now() | plain | | |
Partition key: RANGE (start)
Indexes:
"hits_time_start_idx" btree (start)
Partitions: tracking.hits_time_p20230226 FOR VALUES FROM ('2023-02-26 00:00:00-05') TO ('2023-03-05 00:00:00-05'),
tracking.hits_time_p20230305 FOR VALUES FROM ('2023-03-05 00:00:00-05') TO ('2023-03-12 00:00:00-05'),
tracking.hits_time_p20230312 FOR VALUES FROM ('2023-03-12 00:00:00-05') TO ('2023-03-19 00:00:00-04'),
tracking.hits_time_p20230326 FOR VALUES FROM ('2023-03-26 00:00:00-04') TO ('2023-04-02 00:00:00-04'),
tracking.hits_time_p20230402 FOR VALUES FROM ('2023-04-02 00:00:00-04') TO ('2023-04-09 00:00:00-04'),
tracking.hits_time_p20230409 FOR VALUES FROM ('2023-04-09 00:00:00-04') TO ('2023-04-16 00:00:00-04'),
tracking.hits_time_p20230416 FOR VALUES FROM ('2023-04-16 00:00:00-04') TO ('2023-04-23 00:00:00-04'),
tracking.hits_time_p20230423 FOR VALUES FROM ('2023-04-23 00:00:00-04') TO ('2023-04-30 00:00:00-04'),
tracking.hits_time_default DEFAULT
По умолчанию значение premake равно 4, поэтому создается 4 недели в будущем. И поскольку это было первоначальное создание, также создаются 4 таблицы в прошлом. Хотя у нас уже были некоторые таблицы, которые покрывали эти прошлые 4 недели, pg_partman увидел, что они там есть, и просто продолжил работу. Если бы мы не передали параметр p_start_partition, Tantor SE на самом деле выдал бы ошибку, так как прошлые таблицы с другими границами, которые он попытался бы создать, пересекли бы границу уже существующей старой таблицы.
Этот последний шаг абсолютно одинаков независимо от типа или интервала партиционирования, поэтому, как только вы дойдете до этого момента, выполните COMMIT, и все готово! Запланируйте выполнение функции run_maintenance() (либо через cron, либо через BGW), и будущая поддержка партиционирования будет выполняться автоматически. Ознакомьтесь с главой Раздел F.54.5 для дополнительных параметров конфигурации.
Я привел примеры времени здесь с набором еженедельного секционирования, начинающегося в воскресенье, чтобы показать, что могут быть некоторые нюансы при вашей миграции на pg_partman, которые нужно учитывать. Если вы делаете что-то более простое, например, ежедневные или серийные целочисленные значения, это может быть не так сложно. Или если вы делаете что-то более сложное, например, интервал в 9 недель, может быть еще больше нюансов, которые могут не казаться очевидными на первый взгляд без обширного тестирования в разработке. Поэтому, пожалуйста, тщательно планируйте свою миграцию.
Если у вас возникли проблемы с этим документом по миграции, пожалуйста, создайте проблему на Github.
F.54.8. Миграция с триггерного разбиения на нативное декларативное разбиение #
Этот документ охватывает, как мигрировать набор секций, используя старый метод триггеров/наследования/ограничений, к набору секций, используя нативные функции, найденные в Tantor SE. Этот документ предполагает, что вы используете как минимум Tantor SE 14. Это не мигрирует набор секций полностью для использования pg_partman, это просто предоставляет общее руководство для процесса триггер->нативный. Пожалуйста, смотрите главу Раздел F.54.7 для миграции ваших наборов секций в pg_partman.
Для разделения на подсекции вы должны быть в состоянии следовать всем тем же процессам, описанным здесь, но вам придется работать с самого нижнего уровня вверх и выполнять миграцию на каждом суб-родителе до самого верхнего уровня родителя.
Как всегда, настоятельно рекомендуется сначала протестировать эту миграцию на системе разработки. Полный набор данных для этого не требуется, достаточно только схемы с меньшим набором данных в каждом дочернем элементе, чтобы убедиться в его правильной работе.
Вот как выглядит наш набор секций перед миграцией:
\d+ partman_test.time_taptest_table
Table "partman_test.time_taptest_table"
Column | Type | Collation | Nullable | Default | Storage | Compression | 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_p2023_03_26,
partman_test.time_taptest_table_p2023_03_27,
partman_test.time_taptest_table_p2023_03_28,
partman_test.time_taptest_table_p2023_03_29,
partman_test.time_taptest_table_p2023_03_30,
partman_test.time_taptest_table_p2023_03_31,
partman_test.time_taptest_table_p2023_04_01,
partman_test.time_taptest_table_p2023_04_02,
partman_test.time_taptest_table_p2023_04_03,
partman_test.time_taptest_table_p2023_04_04,
partman_test.time_taptest_table_p2023_04_05,
partman_test.time_taptest_table_p2023_04_06,
partman_test.time_taptest_table_p2023_04_07,
partman_test.time_taptest_table_p2023_04_08,
partman_test.time_taptest_table_p2023_04_09
Access method: heap
Если ваш набор секций на основе триггеров управляется
pg_partman версии до 5.0.0, лучше удалить его из
управления partman. Это можно сделать, удалив его из
part_config и
part_config_sub таблиц (если есть подсекции,
убедитесь, что все дочерние таблицы также удалены). После того как он будет
мигрирован на нативное секционирование, см. документ "Миграция в pg_partman",
упомянутый выше, чтобы вернуть его под управление partman.
DELETE FROM partman.part_config WHERE parent_table = 'partman_test.time_taptest_table';
Если он не управляется pg_partman и у вас есть какой-то другой метод автоматического обслуживания, убедитесь, что этот процесс отключен.
Далее нам нужно создать новую родительскую таблицу с использованием
нативного секционирования, поскольку в настоящее время нельзя преобразовать существующую таблицу
в родительскую таблицу с встроенным секционированием. Обратите внимание, что в этом случае наша исходная
таблица имела первичный ключ на col1. Поскольку
col1 не является частью ключа секции, встроенное
секционирование не позволяет нам объявить его в качестве первичного ключа на
верхнем уровне таблицы. Если вам все еще нужен этот первичный ключ,
pg_partman предоставляет шаблон таблицы, на которой можно установить это, но он
все равно не будет обеспечивать уникальность по всему набору секций,
только на основе каждого дочернего элемента, аналогично тому, как это работало до встроенного.
Пожалуйста, смотрите главу Раздел F.54.5.1.1 для получения информации о том, какие свойства могут быть установлены на родительском объекте и какие должны управляться через шаблон.
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 format('ALTER TABLE %s NO INHERIT %s;', inhrelid::regclass, inhparent::regclass)
FROM pg_inherits
WHERE inhparent::regclass = 'partman_test.time_taptest_table'::regclass;
?column?
-----------------------------------------------------------------------------------------------------
ALTER TABLE partman_test.time_taptest_table_p2023_03_26 NO INHERIT partman_test.time_taptest_table;
ALTER TABLE partman_test.time_taptest_table_p2023_03_27 NO INHERIT partman_test.time_taptest_table;
ALTER TABLE partman_test.time_taptest_table_p2023_03_28 NO INHERIT partman_test.time_taptest_table;
ALTER TABLE partman_test.time_taptest_table_p2023_03_29 NO INHERIT partman_test.time_taptest_table;
ALTER TABLE partman_test.time_taptest_table_p2023_03_30 NO INHERIT partman_test.time_taptest_table;
ALTER TABLE partman_test.time_taptest_table_p2023_03_31 NO INHERIT partman_test.time_taptest_table;
ALTER TABLE partman_test.time_taptest_table_p2023_04_01 NO INHERIT partman_test.time_taptest_table;
ALTER TABLE partman_test.time_taptest_table_p2023_04_02 NO INHERIT partman_test.time_taptest_table;
ALTER TABLE partman_test.time_taptest_table_p2023_04_03 NO INHERIT partman_test.time_taptest_table;
ALTER TABLE partman_test.time_taptest_table_p2023_04_04 NO INHERIT partman_test.time_taptest_table;
ALTER TABLE partman_test.time_taptest_table_p2023_04_05 NO INHERIT partman_test.time_taptest_table;
ALTER TABLE partman_test.time_taptest_table_p2023_04_06 NO INHERIT partman_test.time_taptest_table;
ALTER TABLE partman_test.time_taptest_table_p2023_04_07 NO INHERIT partman_test.time_taptest_table;
ALTER TABLE partman_test.time_taptest_table_p2023_04_08 NO INHERIT partman_test.time_taptest_table;
ALTER TABLE partman_test.time_taptest_table_p2023_04_09 NO INHERIT partman_test.time_taptest_table;
(15 rows)
СНОВА, НЕ ВЫПОЛНЯЙТЕ ЭТИ ЗАЯВЛЕНИЯ СЕЙЧАС. Следующий запрос не будет работать, если дочерние таблицы больше не являются частью набора наследования.
Для любых наборов секций, даже тех, которые не управляются pg_partman, следующим шагом является определение граничных значений ваших существующих дочерних таблиц и передача их в команду ATTACH PARTITION, используемую в нативном секционировании. Вам нужно будет найти метод для определения границ ваших дочерних таблиц, чтобы иметь возможность преобразовать их в синтаксис, необходимый для декларативных команд Tantor SE. В нашем случае суффиксы дочерних таблиц имеют фиксированный шаблон именования (YYYY_MM_DD), который можно разобрать для определения начального граничного значения. В этом случае нам также нужно будет знать интервал секционирования (1 день), чтобы иметь возможность рассчитать конечное время границы.
Если имена ваших дочерних таблиц не имеют подходящего шаблона, вам придется найти какой-то метод для определения границ каждой дочерней таблицы.
Снова мы можем использовать некоторый sql для генерации операторов, чтобы повторно присоединить дочерние элементы к новому родителю:
WITH child_tables AS (
SELECT
inhrelid::regclass::text AS child_tablename_safe
, relname AS child_tablename -- need unquoted name for parsing
FROM pg_inherits
JOIN pg_class c ON inhrelid = c.oid
WHERE inhparent = 'partman_test.time_taptest_table'::regclass
)
SELECT format(
'ALTER TABLE %s ATTACH PARTITION %s FOR VALUES FROM (%L) TO (%L);'
, 'partman_test.time_taptest_table_native'::regclass
, child_tablename_safe
, to_timestamp(right(x.child_tablename, 10), 'YYYY_MM_DD')
, to_timestamp(right(x.child_tablename, 10), 'YYYY_MM_DD')+'1 day'::interval
)
FROM child_tables x;
?column?
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2023_03_26 FOR VALUES FROM ('2023-03-26 00:00:00-04') TO ('2023-03-27 00:00:00-04');
ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2023_03_27 FOR VALUES FROM ('2023-03-27 00:00:00-04') TO ('2023-03-28 00:00:00-04');
ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2023_03_28 FOR VALUES FROM ('2023-03-28 00:00:00-04') TO ('2023-03-29 00:00:00-04');
ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2023_03_29 FOR VALUES FROM ('2023-03-29 00:00:00-04') TO ('2023-03-30 00:00:00-04');
ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2023_03_30 FOR VALUES FROM ('2023-03-30 00:00:00-04') TO ('2023-03-31 00:00:00-04');
ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2023_03_31 FOR VALUES FROM ('2023-03-31 00:00:00-04') TO ('2023-04-01 00:00:00-04');
ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2023_04_01 FOR VALUES FROM ('2023-04-01 00:00:00-04') TO ('2023-04-02 00:00:00-04');
ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2023_04_02 FOR VALUES FROM ('2023-04-02 00:00:00-04') TO ('2023-04-03 00:00:00-04');
ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2023_04_03 FOR VALUES FROM ('2023-04-03 00:00:00-04') TO ('2023-04-04 00:00:00-04');
ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2023_04_04 FOR VALUES FROM ('2023-04-04 00:00:00-04') TO ('2023-04-05 00:00:00-04');
ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2023_04_05 FOR VALUES FROM ('2023-04-05 00:00:00-04') TO ('2023-04-06 00:00:00-04');
ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2023_04_06 FOR VALUES FROM ('2023-04-06 00:00:00-04') TO ('2023-04-07 00:00:00-04');
ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2023_04_07 FOR VALUES FROM ('2023-04-07 00:00:00-04') TO ('2023-04-08 00:00:00-04');
ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2023_04_08 FOR VALUES FROM ('2023-04-08 00:00:00-04') TO ('2023-04-09 00:00:00-04');
ALTER TABLE partman_test.time_taptest_table_native ATTACH PARTITION partman_test.time_taptest_table_p2023_04_09 FOR VALUES FROM ('2023-04-09 00:00:00-04') TO ('2023-04-10 00:00:00-04');
(15 rows)
Теперь можно выполнить эти два набора операторов ALTER TABLE, чтобы сначала отменить их наследование от старого родителя на основе триггера и присоединить их к новому нативному родителю. После этого у старого родителя на основе триггера не должно больше быть дочерних элементов:
\d+ partman_test.time_taptest_table
Table "partman_test.time_taptest_table"
Column | Type | Collation | Nullable | Default | Storage | Compression | 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
И наш новый нативной родитель теперь должен был принять всех своих новых детей:
\d+ partman_test.time_taptest_table_native
Partitioned table "partman_test.time_taptest_table_native"
Column | Type | Collation | Nullable | Default | Storage | Compression | 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_p2023_03_26 FOR VALUES FROM ('2023-03-26 00:00:00-04') TO ('2023-03-27 00:00:00-04'),
partman_test.time_taptest_table_p2023_03_27 FOR VALUES FROM ('2023-03-27 00:00:00-04') TO ('2023-03-28 00:00:00-04'),
partman_test.time_taptest_table_p2023_03_28 FOR VALUES FROM ('2023-03-28 00:00:00-04') TO ('2023-03-29 00:00:00-04'),
partman_test.time_taptest_table_p2023_03_29 FOR VALUES FROM ('2023-03-29 00:00:00-04') TO ('2023-03-30 00:00:00-04'),
partman_test.time_taptest_table_p2023_03_30 FOR VALUES FROM ('2023-03-30 00:00:00-04') TO ('2023-03-31 00:00:00-04'),
partman_test.time_taptest_table_p2023_03_31 FOR VALUES FROM ('2023-03-31 00:00:00-04') TO ('2023-04-01 00:00:00-04'),
partman_test.time_taptest_table_p2023_04_01 FOR VALUES FROM ('2023-04-01 00:00:00-04') TO ('2023-04-02 00:00:00-04'),
partman_test.time_taptest_table_p2023_04_02 FOR VALUES FROM ('2023-04-02 00:00:00-04') TO ('2023-04-03 00:00:00-04'),
partman_test.time_taptest_table_p2023_04_03 FOR VALUES FROM ('2023-04-03 00:00:00-04') TO ('2023-04-04 00:00:00-04'),
partman_test.time_taptest_table_p2023_04_04 FOR VALUES FROM ('2023-04-04 00:00:00-04') TO ('2023-04-05 00:00:00-04'),
partman_test.time_taptest_table_p2023_04_05 FOR VALUES FROM ('2023-04-05 00:00:00-04') TO ('2023-04-06 00:00:00-04'),
partman_test.time_taptest_table_p2023_04_06 FOR VALUES FROM ('2023-04-06 00:00:00-04') TO ('2023-04-07 00:00:00-04'),
partman_test.time_taptest_table_p2023_04_07 FOR VALUES FROM ('2023-04-07 00:00:00-04') TO ('2023-04-08 00:00:00-04'),
partman_test.time_taptest_table_p2023_04_08 FOR VALUES FROM ('2023-04-08 00:00:00-04') TO ('2023-04-09 00:00:00-04'),
partman_test.time_taptest_table_p2023_04_09 FOR VALUES FROM ('2023-04-09 00:00:00-04') TO ('2023-04-10 00:00:00-04')
Далее следует поменять местами имена вашего старого родителя на основе триггера и нового родительского элемента.
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+ поддерживает функцию секции по умолчанию для захвата любых
данных, которые не имеют соответствующего дочернего элемента. Если ваши имена таблиц
особенно длинные, убедитесь, что добавление суффикса
_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;
На исходной родительской таблице был первичный ключ, но это невозможно с использованием нативного секционирования, если только первичный ключ также не включает ключ разбиения. Это обычно непрактично при разбиении на основе времени. Вы можете установить первичный ключ на каждую отдельную дочернюю таблицу, но это будет обеспечивать ограничение только для этой дочерней таблицы, а не для всего набора секций. Вы можете добавить первичный ключ к каждой отдельной таблице, используя аналогичную генерацию SQL, как выше, но если вам нужен метод для управления добавлением этих ключей к любым новым дочерним таблицам, пожалуйста, ознакомьтесь с функциями, доступными в pg_partman.
Если вы запустили этот процесс внутри транзакции, обязательно подтвердите свою работу сейчас:
COMMIT;
Это должно завершить процесс миграции. Если вы хотите мигрировать ваш набор секций для управления с помощью pg_partman, пожалуйста, смотрите главу Раздел F.54.7.
F.54.9. Руководство по обновлению до pg_partman 5.0.1 #
Предостережение
Этот документ предполагает, что все наборы секций являются нативными наборами секций. Если у вас есть наборы секций на основе триггеров, вы должны сначала мигрировать их на нативные, прежде чем выполнять какую-либо дальнейшую работу по приведению ваших наборов секций в соответствие с pg_partman 5.0.1 и выше.
F.54.9.1. Ошибка триггера во время обновления #
Если вы видите следующие ошибки во время обновления, вам придется выполнить поэтапное обновление pg_partman. Обновление должно добавить ограничения и изменить значения таблицы в одной транзакции, и Tantor SE не всегда это позволяет.
ERROR: cannot ALTER TABLE "part_config_sub" because it has pending trigger events
Или из Amazon RDS
ERROR: 42501: cannot fire deferred trigger within security-restricted operation
Сначала обновите напрямую до 5.0.0
BEGIN; ALTER EXTENSION pg_partman UPDATE TO '5.0.0'; COMMIT;
Затем НЕЗАМЕДЛИТЕЛЬНО обновитесь до 5.0.1
BEGIN; ALTER EXTENSION pg_partman UPDATE TO '5.0.1'; COMMIT;
Если вы не можете выполнить оба этих обновления в быстрой последовательности, каждое в своей отдельной транзакции, вам следует подождать до окна обслуживания, которое позволит это сделать, прежде чем обновляться до версии 5.x или выше.
F.54.9.2. Устаревшие методы разбиения #
Существует несколько схем секционирования, которые поддерживались pg_partman до версии 5.0.1, но больше не поддерживаются, а именно ISO еженедельно и ежеквартально. Обратите внимание, что все еще возможно секционирование с этими интервалами в версии 5 и выше, используя интервалы “1 неделя” или “3 месяца”. Просто специализированные версии этого секционирования, которые были сделаны ранее, больше не поддерживаются.
Ранее еженедельное разбиение можно было выполнять с использованием формата ISO недели
IYYYwIW, что приводило к суффиксам разбиения, таким как 2021w44 или 1994w32, где число
после w было номером недели этого года, и год всегда был бы выровнен по ISO
неделе. Также поддерживался метод ежеквартального разбиения с
суффиксом YYYYq#, где # был одним из 4
стандартных кварталов в году, начиная с 1 января. Метод
ежеквартального разбиения был особенно проблематичен для поддержки, так как
не все функции, основанные на времени, поддерживали его должным образом.
Так что, хотя ежеквартальное обновление было проблематично поддерживать, еженедельное работало хорошо в большинстве случаев. Но после пересмотра кода для поддержания всех различных специализированных суффиксов (всего 8), было решено упростить суффиксы, которые будут поддерживаться. Теперь есть только две возможности:
YYYYMMDDдля интервалов, равных или превышающих 1 деньYYYYMMDD_HH24MISSдля интервалов менее 1 дня (поддерживается до 1 секунды)
Поддержка пользовательских интервалов всегда была возможна с pg_partman, особенно с нативным партиционированием. Но упрощение поддерживаемых суффиксов значительно упрощает сопровождение кода. Если вы использовали любой из этих методов партиционирования, вам придется отказаться от них, чтобы использовать pg_partman 5.0.1 или выше. Миграцию можно выполнить до или после обновления, но обратите внимание, что обслуживание партиций не будет работать для этих методов в версии 5+ и будет генерировать ошибки при запуске обслуживания для этих наборов до завершения миграции.
Обратите внимание, что ранее hourly разбиение не имело секунд в суффиксе, но любые новые наборы секций, созданные с этим интервалом, будут их иметь. Нет жесткого требования к миграции, как это было для еженедельных и ежеквартальных, но будет несоответствие в именовании дочерних таблиц с любыми существующими наборами секций до версии 5.x. Если требуется согласованность, можно выполнить миграцию, аналогичную приведенным ниже примерам.
Миграция представляет собой простое переименование дочерних таблиц и обновление таблицы(таблиц) конфигурации partman, поэтому это должен быть относительно плавный процесс с минимальным временем простоя.
F.54.9.2.1. Еженедельная миграция #
Вот пример набора еженедельных секций, созданного с помощью pg_partman версии 4.7.3.
\d+ partman_test.time_taptest_table
Partitioned table "partman_test.time_taptest_table"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+--------------------------+-----------+----------+------------------------------------------------+----------+-------------+--------------+-------------
col1 | timestamp with time zone | | | | plain | | |
col2 | text | | | | extended | | |
col3 | integer | | not null | EXTRACT(epoch FROM CURRENT_TIMESTAMP)::integer | plain | | |
Partition key: RANGE (col1)
Partitions: partman_test.time_taptest_table_p2023w05 FOR VALUES FROM ('2023-01-30 00:00:00-05') TO ('2023-02-06 00:00:00-05'),
partman_test.time_taptest_table_p2023w06 FOR VALUES FROM ('2023-02-06 00:00:00-05') TO ('2023-02-13 00:00:00-05'),
partman_test.time_taptest_table_p2023w07 FOR VALUES FROM ('2023-02-13 00:00:00-05') TO ('2023-02-20 00:00:00-05'),
partman_test.time_taptest_table_p2023w08 FOR VALUES FROM ('2023-02-20 00:00:00-05') TO ('2023-02-27 00:00:00-05'),
partman_test.time_taptest_table_p2023w09 FOR VALUES FROM ('2023-02-27 00:00:00-05') TO ('2023-03-06 00:00:00-05'),
partman_test.time_taptest_table_p2023w10 FOR VALUES FROM ('2023-03-06 00:00:00-05') TO ('2023-03-13 00:00:00-04'),
partman_test.time_taptest_table_p2023w11 FOR VALUES FROM ('2023-03-13 00:00:00-04') TO ('2023-03-20 00:00:00-04'),
partman_test.time_taptest_table_p2023w12 FOR VALUES FROM ('2023-03-20 00:00:00-04') TO ('2023-03-27 00:00:00-04'),
partman_test.time_taptest_table_p2023w13 FOR VALUES FROM ('2023-03-27 00:00:00-04') TO ('2023-04-03 00:00:00-04'),
partman_test.time_taptest_table_p2023w14 FOR VALUES FROM ('2023-04-03 00:00:00-04') TO ('2023-04-10 00:00:00-04'),
partman_test.time_taptest_table_p2023w15 FOR VALUES FROM ('2023-04-10 00:00:00-04') TO ('2023-04-17 00:00:00-04'),
partman_test.time_taptest_table_p2023w16 FOR VALUES FROM ('2023-04-17 00:00:00-04') TO ('2023-04-24 00:00:00-04'),
partman_test.time_taptest_table_p2023w17 FOR VALUES FROM ('2023-04-24 00:00:00-04') TO ('2023-05-01 00:00:00-04'),
partman_test.time_taptest_table_default DEFAULT
Содержимое таблицы конфигурации выглядит следующим образом (обратите внимание, что столбцы в таблице конфигурации значительно изменились в версии 5.x и выше)
SELECT * FROM partman.part_config; -[ RECORD 1 ]--------------+------------------------------------------------- parent_table | partman_test.time_taptest_table control | col1 partition_type | native partition_interval | 7 days constraint_cols | premake | 4 optimize_trigger | 4 optimize_constraint | 30 epoch | none inherit_fk | t retention | retention_schema | retention_keep_table | t retention_keep_index | t infinite_time_partitions | f datetime_string | IYYY"w"IW automatic_maintenance | on jobmon | t sub_partition_set_full | f undo_in_progress | f trigger_exception_handling | f upsert | trigger_return_null | t template_table | partman.template_partman_test_time_taptest_table publications | inherit_privileges | f constraint_valid | t subscription_refresh | drop_cascade_fk | f ignore_default_data | f
Мы используем запрос ниже, чтобы сгенерировать SQL, который переименует таблицы в новый шаблон именования, поддерживаемый в версии 5.x и выше. Корректировки этого запроса для ваших таблиц являются
Замените имя таблицы родителя в этом запросе на ваше имя
В первой функции подстроки число после
forдолжно быть равно длине имени вашей родительской таблицы +2, чтобы учесть_p. В этом случае20Во второй функции подстроки число после
fromдолжно быть предыдущим числом +1. В этом случае21
SELECT format(
'ALTER TABLE %I.%I RENAME TO %I;'
, n.nspname
, c.relname
, substring(c.relname from 1 for 20) || to_char(to_timestamp(substring(c.relname from 21), 'IYYY"w"IW'), 'YYYYMMDD')
)
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 = 'partman_test.time_taptest_table'::regclass
AND c.relname NOT LIKE '%_default'
ORDER BY c.relname;
?column?
----------------------------------------------------------------------------------------------
ALTER TABLE partman_test.time_taptest_table_p2023w05 RENAME TO time_taptest_table_p20230130;
ALTER TABLE partman_test.time_taptest_table_p2023w06 RENAME TO time_taptest_table_p20230206;
ALTER TABLE partman_test.time_taptest_table_p2023w07 RENAME TO time_taptest_table_p20230213;
ALTER TABLE partman_test.time_taptest_table_p2023w08 RENAME TO time_taptest_table_p20230220;
ALTER TABLE partman_test.time_taptest_table_p2023w09 RENAME TO time_taptest_table_p20230227;
ALTER TABLE partman_test.time_taptest_table_p2023w10 RENAME TO time_taptest_table_p20230306;
ALTER TABLE partman_test.time_taptest_table_p2023w11 RENAME TO time_taptest_table_p20230313;
ALTER TABLE partman_test.time_taptest_table_p2023w12 RENAME TO time_taptest_table_p20230320;
ALTER TABLE partman_test.time_taptest_table_p2023w13 RENAME TO time_taptest_table_p20230327;
ALTER TABLE partman_test.time_taptest_table_p2023w14 RENAME TO time_taptest_table_p20230403;
ALTER TABLE partman_test.time_taptest_table_p2023w15 RENAME TO time_taptest_table_p20230410;
ALTER TABLE partman_test.time_taptest_table_p2023w16 RENAME TO time_taptest_table_p20230417;
ALTER TABLE partman_test.time_taptest_table_p2023w17 RENAME TO time_taptest_table_p20230424;
Обратите внимание, что все ISO недели начинаются с понедельника, поэтому это первый день недели, на основе которого будут создаваться имена дочерних таблиц. Это также совпадает с исходными границами дочерних таблиц, если вы посмотрите на них выше, поэтому имена дочерних таблиц должны совпадать с нижними границами данных, которые они содержат. Если вы хотите, чтобы ваши недели начинались с воскресенья (или какого-то другого дня), это потребует гораздо более сложной миграции самих данных и выходит за рамки данного документа.
Наконец, вам нужно обновить
datetime_string столбец в таблице part_config
на новый шаблон суффикса.
UPDATE partman.part_config SET datetime_string = 'YYYYMMDD';
Теперь мы можем проверить, работает ли это, увеличив значение premake. Значение premake по умолчанию равно 4, поэтому, если вы изменили его ранее, просто добавьте к этому значению единицу. Мы также устанавливаем infinite_time_partitions в true, потому что в наборе секций может быть недостаточно данных, чтобы вызвать создание будущих таблиц. Это можно отключить после тестирования, если это не требуется.
UPDATE partman.part_config SET premake = premake+1, infinite_time_partitions = true;
Теперь мы можем вызвать run_maintenance(), чтобы убедиться, что обслуживание секций работает должным образом
SELECT partman.run_maintenance('partman_test.time_taptest_table');
keith=# \d+ partman_test.time_taptest_table
Partitioned table "partman_test.time_taptest_table"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+--------------------------+-----------+----------+------------------------------------------------+----------+-------------+--------------+-------------
col1 | timestamp with time zone | | | | plain | | |
col2 | text | | | | extended | | |
col3 | integer | | not null | EXTRACT(epoch FROM CURRENT_TIMESTAMP)::integer | plain | | |
Partition key: RANGE (col1)
Partitions: partman_test.time_taptest_table_p20230130 FOR VALUES FROM ('2023-01-30 00:00:00-05') TO ('2023-02-06 00:00:00-05'),
partman_test.time_taptest_table_p20230206 FOR VALUES FROM ('2023-02-06 00:00:00-05') TO ('2023-02-13 00:00:00-05'),
partman_test.time_taptest_table_p20230213 FOR VALUES FROM ('2023-02-13 00:00:00-05') TO ('2023-02-20 00:00:00-05'),
partman_test.time_taptest_table_p20230220 FOR VALUES FROM ('2023-02-20 00:00:00-05') TO ('2023-02-27 00:00:00-05'),
partman_test.time_taptest_table_p20230227 FOR VALUES FROM ('2023-02-27 00:00:00-05') TO ('2023-03-06 00:00:00-05'),
partman_test.time_taptest_table_p20230306 FOR VALUES FROM ('2023-03-06 00:00:00-05') TO ('2023-03-13 00:00:00-04'),
partman_test.time_taptest_table_p20230313 FOR VALUES FROM ('2023-03-13 00:00:00-04') TO ('2023-03-20 00:00:00-04'),
partman_test.time_taptest_table_p20230320 FOR VALUES FROM ('2023-03-20 00:00:00-04') TO ('2023-03-27 00:00:00-04'),
partman_test.time_taptest_table_p20230327 FOR VALUES FROM ('2023-03-27 00:00:00-04') TO ('2023-04-03 00:00:00-04'),
partman_test.time_taptest_table_p20230403 FOR VALUES FROM ('2023-04-03 00:00:00-04') TO ('2023-04-10 00:00:00-04'),
partman_test.time_taptest_table_p20230410 FOR VALUES FROM ('2023-04-10 00:00:00-04') TO ('2023-04-17 00:00:00-04'),
partman_test.time_taptest_table_p20230417 FOR VALUES FROM ('2023-04-17 00:00:00-04') TO ('2023-04-24 00:00:00-04'),
partman_test.time_taptest_table_p20230424 FOR VALUES FROM ('2023-04-24 00:00:00-04') TO ('2023-05-01 00:00:00-04'),
partman_test.time_taptest_table_p20230501 FOR VALUES FROM ('2023-05-01 00:00:00-04') TO ('2023-05-08 00:00:00-04'),
partman_test.time_taptest_table_p20230508 FOR VALUES FROM ('2023-05-08 00:00:00-04') TO ('2023-05-15 00:00:00-04'),
partman_test.time_taptest_table_default DEFAULT
Вы можете увидеть, что обслуживание создало новые сеекции с нашим новым шаблоном именования без каких-либо проблем. Вы можете получить один или несколько в зависимости от состояния ваших дочерних таблиц на момент выполнения этого.
Если все в порядке, вы можете вернуть свои значения premake
и infinite_time_partitions к их предыдущим значениям, если это необходимо.
Если вы хотите убедиться, что любые еженедельные секционированные таблицы начинаются с понедельника при их создании, вы можете использовать функцию date_trunc() в параметре p_start_partition функции create_partition() для этого. Следующий пример показывает, как это сделать во вторник. Без указания конкретного начального раздела таким образом набор секций начинался бы во вторник, 29 августа 2023 года, и каждая последующая секция основывалась бы на неделе, начинающейся во вторник.
SELECT CURRENT_TIMESTAMP;
current_timestamp
-------------------------------
2023-08-29 13:22:08.190552-04
CREATE TABLE public.time_table (
col1 int,
col2 text,
col3 timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP)
PARTITION BY RANGE (col3);
SELECT partman.create_partition('public.time_table', 'col3', '1 week', p_start_partition := to_char(date_trunc('week',CURRENT_TIMESTAMP), 'YYYY-MM-DD HH24:MI:SS'));
\d+ public.time_table
Partitioned table "public.time_table"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+--------------------------+-----------+----------+-------------------+----------+-------------+--------------+-------------
col1 | integer | | | | plain | | |
col2 | text | | | | extended | | |
col3 | timestamp with time zone | | not null | CURRENT_TIMESTAMP | plain | | |
Partition key: RANGE (col3)
Partitions: time_table_p20230828 FOR VALUES FROM ('2023-08-28 00:00:00-04') TO ('2023-09-04 00:00:00-04'),
time_table_p20230904 FOR VALUES FROM ('2023-09-04 00:00:00-04') TO ('2023-09-11 00:00:00-04'),
time_table_p20230911 FOR VALUES FROM ('2023-09-11 00:00:00-04') TO ('2023-09-18 00:00:00-04'),
time_table_p20230918 FOR VALUES FROM ('2023-09-18 00:00:00-04') TO ('2023-09-25 00:00:00-04'),
time_table_p20230925 FOR VALUES FROM ('2023-09-25 00:00:00-04') TO ('2023-10-02 00:00:00-04'),
time_table_default DEFAULT
F.54.9.2.2. Ежеквартальное Разделение #
Аналогично еженедельному, имена дочерних таблиц просто нужно переименовать и обновить конфигурацию partman с новым datetime_string. Ниже представлена таблица, как она была с оригинальными квартальными именами
\d+ partman_test.time_taptest_table
Partitioned table "partman_test.time_taptest_table"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+--------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
col1 | integer | | | | plain | | |
col2 | text | | | | extended | | |
col3 | timestamp with time zone | | not null | now() | plain | | |
Partition key: RANGE (col3)
Partitions: partman_test.time_taptest_table_p2022q1 FOR VALUES FROM ('2022-01-01 00:00:00-05') TO ('2022-04-01 00:00:00-04'),
partman_test.time_taptest_table_p2022q2 FOR VALUES FROM ('2022-04-01 00:00:00-04') TO ('2022-07-01 00:00:00-04'),
partman_test.time_taptest_table_p2022q3 FOR VALUES FROM ('2022-07-01 00:00:00-04') TO ('2022-10-01 00:00:00-04'),
partman_test.time_taptest_table_p2022q4 FOR VALUES FROM ('2022-10-01 00:00:00-04') TO ('2023-01-01 00:00:00-05'),
partman_test.time_taptest_table_p2023q1 FOR VALUES FROM ('2023-01-01 00:00:00-05') TO ('2023-04-01 00:00:00-04'),
partman_test.time_taptest_table_p2023q2 FOR VALUES FROM ('2023-04-01 00:00:00-04') TO ('2023-07-01 00:00:00-04'),
partman_test.time_taptest_table_p2023q3 FOR VALUES FROM ('2023-07-01 00:00:00-04') TO ('2023-10-01 00:00:00-04'),
partman_test.time_taptest_table_p2023q4 FOR VALUES FROM ('2023-10-01 00:00:00-04') TO ('2024-01-01 00:00:00-05'),
partman_test.time_taptest_table_p2024q1 FOR VALUES FROM ('2024-01-01 00:00:00-05') TO ('2024-04-01 00:00:00-04'),
partman_test.time_taptest_table_default DEFAULT
Вот SQL для генерации переименований дочерних таблиц.
Обратите внимание, что это значительно сложнее, потому что квартальные
данные не работают как ожидалось с функцией
to_timestamp(). Нам придется использовать
анонимную функцию для выполнения некоторого кода PL/PQSL, чтобы сгенерировать
SQL для изменения этих имен таблиц.
Обратите внимание, что потребуется скорректировать подстроку при генерации оператора ALTER TABLE, чтобы её длина соответствовала длине имени родительской таблицы +2 (для суффикса _p). В данном случае значение равно 20.
DO $rename$
DECLARE
v_child_start_time timestamptz;
v_row record;
v_quarter text;
v_sql text;
v_suffix text;
v_suffix_position int;
v_year 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 = 'partman_test.time_taptest_table'::regclass
AND c.relname NOT LIKE '%_default'
ORDER BY c.relname
LOOP
v_suffix_position := (length(v_row.child_table) - position('p_' in reverse(v_row.child_table))) + 2;
v_suffix := substring(v_row.child_table from v_suffix_position);
v_year := split_part(v_suffix, 'q', 1);
v_quarter := split_part(v_suffix, 'q', 2);
CASE
WHEN v_quarter = '1' THEN
v_child_start_time := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD');
WHEN v_quarter = '2' THEN
v_child_start_time := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD');
WHEN v_quarter = '3' THEN
v_child_start_time := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD');
WHEN v_quarter = '4' THEN
v_child_start_time := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD');
ELSE
RAISE EXCEPTION 'Unexpected code path';
END CASE;
-- 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 20)||to_char(v_child_start_time, 'YYYYMMDD'));
RAISE NOTICE '%', v_sql;
END LOOP;
END
$rename$;
Затем обновите конфигурацию, чтобы использовать новую datetime_string. Обратите внимание, что это то же самое, что и еженедельно сейчас
UPDATE partman.part_config SET datetime_string = 'YYYYMMDD';
Оставшиеся шаги для проверки того, что все работает, такие же, как еженедельно
UPDATE partman.part_config SET premake = premake+1, infinite_time_partitions = true;
SELECT partman.run_maintenance('partman_test.time_taptest_table');
Вы должны увидеть как минимум еще одну дополнительную дочернюю таблицу
\d+ partman_test.time_taptest_table
Partitioned table "partman_test.time_taptest_table"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+--------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
col1 | integer | | | | plain | | |
col2 | text | | | | extended | | |
col3 | timestamp with time zone | | not null | now() | plain | | |
Partition key: RANGE (col3)
Partitions: partman_test.time_taptest_table_p20220101 FOR VALUES FROM ('2022-01-01 00:00:00-05') TO ('2022-04-01 00:00:00-04'),
partman_test.time_taptest_table_p20220401 FOR VALUES FROM ('2022-04-01 00:00:00-04') TO ('2022-07-01 00:00:00-04'),
partman_test.time_taptest_table_p20220701 FOR VALUES FROM ('2022-07-01 00:00:00-04') TO ('2022-10-01 00:00:00-04'),
partman_test.time_taptest_table_p20221001 FOR VALUES FROM ('2022-10-01 00:00:00-04') TO ('2023-01-01 00:00:00-05'),
partman_test.time_taptest_table_p20230101 FOR VALUES FROM ('2023-01-01 00:00:00-05') TO ('2023-04-01 00:00:00-04'),
partman_test.time_taptest_table_p20230401 FOR VALUES FROM ('2023-04-01 00:00:00-04') TO ('2023-07-01 00:00:00-04'),
partman_test.time_taptest_table_p20230701 FOR VALUES FROM ('2023-07-01 00:00:00-04') TO ('2023-10-01 00:00:00-04'),
partman_test.time_taptest_table_p20231001 FOR VALUES FROM ('2023-10-01 00:00:00-04') TO ('2024-01-01 00:00:00-05'),
partman_test.time_taptest_table_p20240101 FOR VALUES FROM ('2024-01-01 00:00:00-05') TO ('2024-04-01 00:00:00-04'),
partman_test.time_taptest_table_p20240401 FOR VALUES FROM ('2024-04-01 00:00:00-04') TO ('2024-07-01 00:00:00-04'),
partman_test.time_taptest_table_p20240701 FOR VALUES FROM ('2024-07-01 00:00:00-04') TO ('2024-10-01 00:00:00-04'),
partman_test.time_taptest_table_default DEFAULT
Вы можете при необходимости вернуть значения ваших столбцов premake и infinite_time_partitions к их исходным значениям.