こんにちは。サイオステクノロジー 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 パーティショニングを適用すると、当該テーブルにアクセスする際の性能向上等が期待できるかもしれません。