|
| 1 | +#pathman |
| 2 | + |
| 3 | +Модуль`pathman` предоставляет оптимизированный механизм секционирования, а также функции для создания и управления секциями. |
| 4 | + |
| 5 | +##Концепция pathman |
| 6 | + |
| 7 | +Секционирование -- это способ разбиения одной большой таблицы на множество меньших по размеру. Для каждой записи можно однозначно определить секцию, в которой она должна храниться посредством вычисления ключа. Традиционно выделяют три стратегии секционирования: |
| 8 | + |
| 9 | +* HASH - данные равномерно распределяются по секциям в соответствии со значениями hash-функции, вычисленными по некоторому атрибуту; |
| 10 | +* RANGE - данные распределяются по секциям, каждая из которых ответственна за заданный диапазон значений аттрибута; |
| 11 | +* LIST - для каждой секции определяется набор конкретных значений атрибута. |
| 12 | + |
| 13 | +Секционирование в postgres основано на механизме наследования. Каждому наследнику задается условие CHECK CONSTRAINT. Например: |
| 14 | + |
| 15 | +``` |
| 16 | +CHECK ( id >= 100 AND id < 200 ) |
| 17 | +CHECK ( id >= 200 AND id < 300 ) |
| 18 | +``` |
| 19 | + |
| 20 | +Несмотря на гибкость, этот механизм обладает недостатками. Так при фильтрации данных оптимизатор вынужден перебирать все дочерние секции и сравнивать условие запроса с CHECK CONSTRAINT-ами секции, чтобы определить из каких секций ему следует загружать данные. При большом количестве секций это создает дополнительные накладные расходы, которые могут свести на нет выигрыш в производительности от применения секционирования. |
| 21 | + |
| 22 | +Модуль`pathman` предоставляет функции для создания и управления |
| 23 | +секциями (см. следующий раздел) и механизм секционирования, |
| 24 | +оптимизированный с учетом знания о структуре дочерних таблиц. Конфигурация сохраняется таблице`pathman_config`, каждая строка которой содержит запись для одной секционированной таблицы (название таблицы, атрибут и тип разбиения). В процессе инициализации модуля в разделяемую память сохраняется конфигурация дочерних таблиц в удобном для поиска формате. Получив запрос типа`SELECT` к секционированной таблице,`pathman` анализирует дерево условий запроса и выделяет из него условия вида: |
| 25 | + |
| 26 | +``` |
| 27 | +ПЕРЕМЕННАЯ ОПЕРАТОР КОНСТАНТА |
| 28 | +``` |
| 29 | +где`ПЕРЕМЕННАЯ` -- это атрибут, по которому было выполнено разбиение,`ОПЕРАТОР` -- оператор сравнения (поддерживаются =, <, <=, >, >=),`КОНСТАНТА` -- скалярное значение. Например: |
| 30 | + |
| 31 | +``` |
| 32 | +WHERE id = 150 |
| 33 | +``` |
| 34 | +Затем основываясь на стратегии секционирования и условиях запроса`pathman` выбирает соответствующие секции и строит план. |
| 35 | + |
| 36 | +##Installation |
| 37 | + |
| 38 | +Для установки pathman выполните в командной строке: |
| 39 | +``` |
| 40 | +CREATE SCHEMA pathman; |
| 41 | +CREATE EXTENSION pathman SCHEMA pathman; |
| 42 | +
|
| 43 | +``` |
| 44 | +Затем модифицируйте параметр shared_preload_libraries в конфигурационном файле postgres.conf: |
| 45 | +``` |
| 46 | +shared_preload_libraries = 'pathman' |
| 47 | +``` |
| 48 | +Для вступления изменений в силу потребуется перезагрузка сервера PostgreSQL. |
| 49 | + |
| 50 | +##Функции pathman |
| 51 | + |
| 52 | +###Создание секций |
| 53 | +``` |
| 54 | +create_hash_partitions( |
| 55 | + relation TEXT, |
| 56 | + attribute TEXT, |
| 57 | + partitions_count INTEGER) |
| 58 | +``` |
| 59 | +Выполняет HASH-секционирование таблицы`relation` по целочисленному полю`attribute`. Создает`partitions_count` дочерних секций, а также триггер на вставку. Данные из родительской таблицы не копируются автоматически в дочерние. Миграцию данных можно выполнить с помощью функции`partition_data()` (см. ниже), либо вручную. |
| 60 | + |
| 61 | +``` |
| 62 | +create_range_partitions( |
| 63 | + relation TEXT, |
| 64 | + attribute TEXT, |
| 65 | + start_value ANYELEMENT, |
| 66 | + interval ANYELEMENT, |
| 67 | + premake INTEGER) |
| 68 | +``` |
| 69 | +Выполняет RANGE-секционирование таблицы`relation` по полю`attribute`. Аргумент`start_value` задает начальное значение,`interval` -- диапазон значений внутри одной секции,`premake` -- количество заранее создаваемых секций (если 0, то будет создана единственная секция). |
| 70 | +``` |
| 71 | +create_range_partitions( |
| 72 | + relation TEXT, |
| 73 | + attribute TEXT, |
| 74 | + start_value ANYELEMENT, |
| 75 | + interval INTERVAL, |
| 76 | + premake INTEGER) |
| 77 | +``` |
| 78 | +Аналогично предыдущей с тем лишь отличием, что данная функция предназначена для секционирования по полю типа`DATE` или`TIMESTAMP`. |
| 79 | + |
| 80 | +###Утилиты |
| 81 | +``` |
| 82 | +partition_data(parent text) |
| 83 | +``` |
| 84 | +Копирует данные из родительской таблицы`parent` в дочерние секции. |
| 85 | +``` |
| 86 | +create_hash_update_trigger(parent TEXT) |
| 87 | +``` |
| 88 | +Создает триггер на UPDATE для HASH секций. По-умолчанию триггер на обновление данных не создается, т.к. это создает дополнительные накладные расходы. Триггер полезен только в том случае, когда меняется значение ключевого аттрибута. |
| 89 | +``` |
| 90 | +create_hash_update_trigger(parent TEXT) |
| 91 | +``` |
| 92 | +Аналогично предыдущей, но для RANGE секций. |
| 93 | + |
| 94 | +###Управление секциями |
| 95 | +``` |
| 96 | +split_range_partition(partition TEXT, value ANYELEMENT) |
| 97 | +``` |
| 98 | +Разбивает RANGE секцию`partition` на две секции по значению`value`. |
| 99 | +``` |
| 100 | +merge_range_partitions(partition1 TEXT, partition2 TEXT) |
| 101 | +``` |
| 102 | +Объединяет две смежные RANGE секции. Данные из`partition2` копируются в`partition1`, после чего секция`partition2` удаляется. |
| 103 | +``` |
| 104 | +append_partition(p_relation TEXT) |
| 105 | +``` |
| 106 | +Добавляет новую секцию в конец списка секций. Диапазон значений устанавливается равным последней секции. |
| 107 | +``` |
| 108 | +prepend_partition(p_relation TEXT) |
| 109 | +``` |
| 110 | +Добавляет новую секцию в начало списка секций. |
| 111 | +``` |
| 112 | +disable_partitioning(relation TEXT) |
| 113 | +``` |
| 114 | +Отключает механизм секционирования`pathman` для заданной таблицы и удаляет триггер на вставку. При этом созданные ранее секции остаются без изменений. |
| 115 | + |
| 116 | +##Примеры использования |
| 117 | +###HASH |
| 118 | +Рассмотрим пример секционирования таблицы, используя HASH-стратегию на примере таблицы. |
| 119 | +``` |
| 120 | +CREATE TABLE hash_rel ( |
| 121 | + id SERIAL PRIMARY KEY, |
| 122 | + value INTEGER); |
| 123 | +INSERT INTO hash_rel (value) SELECT g FROM generate_series(1, 10000) as g; |
| 124 | +``` |
| 125 | +Разобьем таблицу`hash_rel` на 100 секций по полю`value`: |
| 126 | +``` |
| 127 | +SELECT create_hash_partitions('hash_rel', 'value', 100); |
| 128 | +``` |
| 129 | +Перенесем данные из родительской таблицы в дочерние секции. |
| 130 | +``` |
| 131 | +SELECT partition_data('hash_rel'); |
| 132 | +``` |
| 133 | +###RANGE |
| 134 | +Пример секционирования таблицы с использованием стратегии RANGE. |
| 135 | +``` |
| 136 | +CREATE TABLE range_rel ( |
| 137 | + id SERIAL PRIMARY KEY, |
| 138 | + dt TIMESTAMP); |
| 139 | +INSERT INTO range_rel (dt) SELECT g FROM generate_series('2010-01-01'::date, '2015-12-31'::date, '1 day') as g; |
| 140 | +``` |
| 141 | +Разобьем таблицу на 60 секций так, чтобы каждая секция содержала данные за один месяц: |
| 142 | +``` |
| 143 | +SELECT create_range_partitions('range_rel', 'dt', '2010-01-01'::date, '1 month'::interval, 59); |
| 144 | +``` |
| 145 | +>Значение`premake` равно 59, а не 60, т.к. 1 секция создается независимо от значения`premake` |
| 146 | +
|
| 147 | +Перенесем данные из родительской таблицы в дочерние секции. |
| 148 | +``` |
| 149 | +SELECT partition_data('range_rel'); |
| 150 | +``` |
| 151 | +Объединим секции первые две секции: |
| 152 | +``` |
| 153 | +SELECT merge_range_partitions('range_rel_1', 'range_rel_2'); |
| 154 | +``` |
| 155 | +Разделим первую секцию на две по дате '2010-02-15': |
| 156 | +``` |
| 157 | +SELECT split_range_partition('range_rel_1', '2010-02-15'::date); |
| 158 | +``` |
| 159 | +Добавим новую секцию в конец списка секций: |
| 160 | +``` |
| 161 | +SELECT append_partition('range_rel') |
| 162 | +``` |