パーティショニングとは何ですか?
パーティション分割により、大きなテーブルが小さな部分に分割されます。これにより、クエリパフォーマンスが向上し、メンテナンスタスクが容易になり、データアーカイブの効率が向上し、データベースのバックアップが高速化されます。 PostgreSQLのパーティショニングについて詳しくは、ブログ「PostgreSQLでデータをパーティショニングするためのガイド」をご覧ください。
PostgreSQL 11の最近のリリースでは、多くの新しい驚くべきパーティショニング機能があります。これらの新しいパーティショニング機能の詳細については、このブログでいくつかのコード例を使用して説明します。
パーティションキーの更新
PostgreSQL 11より前は、パーティションキーの値を変更するUpdateステートメントは制限されており、許可されていませんでした。これは、新しいバージョンで可能になりました。 Updateステートメントは、パーティションキーの値を変更できます。実際には、行を正しいパーティションテーブルに移動します。内部的には、基本的に古いパーティションからDELETE FROMを実行し、新しいパーティションにINSERTを実行します(DELETE + INSERT)。
了解しました。これをテストしてみましょう。テーブルを作成し、パーティションキーで更新がどのように機能するかを確認します。
CREATE TABLE customers(cust_id bigint NOT NULL,cust_name varchar(32) NOT NULL,cust_address text,
cust_country text)PARTITION BY LIST(cust_country);
CREATE TABLE customer_ind PARTITION OF customers FOR VALUES IN ('ind');
CREATE TABLE customer_jap PARTITION OF customers FOR VALUES IN ('jap');
CREATE TABLE customers_def PARTITION OF customers DEFAULT;
severalnines_v11=# INSERT INTO customers VALUES (2039,'Puja','Hyderabad','ind');
INSERT 0 1
severalnines_v11=# SELECT * FROM customer_ind;
cust_id | cust_name | cust_address | cust_country
2039 | Puja | Hyderabad | ind
(1 row)
severalnines_v11=# UPDATE customers SET cust_country ='jap' WHERE cust_id=2039;
UPDATE 1
-- it moved the row to correct partition table.
severalnines_v11=# SELECT * FROM customer_ind;
cust_id | cust_name | cust_address | cust_country
---------+-----------+--------------+--------------
(0 rows)
severalnines_v11=# SELECT * FROM customer_jap;
cust_id | cust_name | cust_address | cust_country
---------+-----------+--------------+--------------
2039 | Puja | Hyderabad | jap
(1 row)
注意:デフォルトのパーティションテーブルがなく、更新された値がどの子テーブルのパーティション基準とも一致しない場合、UPDATEはエラーになります。
severalnines_v11=# UPDATE customers1 SET cust_country ='ypp' WHERE cust_id=2039;
2018-11-21 00:13:54.901 IST [1479] ERROR: no partition of relation "customers1" found for row
2018-11-21 00:13:54.901 IST [1479] DETAIL: Partition key of the failing row contains (cust_country) = (ypp).
2018-11-21 00:13:54.901 IST [1479] STATEMENT: UPDATE customers1 SET cust_country ='ypp' WHERE cust_id=2039;
ERROR: no partition of relation "customers1" found for row
DETAIL: Partition key of the failing row contains (cust_country) = (ypp).
[ -- the value of cust_country was not mapped to any part table so it failed]
デフォルトパーティションの作成
PostgreSQL 11 DEFAULTパーティション機能は、他のパーティションにマップされないタプルを格納します。 PostgreSQL 11より前は、これらの行はエラーになりました。どのパーティションテーブルにもマップされていない行は、デフォルトのパーティションに挿入されます。
ラボの例:「USA」の国コードは以下のパーティションテーブルで定義されていませんが、デフォルトのテーブルに正常に挿入されています。
CREATE TABLE customers_def PARTITION OF customers DEFAULT;
severalnines_v11=# INSERT INTO customers VALUES (4499,'Tony','Arizona','USA');
INSERT 0 1
severalnines_v11=# select * FROM customers_def;
cust_id | cust_name | cust_address | cust_country
---------+-----------+--------------+--------------
4499 | Tony | Arizona | USA
注意:デフォルトのパーティションは、そのパーティション値がデフォルトのテーブルに存在する場合、新しいパーティションの追加を防ぎます。この場合、 `USA`はデフォルトパーティションに存在していたため、以下のようには機能しません。
severalnines_v11=# CREATE TABLE customer_usa PARTITION OF customers FOR VALUES IN ('USA');
2018-11-21 00:46:34.890 IST [1526] ERROR: updated partition constraint for default partition "customers_def" would be violated by some row
2018-11-21 00:46:34.890 IST [1526] STATEMENT: CREATE TABLE customer_usa PARTITION OF customers FOR VALUES IN ('USA');ERROR: updated partition constraint for default partition "customers_def" would be violated by some row
severalnines_v11=#
Resolution - You need to move/remove those rows from Default table, then it will then let you create new part table like below.
severalnines_v11=# DELETE FROM customers_def WHERE cust_country in ('USA'); DELETE 1
severalnines_v11=# CREATE TABLE customer_usa PARTITION OF customers FOR VALUES IN ('USA');
CREATE TABLE
severalnines_v11=#
Nudgets :
HASHパーティション表にはDEFAULTパーティションを指定できません。パーティションテーブルに複数のDEFAULTテーブルを含めることはできません。
ハッシュ分割
範囲またはリストパーティションを決定できない場合(バケットの大きさがわからないため)、これは新しいパーティションメカニズムです。ハッシュ分割は、このデータ分散の問題を解決します。
テーブルは、各パーティションのモジュラスと余りを指定することによってパーティション化されます。各パーティションは、パーティションキーのハッシュ値を指定された係数で割った値が指定された余りを生成する行を保持します。 HASH関数は、行がすべてのパーティションテーブルにほぼ均等に分散されることを保証します。
まず、必要なパーティションテーブルの数を決定する必要があります。したがって、モジュラスと余りを定義できます。モジュラスが4の場合、余りは[0-3]からのみ可能です。
[モジュラス-テーブルの数|剰余-剰余のどの値がどのバケットに送られるか]
ハッシュパーティションを設定する方法
-- hash partition
CREATE TABLE part_hash_test (x int, y text) PARTITION BY hash (x);
-- create child partitions
CREATE TABLE part_hash_test_0 PARTITION OF part_hash_test FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE part_hash_test_1 PARTITION OF part_hash_test FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE part_hash_test_2 PARTITION OF part_hash_test FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE part_hash_test_3 PARTITION OF part_hash_test FOR VALUES WITH (MODULUS 4, REMAINDER 3);
親テーブルに50kレコードを挿入します:
severalnines_v11=# INSERT INTO part_hash_test SELECT generate_series(0,50000);
INSERT 0 50001
子テーブルでレコードがどのように均等に分散されているかを確認してください...
severalnines_v11=# SELECT count(1),tableoid::regclass FROM part_hash_test GROUP by 2 order by 2 ;
count | tableoid
-------+------------------
12537 | part_hash_test_0
12473 | part_hash_test_1
12509 | part_hash_test_2
12482 | part_hash_test_3
(4 rows)
以前に`Modulus`で指定されたパーティションの数を変更することはできないため、パーティションテーブルの数の要件を十分に前もって計画する必要があります。
残りが異なる新しいパーティションを追加しようとすると、エラーが発生します。
severalnines_v11=# CREATE TABLE part_hash_test_5 PARTITION OF part_hash_test FOR VALUES
WITH (MODULUS 4, REMAINDER 5);severalnines_v11-#
2018-11-21 01:51:28.966 IST [1675] ERROR: remainder for hash partition must be less than modulus
2018-11-21 01:51:28.966 IST [1675] STATEMENT: CREATE TABLE part_hash_test_5 PARTITION OF part_hash_test FOR VALUES WITH (MODULUS 4, REMAINDER 5);
ハッシュパーティショニングは任意のデータ型で機能し、UUID型でも機能します。テーブルの数は2の累乗にすることを常にお勧めします。また、テーブルの作成時に同じ係数を使用する必要はありません。これは、必要に応じて後でパーティションテーブルを作成するのに役立ちます。
この実装により、バキュームが高速化され、パーティションごとの結合が可能になります。
外部キーのサポート
PostgreSQL 11より前は、パーティションテーブルの外部キーはサポートされていませんでした。外部キーは現在パーティションテーブルで可能であり、以下はその方法です...
severalnines_v11=# CREATE TABLE customers2 ( cust_id integer PRIMARY KEY );
CREATE TABLE
severalnines_v11=# CREATE TABLE account (
ac_date date NOT NULL,
cust_id integer REFERENCES customers2(cust_id),
amount INTEGER NOT NULL) PARTITION BY RANGE (ac_date);
CREATE TABLE
子テーブルでの自動インデックス作成
以前のバージョンのPostgreSQLでは、すべてのパーティションテーブルにインデックスを作成するのは手作業でした。 PostgreSQLバージョン11では、ユーザーにとって非常に便利です。マスターテーブルにインデックスが作成されると、既存のすべての子パーティションに同じ構成でインデックスが自動的に作成され、将来のパーティションテーブルも処理されます。
マスターテーブルに作成されたインデックス
severalnines_v11=# CREATE index idx_name ON customers(cust_name);
CREATE INDEX
以下のように、すべての子テーブルにインデックスが自動的に作成されました。 (カタログテーブルで確認)
severalnines_v11=# SELECT tablename,indexname,indexdef FROM pg_indexes WHERE tablename ilike '%customer_%';
tablename | indexname | indexdef
---------------+-----------------------------+------------------------------------------------------------------------------------------
customer_ind | customer_ind_cust_name_idx | CREATE INDEX customer_ind_cust_name_idx ON public.customer_ind USING btree (cust_name)
customer_jap | customer_jap_cust_name_idx | CREATE INDEX customer_jap_cust_name_idx ON public.customer_jap USING btree (cust_name)
customer_usa | customer_usa_cust_name_idx | CREATE INDEX customer_usa_cust_name_idx ON public.customer_usa USING btree (cust_name)
customers_def | customers_def_cust_name_idx | CREATE INDEX customers_def_cust_name_idx ON public.customers_def USING btree (cust_name)
(4 rows)
インデックスはマスターテーブルにのみ作成でき、子テーブルには作成できません。自動生成されたインデックスを個別に削除することはできません。
子テーブルでの自動トリガーの作成
マスターテーブルでトリガーが作成されると、すべての子テーブルでトリガーが自動的に作成されます(この動作はインデックスで見られる動作と似ています)。
一意のインデックスを作成できる
バージョン11では、マスターテーブルに一意のインデックスを追加して、既存のすべての子テーブルと将来のパーティションテーブルに一意の制約を作成できます。
一意の制約を持つマスターテーブルを作成しましょう。
CREATE TABLE uniq_customers( cust_id bigint NOT NULL, cust_name varchar(32) NOT NULL, cust_address text, cust_country text,cust_email text, unique(cust_email,cust_id,cust_country) )PARTITION BY LIST(cust_country);
一意の制約は、以下のように子テーブルに自動的に作成されています。
severalnines_v11=# SELECT table_name,constraint_name,constraint_type FROM information_schema.table_constraints WHERE table_name ilike '%uniq%' AND constraint_type = 'UNIQUE';
table_name | constraint_name | constraint_type
-------------------+-------------------------------------------------------+-----------------
uniq_customers | uniq_customers_cust_email_cust_id_cust_country_key | UNIQUE
uniq_customer_ind | uniq_customer_ind_cust_email_cust_id_cust_country_key | UNIQUE
(2 rows)
注意:親テーブルの一意性制約は、パーティション階層全体にわたる一意性を実際に保証するものではありません。これはグローバルな制約ではなく、ローカルのみです。
今日のホワイトペーパーをダウンロードするClusterControlを使用したPostgreSQLの管理と自動化PostgreSQLの導入、監視、管理、スケーリングを行うために知っておくべきことについて学ぶホワイトペーパーをダウンロードするより高速なクエリパフォーマンス
動的パーティションプルーニング
PostgreSQL 11では、バイナリ検索により、LISTまたはRANGEパーティション化されているかどうかに関係なく、必要な子テーブルをより迅速に識別できます。ハッシュ関数は、HASHパーティションに一致するパーティションを見つけます。実際には、不要なパーティションテーブルを動的に削除し、クエリのパフォーマンスを向上させます。
動的パーティションプルーニングは、`enable_partition_pruning`パラメーターで制御できます。
severalnines_v11=# show enable_partition_pruning;
enable_partition_pruning
--------------------------
off
(1 row)
severalnines_v11=# EXPLAIN SELECT * from customers where cust_country = 'ind';
QUERY PLAN
---------------------------------------------------------------------
Append (cost=0.00..18.54 rows=5 width=154)
-> Seq Scan on customer_ind (cost=0.00..1.01 rows=1 width=154)
Filter: (cust_country = 'ind'::text)
-> Seq Scan on customer_jap (cost=0.00..1.00 rows=1 width=154)
Filter: (cust_country = 'ind'::text)
-> Seq Scan on customer_usa (cost=0.00..15.50 rows=2 width=154)
Filter: (cust_country = 'ind'::text)
-> Seq Scan on customers_def (cost=0.00..1.00 rows=1 width=154)
Filter: (cust_country = 'ind'::text)
(9 rows)
Enabled the parameter to ON.
severalnines_v11=# set enable_partition_pruning TO on;
SET
severalnines_v11=# EXPLAIN SELECT * from customers where cust_country = 'ind';
QUERY PLAN
--------------------------------------------------------------------
Append (cost=0.00..1.02 rows=1 width=154)
-> Seq Scan on customer_ind (cost=0.00..1.01 rows=1 width=154)
Filter: (cust_country = 'ind'::text)
(3 rows)
他の素晴らしい実装はこのようなものです。
実行-時間パーティションのプルーニング
11より前のバージョンのPostgreSQLでは、パーティションのプルーニングは計画時にのみ発生します。プランナーにはパーティションキーの値が必要です 正しいパーティションを識別します。この動作はPostgreSQL11で修正されています。これは、実行時間プランナーがどの値が提供されているかを認識し、それに基づいてパーティションの選択/削除が可能であり、実行速度が大幅に向上するためです。ユースケースは、パラメーター(プリペアドステートメント)を使用するクエリ、またはパラメーターとして値を提供するサブクエリです。
Example : cus_country is partition key and getting value from subquery
severalnines_v11=# explain analyze select * from customers WHERE cust_country = (select cust_count_x FROM test_execution_prun1);
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
Append (cost=23.60..42.14 rows=5 width=154) (actual time=0.019..0.020 rows=0 loops=1)
InitPlan 1 (returns $0)
-> Seq Scan on test_execution_prun1 (cost=0.00..23.60 rows=1360 width=32) (actual time=0.006..0.007 rows=1 loops=1)
-> Seq Scan on customer_ind (cost=0.00..1.01 rows=1 width=154) (never executed)
Filter: (cust_country = $0)
-> Seq Scan on customer_jap (cost=0.00..1.00 rows=1 width=154) (never executed)
Filter: (cust_country = $0)
-> Seq Scan on customer_usa (cost=0.00..15.50 rows=2 width=154) (never executed)
Filter: (cust_country = $0)
-> Seq Scan on customers_def (cost=0.00..1.00 rows=1 width=154) (actual time=0.003..0.003 rows=0 loops=1)
Filter: (cust_country = $0)
Planning Time: 0.237 ms
Execution Time: 0.057 ms
(13 rows)
上記のプランの説明では、実行時に、プランナーがパラメーター値に基づいて正しいパーティションテーブルを識別し、はるかに高速に実行され、他のパーティションテーブルのスキャン/ループに時間を費やさなかったことがわかります(決して参照しないでください)。上記の説明計画の実行されたセクション)。これは非常に強力であり、パーティショニングにおけるパフォーマンス向上の新時代を開始しました。
パーティションワイズアグリゲート
パラメーター:enable_partitionwise_aggregate
パーティションキーがグループ化キーと一致する場合、すべてのパーティションは、すべてのパーティションを一度にスキャンするのではなく、グループの個別のセットを生成します。パーティションごとに並列集計を実行し、最終結果の間にすべての結果を連結します。
severalnines_v11=# explain SELECT count(1),cust_country FROM customers GROUP BY 2;
QUERY PLAN
----------------------------------------------------------------------------
HashAggregate (cost=21.84..23.84 rows=200 width=40)
Group Key: customer_ind.cust_country
-> Append (cost=0.00..19.62 rows=443 width=32)
-> Seq Scan on customer_ind (cost=0.00..1.01 rows=1 width=32)
-> Seq Scan on customer_jap (cost=0.00..1.00 rows=1 width=32)
-> Seq Scan on customer_usa (cost=0.00..14.40 rows=440 width=32)
-> Seq Scan on customers_def (cost=0.00..1.00 rows=1 width=32)
(7 rows)
severalnines_v11=# SET enable_partitionwise_aggregate TO on;
SET
severalnines_v11=# explain SELECT count(1),cust_country FROM customers GROUP BY 2;
QUERY PLAN
----------------------------------------------------------------------------
Append (cost=1.01..22.67 rows=203 width=40)
-> HashAggregate (cost=1.01..1.02 rows=1 width=40)
Group Key: customer_ind.cust_country
-> Seq Scan on customer_ind (cost=0.00..1.01 rows=1 width=32)
-> HashAggregate (cost=1.00..1.01 rows=1 width=40)
Group Key: customer_jap.cust_country
-> Seq Scan on customer_jap (cost=0.00..1.00 rows=1 width=32)
-> HashAggregate (cost=16.60..18.60 rows=200 width=40)
Group Key: customer_usa.cust_country
-> Seq Scan on customer_usa (cost=0.00..14.40 rows=440 width=32)
-> HashAggregate (cost=1.00..1.01 rows=1 width=40)
Group Key: customers_def.cust_country
-> Seq Scan on customers_def (cost=0.00..1.00 rows=1 width=32)
(13 rows)
並列集計処理とパーティションごとのスキャンが含まれているため、これは確かに高速です。
カタログクエリを使用して、すべての親パーティションテーブルを知ることができます。
SELECT relname FROM pg_class WHERE oid in (select partrelid FROM pg_partitioned_table);
簡単なパーティション機能マトリックス
パーティション機能 | v11 | v10 |
---|---|---|
デフォルトのパーティション | はい | いいえ |
外部テーブルの継承 | はい | いいえ |
ハッシュキーによるパーティション分割 | はい | いいえ |
PKとFKのサポート | はい | いいえ |
パーティションキーの更新 | はい | いいえ |
CTでの自動Inexes | はい | いいえ |
CTでの自動トリガー | はい | いいえ |
実行時間パーティションのプルーニング | はい | いいえ |
パーティションごとの参加 | はい | いいえ |
動的パーティションプルーン | はい | いいえ |
次は?
パーティショニングパフォーマンス
これは、現在PostgreSQLコミュニティで最も活発な作業領域の1つです。 PostgreSQLバージョン12は、パーティショニングスペースでさらにパフォーマンスが向上したパッケージになります。バージョン12は2019年11月にリリースされる予定です。