[Перевод] Партицирование в GreenPlum 7: рекурсия и наследование

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

f46c74bccb04a3fcc684a4f5dc47e419.png

Ключевое слово ONLY

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

-- Assuming partitioned table 'sales' and all 
-- of its child partitions are heap tables.
ALTER TABLE ONLY sales SET ACCESS METHOD ao_row;

Для секционированной таблицы зададим подходящий способ доступа:

\d+ sales
                                Partitioned table "public.sales"
 Column |     Type      | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------------+-----------+----------+---------+---------+--------------+-------------
 id     | integer       |           |          |         | plain   |              |
 date   | date          |           |          |         | plain   |              |
 amt    | numeric(10,2) |           |          |         | main    |              |
Partition key: RANGE (date)
Partitions: jan_sales FOR VALUES FROM ('2023-01-01') TO ('2023-02-01')
Distributed by: (id)
Access method: ao_row

При этом существующий дочерний раздел приложения не будет затронут:

\d+ jan_sales
                                    Table "public.jan_sales"
 Column |     Type      | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------------+-----------+----------+---------+---------+--------------+-------------
 id     | integer       |           |          |         | plain   |              |
 date   | date          |           |          |         | plain   |              |
 amt    | numeric(10,2) |           |          |         | main    |              |
Partition of: sales FOR VALUES FROM ('2023-01-01') TO ('2023-02-01')
Partition constraint: ((date IS NOT NULL) AND (date >= '2023-01-01'::date) AND (date < '2023-02-01'::date))
Distributed by: (id)
Access method: heap

GRANT ONLY|REVOKE ONLY

Также в Greenplum 7 можем указать ключевое слово ONLY для GRANT/REVOKE, которых нет в PostgreSQL. Обратите внимание, что исторически у Greenplum было такое поведение, при котором GRANT|REVOKE в родительской секционированной таблице рекурсивно выполнялось в её дочерних разделах. Greenplum 7 придерживается такого же поведения, но добавляет опцию ONLY, чтобы обеспечить гибкость, если вы не хотите выполнять рекурсию.

Простой пример использования:

-- Let's say at some point you want two roles with read permission 
-- on our 'sale' partitioned table, but one for existing partitions
-- and another for future partitions.

-- 1. Grant only on the parent table for just future partitions.
GRANT SELECT ON ONLY sales TO role_that_can_read_only_future_partitions;

-- 2. W/o "ONLY", this will grant for all existing partitions.
-- Then, revoke only for parent to limit permission for future partitions.
GRANT SELECT ON sales TO roles_that_can_read_only_existing_partitions;
REVOKE SELECT ON ONLY sales FROM roles_that_can_read_only_existing_partitions

«DWH на основе GreenPlum»

Создание новой дочерней таблицы

Как правило, при создании новой таблицы в качестве дочернего раздела наследуются все свойства родительской таблицы. Начнём с таблицы sales:

CREATE TABLE sales (id int, date date, amt decimal(10,2))
USING ao_row
DISTRIBUTED BY (id)
PARTITION BY RANGE (date);

Можем использовать два типа синтаксиса для создания новой таблицы в качестве дочернего раздела:

-- Create child partition using the new Greenplum 7 syntax
CREATE TABLE jan_sales PARTITION OF sales FOR VALUES FROM ('2023-01-01') TO ('2023-02-01');

-- Create child partition using the legacy syntax
ALTER TABLE sales ADD PARTITION feb_sales START ('2023-02-01') END ('2023-03-01');

Оба дочерних раздела унаследуют метод доступа к родительской таблице, ориентированный на добавление строк (ao_row):

\d+ sales
                                Partitioned table "public.sales"
 Column |     Type      | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------------+-----------+----------+---------+---------+--------------+-------------
 id     | integer       |           |          |         | plain   |              |
 date   | date          |           |          |         | plain   |              |
 amt    | numeric(10,2) |           |          |         | main    |              |
Partition key: RANGE (date)
Partitions: jan_sales FOR VALUES FROM ('2023-01-01') TO ('2023-02-01'),
            sales_1_prt_feb_sales FOR VALUES FROM ('2023-02-01') TO ('2023-03-01')
Distributed by: (id)
Access method: ao_row

\d+ jan_sales
                                    Table "public.jan_sales"
 Column |     Type      | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------------+-----------+----------+---------+---------+--------------+-------------
 id     | integer       |           |          |         | plain   |              |
 date   | date          |           |          |         | plain   |              |
 amt    | numeric(10,2) |           |          |         | main    |              |
Partition of: sales FOR VALUES FROM ('2023-01-01') TO ('2023-02-01')
Partition constraint: ((date IS NOT NULL) AND (date >= '2023-01-01'::date) AND (date < '2023-02-01'::date))
Compression Type: None
Compression Level: 0
Block Size: 32768
Checksum: t
Distributed by: (id)
Access method: ao_row

\d+ sales_1_prt_feb_sales
                              Table "public.sales_1_prt_feb_sales"
 Column |     Type      | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------------+-----------+----------+---------+---------+--------------+-------------
 id     | integer       |           |          |         | plain   |              |
 date   | date          |           |          |         | plain   |              |
 amt    | numeric(10,2) |           |          |         | main    |              |
Partition of: sales FOR VALUES FROM ('2023-02-01') TO ('2023-03-01')
Partition constraint: ((date IS NOT NULL) AND (date >= '2023-02-01'::date) AND (date < '2023-03-01'::date))
Compression Type: None
Compression Level: 0
Block Size: 32768
Checksum: t
Distributed by: (id)
Access method: ao_row

SPLIT PARTITION

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

ALTER TABLE sales_1_prt_feb_sales SET ACCESS METHOD ao_column;

Теперь разделим его на два новых раздела:

ALTER TABLE sales 
SPLIT PARTITION feb_sales AT ('2023-02-15') INTO 
(partition feb_first_half, partition feb_second_half);

Теперь новые разделы будут иметь метод доступа, отличный от родительского (обратите внимание, что исходный раздел feb_sales исчез):

\d+ sales
                                Partitioned table "public.sales"
 Column |     Type      | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------------+-----------+----------+---------+---------+--------------+-------------
 id     | integer       |           |          |         | plain   |              |
 date   | date          |           |          |         | plain   |              |
 amt    | numeric(10,2) |           |          |         | main    |              |
Partition key: RANGE (date)
Partitions: jan_sales FOR VALUES FROM ('2023-01-01') TO ('2023-02-01'),
            sales_1_prt_feb_first_half FOR VALUES FROM ('2023-02-01') TO ('2023-02-15'),
            sales_1_prt_feb_second_half FOR VALUES FROM ('2023-02-15') TO ('2023-03-01')
Distributed by: (id)
Access method: ao_row

\d+ sales_1_prt_feb_first_half
                                                     Table "public.sales_1_prt_feb_first_half"
 Column |     Type      | Collation | Nullable | Default | Storage | Stats target | Compression Type | Compression Level | Block Size | Description
--------+---------------+-----------+----------+---------+---------+--------------+------------------+-------------------+------------+-------------
 id     | integer       |           |          |         | plain   |              | none             | 0                 | 32768      |
 date   | date          |           |          |         | plain   |              | none             | 0                 | 32768      |
 amt    | numeric(10,2) |           |          |         | main    |              | none             | 0                 | 32768      |
Partition of: sales FOR VALUES FROM ('2023-02-01') TO ('2023-02-15')
Partition constraint: ((date IS NOT NULL) AND (date >= '2023-02-01'::date) AND (date < '2023-02-15'::date))
Distributed by: (id)
Access method: ao_column
Options: blocksize=32768, compresslevel=0, compresstype=none, checksum=true

Это поведение наследования в SPLIT PARTITION остаётся таким же, как и в Greenplum 6.

Присоединение существующей таблицы

Рассмотрим кейс, когда мы не создаём новую таблицу, а просто присоединяем существующую таблицу в качестве дочернего раздела. В этом случае исходные свойства таблицы сохраняются после того, как она становится дочерним разделом. Предположим, изначально у нас была таблица recent_sales, которую мы продолжаем часто обновлять, и по умолчанию она использует heap access-метод:

CREATE TABLE recent_sales (id int, date date, amt decimal(10,2));

\d+ recent_sales;
                                  Table "public.recent_sales"
 Column |     Type      | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------------+-----------+----------+---------+---------+--------------+-------------
 id     | integer       |           |          |         | plain   |              |
 date   | date          |           |          |         | plain   |              |
 amt    | numeric(10,2) |           |          |         | main    |              |
Distributed by: (id)
Access method: heap

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

ALTER TABLE sales ATTACH PARTITION recent_sales 
    FOR VALUES FROM ('2023-12-01') TO ('2030-12-31');

\d+ recent_sales
                                  Table "public.recent_sales"
 Column |     Type      | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------------+-----------+----------+---------+---------+--------------+-------------
 id     | integer       |           |          |         | plain   |              |
 date   | date          |           |          |         | plain   |              |
 amt    | numeric(10,2) |           |          |         | main    |              |
Partition of: sales FOR VALUES FROM ('2023-12-01') TO ('2030-12-31')
Partition constraint: ((date IS NOT NULL) AND (date >= '2023-12-01'::date) AND (date < '2030-12-31'::date))
Distributed by: (id)
Access method: heap

EXCHANGE PARTITION

Ещё один кейс — когда EXCHANGE PARTITION прикрепляет существующую внешнюю таблицу к дочернему разделу. В Greenplum 7 есть несколько изменений, как теперь новый дочерний раздел наследует свойства таблицы.

Как упоминалось в предыдущей статье, EXCHANGE PARTITION  в Greenplum 7 состоит из DETACHPARTITION  и ATTACH PARTITION. В результате EXCHANGE PARTITION  больше похож на ATTACH PARTITION по поведению наследования. Вот подробный список:

  • Владелец таблицы: EXCHANGE PARTITION  не требует, чтобы у будущей таблицы разделов был тот же владелец, что и у родительской таблицы.

  • Индекс: EXCHANGE PARTITION не требует, чтобы будущий раздел имел тот же индекс, что и родительский. Команда создаст его, если он отсутствует.

  • Ограничение таблицы: EXCHANGE PARTITION требует, чтобы у будущего раздела были все ограничения, которые есть у его родителя.

Заключение

В общем, большинство команд ALTER TABLE и GRANT|REVOKE будут возвращаться к своим дочерним разделам, если только не указано ключевое слово ONLY. И будет ли наследоваться новый дочерний раздел или нет, зависит от того, создан он или присоединен. Если он создан, он будет наследовать от родителя, в противном случае он сохраняет свои собственные исходные свойства.

«DWH на основе GreenPlum»

© Habrahabr.ru