-
2025-11-30 PostgreSQLPostgreSQL ナレッジ
PostgreSQLのバージョン間の性能差を検証(2)
本記事の概要
前回( https://www.insight-ltd.co.jp/tech_blog/postgresql/pg_versionup/947/ )は、PostgreSQL のバージョン 15〜18 において、pgbench のベンチマークスクリプトを利用して性能を比較しました。
今回は、pgbench のテーブルを利用してパラレルクエリなどの SQL の性能を比較を行います。
検証
検証は、前回と同じく以下の環境で実施しました。
- RockyLinux 9.6
- PostgreSQL 15.14
- PostgreSQL 16.10
- PostgreSQL 17.6
- PostgreSQL 18.0
各バージョンとも、同一サーバ上でビルド・初期化を行い、同一設定で実施しています。
shared_buffers や work_mem などは、マシンスペックに合わせて一般的な目安とされている値に設定しました。
検証方法
下記の SQL を pgbench コマンドの -f に指定して、ベンチマークテストを実施しました。
検証に利用した SQL
# SQL 1 ( DISTINCT の改善 )
SELECT DISTINCT bid, aid FROM pgbench_accounts ORDER BY bid, aid LIMIT 10000;
# SQL 2 ( UNION ALL の改善 )
( SELECT aid FROM pgbench_accounts WHERE bid BETWEEN 1 AND 100 ORDER BY aid )
UNION ALL
( SELECT aid FROM pgbench_accounts WHERE bid BETWEEN 101 AND 200 ORDER BY aid )
ORDER BY aid;
# SQL 3 ( GROUP BY の改善 )
SELECT aid, bid, max(abalance) FROM pgbench_accounts GROUP BY aid, bid;
# SQL 4 ( GROUPING SETS の改善 )
SELECT bid, sum(abalance) AS s FROM pgbench_accounts GROUP BY GROUPING SETS ((bid), ()) HAVING sum(abalance) > 100000;
前回と同じく 2 パターンのデータサイズで性能を測定しました。
データサイズ = 約 650 MB でのテスト
テストに利用したコマンドは下記となります。
$ pgbench -i -s 50
$ pgbench -c 1 -j 1 -T 300 -f <SQLファイル>
$ pgbench -c 24 -j 12 -T 600 -f <SQLファイル>
データサイズ = 約 6.5 GB でのテスト
テストに利用したコマンドは下記となります。
$ pgbench -i -s 500
$ pgbench -c 24 -j 12 -T 4000 -f <SQLファイル>
オプションの意味は
- -i は、データベースの初期化
- -s は、初期化するデータベースのスケール(サイズ、データ件数)
- -c は、テストの同時接続数
- -j は、テストのスレッド数
- -T は、テストの実行時間(秒)
となります。
「データサイズ = 約 650 MB でのテスト」では、1 接続(並列)と 24 接続(並列)で SQL 実行されています。
「データサイズ = 約 6.5 GB でのテスト」では、24 接続(並列)で SQL 実行されています。
「データサイズ = 約 650 MB でのテスト」の結果
「データサイズ = 約 650 MB でのテスト」の結果を、各バージョンごとに比較したものが下記となります。
1 接続(並列)での結果
| バージョン | count | TPS | min | max | p95 | p99 |
|---|---|---|---|---|---|---|
| 15 | 18 回 | 0.060 t/s | 15,936.472 ms | 17,100.576 ms | 16,997.326 ms | 17,100.576 ms |
| 16 | 21 回 | 0.068 t/s | 14,076.489 ms | 15,482.081 ms | 15,265.680 ms | 15,482.081 ms |
| 17 | 22 回 | 0.070 t/s | 13,937.872 ms | 15,328.112 ms | 15,281.396 ms | 15,328.112 ms |
| 18 | 22 回 | 0.073 t/s | 13,590.447 ms | 15,064.642 ms | 13,834.356 ms | 15,064.642 ms |
各項目の説明は下記となります。
- count: 実行されたトランザクション数。
- tps: 1秒あたりに処理されたトランザクション数。
- min: 実行時間の最小値。最も短いトランザクション処理時間です。
- max: 実行時間の最大値。最も遅いトランザクション処理時間です。
- p95: 全トランザクションのうち 95% が、この時間以下で完了したことを表しています。
- p99: 全トランザクションのうち 99% が、この時間以下で完了したことを表しています。
24 接続(並列)での結果
1 接続(並列)では試行回数が少なかったため、24 接続(並列)でも確認しました。
| バージョン | count | TPS | min | max | p95 | p99 |
|---|---|---|---|---|---|---|
| 15 | 293 回 | 0.479 t/s | 24,761.036 ms | 87,856.245 ms | 58,411.654 ms | 61,983.926 ms |
| 16 | 312 回 | 0.531 t/s | 24,896.716 ms | 89,808.145 ms | 78,537.596 ms | 84,098.765 ms |
| 17 | 296 回 | 0.551 t/s | 20,170.504 ms | 132,029.828 ms | 121,815.829 ms | 126,652.246 ms |
| 18 | 321 回 | 0.564 t/s | 21,607.231 ms | 132,780.028 ms | 87,353.457 ms | 95,587.210 ms |
「データサイズ = 約 6.5 GB でのテスト」の結果
「データサイズ = 約 6.5 GB でのテスト」の結果を、各バージョンごとに比較したものが下記となります。
| バージョン | count | TPS | min | max | p95 | p99 |
|---|---|---|---|---|---|---|
| 15 | 179 回 | 0.044 t/s | 308,085.567 ms | 728,357.052 ms | 694,875.877 ms | 705,954.848 ms |
| 16 | 183 回 | 0.045 t/s | 311,889.801 ms | 689,353.586 ms | 635,016.792 ms | 674,919.096 ms |
| 17 | 188 回 | 0.046 t/s | 289,807.476 ms | 740,415.678 ms | 631,550.352 ms | 715,843.183 ms |
| 18 | 180 回 | 0.045 t/s | 292,632.183 ms | 756,750.662 ms | 693,733.770 ms | 717,640.867 ms |
検証結果から確認できること
結果から下記のことを確認できました。
- データサイズが小さい場合、バージョンと性能が比例していた。
- 特にバージョン 15 から 16 の性能向上が、その他のバージョン間よりも大きい。
- データサイズが大きい場合、バージョンによる性能差は見られなかった
上記の結果となった理由について、実行計画から読み解けるかを確認します。
実行計画の確認(データサイズ = 約 650 MB)
「データサイズ = 約 650 MB でのテスト」について、v15 と v18 の実行計画を比較して、どのような差異があるのかを確認します。
差異が分かりやすいものを 1 つ抜粋して、v15 と v18 の実行計画を、それぞれ下記に記載します。
v15 の実行計画
下記が v15 の実行計画です。
SQL の実行時間はおおよそ 8.2 sec となっています。
EXPLAIN (ANALYZE, BUFFERS) SELECT aid, bid, max(abalance) FROM pgbench_accounts GROUP BY aid, bid;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------
HashAggregate (cost=156968.00..206968.00 rows=5000000 width=12) (actual time=4644.430..7646.144 rows=5000000 loops=1)
Group Key: aid
Batches: 5 Memory Usage: 524337kB Disk Usage: 31504kB
Buffers: shared hit=81968, temp read=3566 written=6589
-> Seq Scan on pgbench_accounts (cost=0.00..131968.00 rows=5000000 width=12) (actual time=0.024..727.782 rows=5000000 loops=1)
Buffers: shared hit=81968
Planning:
Buffers: shared hit=30 read=2
Planning Time: 0.232 ms
Execution Time: 8212.458 ms
(10 rows)
v18 の実行計画
下記が v18 の実行計画です。
SQL の実行時間はおおよそ 6.7 sec となっています。
EXPLAIN (ANALYZE, BUFFERS) SELECT aid, bid, max(abalance) FROM pgbench_accounts GROUP BY aid, bid;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------
HashAggregate (cost=156968.00..206968.00 rows=5000000 width=12) (actual time=4133.848..6249.216 rows=5000000.00 loops=1)
Group Key: aid
Batches: 1 Memory Usage: 327705kB
Buffers: shared hit=81968
-> Seq Scan on pgbench_accounts (cost=0.00..131968.00 rows=5000000 width=12) (actual time=0.030..621.958 rows=5000000.00 loops=1)
Buffers: shared hit=81968
Planning:
Buffers: shared hit=7 read=2
Planning Time: 0.889 ms
Execution Time: 6731.028 ms
(10 rows)
実行計画の差異
v15 と v18 の実行計画を比較しても、処理ノードの順序など、大きな違いはありませんでした。
実行計画が変わらないのは、結合するテーブルの数が少なく、SQL が比較的シンプルであることが理由だと考えられます。
v15 と v18 の実行計画の主な違いは、メモリの消費量が v18 の方が少ないことが挙げられます。
上記の実行計画例では、v15 ではディスク領域が利用されているのに対し、v18 ではメモリ領域のみでハッシュ処理が行われています。
また、データサイズが大きい場合にバージョンによる性能差が見られなかったのは、データ量に対してメモリサイズが不足しており、ディスク領域を利用した処理が発生しているためでした。
まとめ
今回の検証で、PostgreSQL のバージョンが上がるにつれて、SQL 実行時のメモリ消費量が抑えられ、結果として SQL 実行速度が向上することを確認できました。
ただ、今回の検証は、比較的シンプルな SQL を、インサイト社内での環境で実行した結果である点にご留意ください。
実際の運用で実行されている数多くのテーブルを結合している複雑な SQL では、今回の結果と異なる可能性があります。
性能検証の際には、実際の運用で使用されている SQL やワークロードを用いることが推奨となります。