こんにちは。サイオステクノロジー OSS サポート担当 Y です。
今回は PostgreSQL 11 (現時点ではまだ beta 版) の新機能である “HASH パーティショニング” を検証してみました。(※以下の内容は CentOS 7.5/PostgreSQL 11beta3 にて検証しています。)
■はじめに
昨年リリースされた PostgreSQL 10 の新機能である Declarative Partitioning では、RANGE パーティションと LIST パーティションの 2つの種類のパーティションを作成することができるのですが、PostgreSQL 11 ではこれらに加えて “HASH パーティション” が実装される予定となっています。
今回は PostgreSQL 11beta3 を使って、この HASH パーティションを試してみようと思います。
■検証
さて、まずはテーブルを作成してみます。親テーブル “hash_test” に対して子テーブル “hash_child_1”, “hash_child_2”, “hash_child_3” を作成しています。
postgres=# CREATE TABLE hash_test (data text) PARTITION BY HASH (data);
CREATE TABLE
postgres=#
postgres=# CREATE TABLE hash_child_1 PARTITION OF hash_test FOR VALUES WITH (MODULUS 3, REMAINDER 0);
CREATE TABLE
postgres=#
postgres=# CREATE TABLE hash_child_2 PARTITION OF hash_test FOR VALUES WITH (MODULUS 3, REMAINDER 1);
CREATE TABLE
postgres=#
postgres=# CREATE TABLE hash_child_3 PARTITION OF hash_test FOR VALUES WITH (MODULUS 3, REMAINDER 2);
CREATE TABLE
postgres=#
postgres=# \d+ hash_test
Table "public.hash_test"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------+------+-----------+----------+---------+----------+--------------+-------------
data | text | | | | extended | |
Partition key: HASH (data)
Partitions: hash_child_1 FOR VALUES WITH (modulus 3, remainder 0),
hash_child_2 FOR VALUES WITH (modulus 3, remainder 1),
hash_child_3 FOR VALUES WITH (modulus 3, remainder 2)
上記の通り、HASH パーティションの場合も RANGE/LIST パーティションと同様に CREATE TABLE 文だけでパーティショニングされたテーブルを作成することが可能です。
FOR VALUES 句に指定している値の詳細については、まだ正確に把握できていないのですが、以下のドキュメントによると “modulus は正の整数”, “remainder は modulus より小さい負ではない整数” 等の複数の条件があるようです。
それでは、作成したテーブルにデータを INSERT してみます。今回は以下の様なランダムな文字列を生成して INSERT してみました。
postgres=# INSERT INTO hash_test SELECT md5(clock_timestamp()::text) as test_data FROM generate_series(1,6);
INSERT 0 6
postgres=#
postgres=# SELECT * FROM hash_test;
data
----------------------------------
7b889830662255538e88baff30130545
336d0b62f2020f380a4f08aa56c37b97
40d27ceb4af48dc8cb6324ac287cf71f
813de88f6844a517a7f617c75ac8583f
b5d30b6de905a42faf636b68c30d13ce
5bf0458863cf2f10566634b7addbbf7d
(6 rows)
親テーブルにデータが INSERT されたことが確認できたので、次に各子テーブルのデータを確認してみます。
postgres=# SELECT * FROM hash_child_1;
data
----------------------------------
7b889830662255538e88baff30130545
336d0b62f2020f380a4f08aa56c37b97
40d27ceb4af48dc8cb6324ac287cf71f
(3 rows)
postgres=#
postgres=# SELECT * FROM hash_child_2;
data
----------------------------------
813de88f6844a517a7f617c75ac8583f
(1 row)
postgres=#
postgres=# SELECT * FROM hash_child_3;
data
----------------------------------
b5d30b6de905a42faf636b68c30d13ce
5bf0458863cf2f10566634b7addbbf7d
(2 rows)
上記の通り、先ほど親テーブルに INSERT したデータが子テーブルに分散されていることが確認できました。
また、一般的な HASH パーティショニングのメリットとして “データを均等に分散することができる” というものがあります。
上記の例では、レコード数が少ないため均等に分散されているようには見えませんが、レコード数が増えればある程度均等に分散されるはずです。
ということで、実際に 300万レコードほどデータを INSERT して、データが均等に分散されるかを確認してみます。
postgres=# INSERT INTO hash_test SELECT md5(clock_timestamp()::text) as test_data FROM generate_series(1,3000000); INSERT 0 3000000 postgres=# postgres=# SELECT count(*) FROM hash_test; count --------- 3000000 (1 row) postgres=# postgres=# SELECT count(*) FROM hash_child_1; count --------- 1000535 (1 row) postgres=# postgres=# SELECT count(*) FROM hash_child_2; count --------- 1000318 (1 row) postgres=# postgres=# SELECT count(*) FROM hash_child_3; count -------- 999147 (1 row)
すると、各子テーブルにほぼほぼ均等に (約 100万レコードずつ) データが分散されていることが確認できました。
■最後に
今回は PostgreSQL 11 で実装予定の “HASH パーティショニング” を試してみました。
検証内容に記載してある通り、HASH パーティショニングはデータを均等に分散してくれるので、複数の異なるディスク上に作成したテーブルスペースと組み合わせると、I/O 負荷を均等に分散することができるので、大規模なテーブルに HASH パーティショニングを適用すると、当該テーブルにアクセスする際の性能向上等が期待できるかもしれません。

