詳解 PostgreSQL[10/11対応]―現場で役立つ新機能と実践知識

第3章 アプリケーション開発に便利な機能―Window 関数,ストアドファンクション/プロシージャ,JIT,パラレルクエリ

この記事を読むのに必要な時間:およそ 11 分

JITコンパイラ─⁠─実行時に処理を先にコンパイルする

PostgreSQL 11では,なんとLLVMLow Level Virtual Machine低水準仮想マシン)による実行時コンパイラ,つまりJITコンパイラが追加されています。JITコンパイラが効果的なシーンは,計算量が多く,CPUがボトルネックになっており,長時間実行されるようなSQL文です。逆に小さな処理では,オーバーヘッドのほうが大きくなります。

JITコンパイラの使い方

JITコンパイラの利用の有無は,postgresql.confで設定します。デフォルトでは無効です。

先述したとおり,JITコンパイラは大きな処理でなければ非効率になります。そのため有効になっている場合も,実行計画で推定されたコストがjit_above_costの閾値いきち(デフォルト値は100000)を超えるSQLのみ,JITコンパイラが実行されます。この100000という閾値は簡単に超える値ではないため,基本的にはデフォルト値のままで問題ありません。

任意のタイミングで利用したい場合は,jit_above_costをチューニングしましょう。そのほかにもパラメータとして,

jit_optimize_above_cost
JITコンパイル時に最適化するかどうかを決めるコストの閾値を指定する。デフォルト値は500000
jit_inline_above_cost
JITコンパイル時にインライン化を行うかどうかを決めるコストの閾値を指定する。デフォルト値は500000

があります。基本的に変更することは少ないでしょうが,JITコンパイラのチューニングの際には必要になりますので,覚えておきましょう。

それでは,実際にJITコンパイラを使ってみましょう。先述したとおり,JITコンパイラはデフォルトで無効化されており,気軽に試せる閾値にもなっていません。そこで図6のように,JITコンパイラをonにするとともに,jit_above_costを10に下げて利用してみましょう。一時的な値の変更ですので,図6ではSET文で変更しています。

図6 JITコンパイラを利用した際の実行計画

通常の実行計画

demo=# EXPLAIN ANALYZE SELECT SUM(relpages) FROM pg_class;
                                                 QUERY PLAN
-------------------------------------------------------------------------------------------------
 Aggregate (cost=16.27..16.29 rows=1 width=8) (actual time=0.236..0.236 rows=1 loops=1)
   -> Seq Scan on pg_class (cost=0.00..15.42 rows=342 width=4) (actual time=0.008..0.122 rows=342 loops=1)
 Planning Time: 3.317 ms
 Execution Time: 0.485 ms
(4 rows)

JITコンパイラが利用された場合の実行計画
demo=# SET jit TO on;
demo=# SET jit_above_cost TO 10;

demo=# EXPLAIN ANALYZE SELECT SUM(relpages) FROM pg_class;
                                                 QUERY PLAN
------------------------------------------------------------------------------------------------
 Aggregate (cost=16.27..16.29 rows=1 width=8) (actual time=9.850..9.850 rows=1 loops=1)
   -> Seq Scan on pg_class (cost=0.00..15.42 rows=342 width=4) (actual time=0.014..0.066 rows=342 loops=1)
 Planning Time: 0.132 ms
 JIT:
   Functions: 3
   Options: Inlining false, Optimization false, Expressions true, Deforming true
   Timing: Generation 0.894 ms, Inlining 0.000 ms, Optimization 0.652 ms, Emission 8.867 ms, Total 10.414 ms
 Execution Time: 114.956 ms
(8 rows)

JITコンパイラが使われた場合,コンパイルの時間なども実行計画に出力されます。JITコンパイラの実行計画を見るうえ主な項目は表2のとおりです。JITコンパイラをチューニングする場合は,これらの項目を見ながら調整しましょう。

表2 JITコンパイラの実行計画の主な項目

名前説明
FunctionsJITコンパイラで処理された箇所の数
Options: InliningJITコンパイル時にインライン化をしたか
Options: OptimizationJITコンパイル時に最適化をしたか
Timing: GenerationJITコンパイルの所要時間
Timing: InliningJITコンパイルでのインライン化の所要時間
Timing: OptimizationJITコンパイルでの最適化の所要時間
Timing: EmissionJITコンパイラのコード出力の所要時間

単純にExecution Timeを見ると,JITコンパイラの利用の有無でどちらが有効であるかを比較できます。

JITコンパイラの使いどころ

図6のExecution Timeからわかるとおり,小さな処理ではJITコンパイラは逆に遅くなります。

では,実際にどの程度の処理からJITコンパイラの効果があるのでしょうか。⁠SRA OSS Tech Blog」PostgreSQL 11検証報告によると,およそ11の関数や演算子と3ヵ所以上のキャストがあり,1億件以上の行に対して処理するようなSQLの場合に効果が出るようです。

複雑な分析クエリや大きなテーブルに対するキャストや文字列加工が必要な場合は,JITコンパイラの利用を検討してもよいでしょう。また,JITコンパイラは11から入った新機能ですので,今後より進化していくことでしょう。

パラレルクエリ─⁠─並列実行で高速化

PostgreSQLの大きな魅力と言えば,9.6から採用されたパラレルクエリです。パラレルクエリを使うと,複雑な集計クエリなどを並列処理し,高速に行えます。この機能はマルチコアCPU時代にマッチした強力な機能です。

パラレルクエリの役割

パラレルクエリは図7のとおり,テーブルスキャンなどの読み込みに対して並列に処理し,計算結果を最後に結合して返すしくみです。テーブルスキャンの場合などは対象テーブルが大きければ大きいほど効果が出ますし,並列度をうまくチューニングできれば処理速度も数倍速くなります。

図7 パラレルクエリのしくみ

図7 パラレルクエリのしくみ

パラレルクエリの使い方

パラレルクエリはデフォルトで有効となっており,意識しなくても利用条件を満たせば自動的に使われます。

実際の実行計画を見てみましょう。図8のとおり,RDBMSの宿敵とも言えるテーブルスキャンを高速に処理できます。

図8 パラレルクエリの実行計画

10,000,000行のテストデータに対する実行
demo=# EXPLAIN ANALYZE SELECT id FROM t1 WHERE id % 3 = 0;
                                            QUERY PLAN
-------------------------------------------------------------------------------------------------
Gather (cost=1000.00..151833.03 rows=49999 width=4) (actual time=0.374..2555.601 rows=3333333 loops=1)
  Workers Planned: 2
  Workers Launched: 2
  -> Parallel Seq Scan on t1 (cost=0.00..145833.13 rows=20833 width=4) (actual time=0.048..785.725 rows=1111111 loops=3)
       Filter: ((id % 3) = 0)
       Rows Removed by Filter: 2222222
 Planning Time: 0.168 ms
 Execution Time: 2873.127 ms

JITコンパイラほどではありませんが,パラレルクエリも小さなテーブルでは並列化のメリットよりオーバーヘッドのほうが問題になります。そのため,JITコンパイラと同様に,パラレルクエリを利用する閾値を指定します。パラレルクエリのパラメータは次のとおりです。

max_worker_processes
システム全体で起動できるバックグランドワーカの上限数。これにはパラレルワーカも含み,たとえば設定値が8の場合に,自分が定義したバックグランドワーカを4つ起動させると,パラレルワーカは最大4つしか起動しない。デフォルト値は8
max_parallel_workers_per_gather
パラレルクエリ処理中に起動できるパラレルワーカの上限数。設定値が0の場合,パラレルクエリは実行されない。デフォルト値は2
max_parallel_workers
システム全体で起動できるパラレルワーカの上限数。max_worker_processesの設定値を超えることはできない。デフォルト値は8
parallel_tuple_cost
パラレル処理時にほかのプロセスにデータを受け渡しするのに必要なコストに対するプランナの推測値。デフォルト値は0.1
parallel_setup_cost
パラレル処理を行うプロセスを起動するのに必要なコストに対するプランナの推測値。デフォルト値は1000
min_parallel_table_scan_size
テーブルの参照対象が設定値以上だとパラレルワーカを追加起動する。デフォルト値は8MB
min_parallel_index_scan_size
インデックスの参照対象が設定値以上だとパラレルワーカを追加起動する。デフォルト値は512KB

テストや使うべきと事前にわかっているアドホックなクエリを実行する場合は,force_parallel_mode=onにするとパラレルクエリを利用できます。force_parallel_modeはSET文で設定することもできます。

また,パラレルクエリのワーカ数は一般的にCPUの数と同様にするのが望ましいです。ワーカ数を増やして並列度を上げたい場合は,CPUのコア数が多いサーバを用意しましょう。

パラレルクエリの対象

先述したようにパラレルクエリはPostgreSQL 9.6からありましたが,インデックスを活用できなかったため,対象がテーブルスキャンで実行されるHashJoinやNested Loop Join,集約などのみでした。実際の多くのクエリはインデックスを利用するため,パラレルクエリを活用できるケースは限られていました。

それがPostgreSQL 10,11とバージョンが上がるたびに拡張され,今では多くのケースで実行されます。

10で追加されたパラレルクエリの対象
  • Parallel Index Scan(b-treeのみ)
  • Parallel Index Only Scan(b-treeのみ)
  • サブクエリ
  • Merge Join
  • Parallel bitmap heap scan
11で追加されたパラレルクエリの対象
  • Parallel Hash Join(9.6からより強化)
  • CREATE TABLE AS SELECT
  • CREATE MATERIALIZED VIEW
  • UNION ALLによるAPPEND
  • SELECT INTO
  • CREATE INDEX

一般的に利用する参照部分は,PostgreSQL 11でパラレルクエリの対象になったと言えます。特にJOINやインデックスは日常的に利用されるクエリであるため,パラレルクエリの恩恵を受けることが多いでしょう。

また,パラレルクエリはMySQLにはなく,OSSDBの中でPostgreSQLが大きな優位性を持っている機能の一つです。

まとめ

本章では,PostgreSQLの開発者向けの機能を紹介しました。PostgreSQL 10,11では,今までアプリケーションで四苦八苦してチューニングしたり,シャーディングしていたような問題がRDBMSの機能で対応できるようになっています。これを機に新しいPostgreSQLを使って設計の幅を広げていきましょう。

WEB+DB PRESS

本誌最新号をチェック!
WEB+DB PRESS Vol.111

2019年6月24日発売
B5判/160ページ
定価(本体1,480円+税)
ISBN978-4-297-10657-7

  • 特集1
    新機能の数々をコミッターが最速解説!
    詳解Rails 6
    新コンポーネント,複数DB対応,並列テスト,オートロード刷新
  • 特集2
    動的かつ高速!
    はじめてのJulia
    科学技術計算のための新言語
  • 特集3
    見える化大作戦
    進捗,成果,無理/ムダ,個人の気持ち……
  • 一般記事
    Elm入門
    型安全な関数型言語によるフロントエンド開発

著者プロフィール

曽根壮大(そねたけとも)

株式会社オミカレ副社長兼CTO。数々の業務システム,Webサービスなどの開発・運用を担当し,2017年に株式会社はてなでサービス監視サービス「Mackerel」のCRE(Customer Reliability Engineer)を経て現職。 コミュニティでは,Microsoft MVPをはじめ,日本PostgreSQLユーザ会の理事として勉強会の開催を担当し,各地で登壇している。 builderscon 2017,YAPC::Kansaiなどのイベントでベストスピーカーを受賞し,分かりやすく実践的な内容のトークに定評がある。 他に,岡山Python勉強会を主催し,オープンラボ備後にも所属。著書に『Software Design』誌で,データベースに関する連載「RDBアンチパターン」をまとめた『失敗から学ぶRDBの正しい歩き方』を執筆。

@soudai1025
はてなid:Soudai

著書