postgresql」タグアーカイブ

PostgreSQL 11に搭載されるJITコンパイラ機能を動かしてみる

この記事は【2018年7月19日】と作成から2年以上経っているため、記事の内容が古い可能性があります。最新の情報を合わせてご確認されることを推奨いたします。

PostgreSQL 11から、JIT機能が追加されました。

https://www.postgresql.org/about/news/1855/

https://news.mynavi.jp/article/20180324-605157/

あまり理解できていないのですが、どんな感じで動くのか、まずは触ってみたいと思います。

導入と動作確認にあたり、以下の資料を参考にさせていただきました。

https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/07/pg11_report-6.pdf

また、llvmについては参考リンクのページに事前に目を通しておくと、どんな事をしているのかイメージしやすいと思います。llvmのtutorialは、ざっと目を通しておくと良いかもしれません。

インストール

試してみた環境は以下です。

  • Mac OS X mac High Sierra 10.13.5
  • iMac (27-inch Late 2012) Intel Core i5 3.2GHz

llvmのインストール

PostgreSQLは、JITの実装としてデフォルトでLLVMを使用するようになっているようです。私の環境は、Homebrewを使用していますので、LLVMを以下のようにしてインストールします。

参考 src/backend/jit/jit.c

PostgreSQL11beta2のインストール

自作のpgenv2[1]https://github.com/moritoru81/pgenv2でサクッとインストールします。

まず、configureオプションで--with-llvmを追加します。

続いて11beta2をインストールします。

11beta2をデフォルト使用にします。

JITに関連するGUCs

PostgreSQL 11のソースツリーにあるsrc/backend/jit/READMEを参照すると、以下のGUCがjitの動作に関係してくるようです。テストで動作確認したい場合、以下のパラメータに小さなコスト値を設定することで、JITを発動しやすくできます。

jit jitの有効または無効化。デフォルトON(利用可能な場合)
jit_above_cost jitコンパイル適用のコスト閾値。デフォルト100000
jit_optimize_above_cost jitコンパイルされたプログラムを最適化するコスト閾値。デフォルト500000
jit_inline_above_cost jitコンパイルされたプログラムの関数や演算をインライン化するコスト閾値。デフォルト500000

参考 https://www.postgresql.org/docs/11/static/runtime-config-query.html

参考 

JITを動かしてみる

JITが活きてくるのは、CPUバウンドかつ長時間実行されるような処理、とドキュメントでも説明されています。そうでない場合は、JITコンパイルのオーバヘッドの方が大きくなってしまいます。

まずはJITが動くかクエリを実行してみます。GUCでJITが有効になるよう設定して、適当なクエリを実行してみます。演算式や関数が含まれるクエリが良いでしょう。

上記のクエリを実行してみます。explainすると、JIT…という記述があるのが分かります。

次は、JITコンパイルの効果が出ているのがわかるように、もう少し複雑なクエリを実行してみます。(実行するクエリは、参考資料と同様、実際には大容量のデータの入ったテーブルを準備するのでなく、実行時にタプルを生成するようにしています)

私の環境では、結果は以下のようでした(本来は、複数回の計測を行ないばらつきを考慮すべきですが、以下では簡単のため省略しています)

Test クエリ Execution Time (msec)
JIT OFF JIT ON
1 SELECT i + 1 FROM bigtable() AS i; 49630.957 47463.748
2 SELECT i, random() * pi() * exp(i % 2) + log(i % 100 + random() + 1) + sqrt(round(log(i % 100 + 50)))
FROM bigtable() AS i;
68899.182 60690.048
3 SELECT (i % 3) AS G, sum(random() * pi() * exp(i % 2) + log(i % 100 + random() + 1) + sqrt(round(log(i % 100 + 50))))
FROM bigtable() AS i
WHERE i % 3 = 0
GROUP BY G;

 
54232.246 48204.660 

Test1は、単純な加算処理ですが、演算対象の行数が多いためか、JITがONの場合には多少の改善が見られました。

Test2・3では、関数と演算を組み合わせてみましたが、数秒程度の改善が見られました。

厳密なベンチではないので適正な評価はできませんが、どのようなケースでJITが有効か、ざっと雰囲気は掴めたような気がします。

JIT機能を少し追ってみる

『PostgreSQLのJITは、どのような仕組みなのだろうか?』、ざっと雰囲気が掴めたら、次は内部を知りたくなってきます。あまり深いところまで追いきれていませんが、入り口部分を覗いて見ました。

JITプロバイダ

PostgreSQLでは、JIT機能のデフォルト実装はLLVMとなっているが、他のJIT実装も可能なよう設計されている(といっても、私はLLVM以外であまり知りません。。)。

参考 src/backend/jit/jit.c

変数jit_providerで指定される動的ライブラリをロードし、実装ごとの_PG_jit_provider_init関数内でJitProviderCallbacks構造体オブジェクトの関数ポインタに実装ごとの関数のアドレスが登録される。デフォルト実装はLLVMであるが、LLVMの場合はllvmjit.cに_PG_jit_provider_init定義がある。

参考 src/backend/jit/llvm/llvmjit.c

JITプロバイダの初期化は、SQL関数を呼ぶことによって明示的に初期化することもできる。以下を実行すると、backendプロセスで、jit_providerで指定される動的モジュールをロードすることができる。

pg_jit_available関数の呼び出し前後で、lldbで動的ライブラリの適当なシンボルが見えるか確認してみる。以下は、LLVMCreateBuilder関数が見えるかbackendプロセスにアタッチして調べた例である。pg_jit_available関数からprovider_init関数が呼ばれ、llvmjit.soがロードされたことがわかる。

JITの対象

PostgreSQLでは、JIT機能はbackendプロセスで動作し、JITコンパイルの対象は以下となっている。

  1. expression evaluation
    1. WHERE句、ターゲットリスト、集約、射影などの式評価
  2. tuple deforming
    1. ディスク上のタプル表現のインメモリ表現への変換

参考 https://www.postgresql.org/docs/11/static/jit-reason.html#JIT-ACCELERATED-OPERATIONS

JIT Contextと生成されるコード

以降はLLVMを前提で記述する。

JITコンパイルのエントリーポイントは、jit_compile_expr関数である。

1.expression evaluationでは、LLVM IRでevalexpr_<module_generation>_<counter>という関数が生成される。module_generationは、LLVMモジュールの生成数(名前はpg)、counterはオブジェクトの出力回数であり、これらの変数はLLVMJitContext構造体で定義されている。

参考 src/backend/jit/llvm/llvmjit_expr.c

2. tuple deformingでは、LLVM IRでdeform_<module_generation>_<counter>という関数が生成される。

参考 src/backend/jit/llvm/llvmjit_deform.c

LLVMJitContextは、plan実行時に作成され、トランザクションの終わりで解放される。

参考 src/backend/executor/execMain.c

1、2で生成されるLLVM IRをみるには、jit_dump_bitcodeパラメータをonにすればよい。パラメータをonにして、JITが働くクエリを実行すると、pgdata下に.bcファイルが生成される。.llで人が読める形式に変換するにはllvm-disを実行する。

試しに以下のようなクエリを実行してみる。

私の実行した環境では、以下のファイルが生成された。

  • 90645.14.bc
  • 90645.14.optimized.bc

optimizedは最適化されたビットコードである。90645.14.bcをllvm-disでテキストとして読めるLLVM IR形式に変換してみた結果が以下である。

最初の%で始まる定義は、型定義である。LLVM IRでは、%で始まる名前はローカル識別子、@で始まる名前はグローバル識別子を示している[2]https://llvm.org/docs/LangRef.html#identifiers

71行目付近に関数定義があり、evalexpr_xx_xという名前になっていることがわかる。

LLVM IRを生成しているllvm_compile_expr関数をみると、LLVMのIRの構造に沿って、コード作成されていくことがわかる。

LLVMModuleCreateWithNameでModuleを作成し、LLVMAddFunctionでModuleにFunctionを追加し、LLVMAppendBasicBlockでBasicBlockを追加、BuilderでInstructionの構築・・・といった感じである。

参考リンク

 

 

きつねさんでもわかるLLVM ~コンパイラを自作するためのガイドブック~
柏木 餅子 風薬
インプレス
売り上げランキング: 87,944

複数バージョンのPostgreSQLを入れる2

この記事は【2018年6月23日】と作成から2年以上経っているため、記事の内容が古い可能性があります。最新の情報を合わせてご確認されることを推奨いたします。

拙作なツールですが、複数バージョンのPostgreSQLをローカルマシンに入れて、テストや動作確認でバージョンを切り替えたりするツールを以前に作成したことがありましたが、色々と使い勝手も含めて改良したいなあと思っていたこともあり、今回新たに作り直してみました。

動作確認は自分のMac環境でしかできていませんが、それなりに動いて重宝しているので、以下に概要をまとめてみました。

pgenv2

https://github.com/moritoru81/pgenv2

動作環境

  • Bash 4.2 or later(4.4以上を推奨)

インストール

リポジトリをクローンして、install.shでパスを通すだけです。設置場所は、任意です。

機能

今のところ以下ができます。ざっくりと言うと、xxenvと言われるソフトウェアと類似の機能があります。

  • PostgreSQLのインストール(ソースアーカイブをダウンロードしてビルド)
  • インストールした複数バージョンのPostgreSQLの切り替え
  • ローカルマシン上でのレプリケーション環境の構築

利用可能なバージョン一覧の表示

公式サイトのWebページからリリースされているバージョン一覧を取得して表示します。

インストール時に、ここで表示されるバージョンを指定します。

指定のバージョンのPostgreSQLをインストールする

pgenv versionsで利用可能なバージョン一覧を確認できたら、次はインストールします。以下では、10.1をインストールしています。

コマンドを実行すると、10.1のソースアーカイブをダウンロードして、展開、configuremakeと実行していきます。ビルドのログは、pgenv/logsにバージョンの名前で出力されます。

configureのオプションは、pgenv/configure_optionsにデフォルトの条件が書いてあります。pgenvのconfigureコマンドで編集ができます。$EDITORで指定されるエディタでファイルを編集します。

無事にインストールできたら、以下でバージョン確認ができます。

上記では、現在10.3が有効ですが、10.1に切り替えてみます。

切り替わったか確認します。

インストール場所を確認してみます。

レプリケーション環境の構築

PostgreSQLには、レプリケーション機能がありますが、サクッと手元で環境を構築し、動作確認したいことがあります。pgenv2には、そういった機能があります。

1対1の同期レプリケーションを組みたい場合、以下のようにできます。

上記例では、fooというディレクトリに、各種インスタンスディレクトリが作成されます。

プライマリは「primary」、同期スタンバイは「standby」としています。各ノードの設定は、基本的に外出しで、includeパラメータで読み込ませています。port番号は、デフォルトでは24312から順にインクリメントして使用しています。

以下で構築した環境の状態を確認してみます。primary -> standbyはレプリケーション関係を示しています。pg_stat_replicationビューの情報をもとにしています。

別ターミナルで、各種インスタンスのログを表示しておきます。

続いて、スタンバイを昇格させてみます。

ログも出力されていることを確認します。

レプリケーション状態を見てみます。

続けて、standbyに新しく非同期スタンバイを追加してみます。

非同期スタンバイを起動します。非同期スタンバイ追加時にも-Sオプションで同時に起動できます。

レプリケーション状態を確認してみます。

別の方法でも確認してみます。

インスタンスを停止します。

昇格させたstandbyと非同期レプリケーションstandby2のみを起動します。

状態を確認してみます。

と、こんな感じでレプリケーションの動作確認を手元ですぐに確認できます。

ちょっとした拡張機能の確認

OSS-DB GOLDを取得!

この記事は【2018年6月3日】と作成から2年以上経っているため、記事の内容が古い可能性があります。最新の情報を合わせてご確認されることを推奨いたします。

以前OSS-DB Silver試験に合格してから、次は『OSS-DB Goldにチャレンジだ!』と、なんとか2018年上半期には合格したいと学習を進めていましたが、本日受験をし無事合格することができました 🙂 

しかし、結果は、以下の通り合格点ギリギリ・・・だったので、知識の見直しが必要です。。

結果・概要

スコア:73点(合格ライン:70点)

1.運用管理 ・・・ 77%
2.性能監視 ・・・ 66%
3.パフォーマンスチューニング ・・・ 66%
4.障害対応 ・・・ 83%

受験時スペック

  • 普段の仕事
    データベース関連の開発やテストなどに携わっており、主にPostgreSQLが中心。
    PostgreSQLを触るようになって、1年と数ヶ月。
    IT業界でのエンジニアとしての経歴は10年ほど。
  • 受験回数
    1回目
  • データベースに全般に関する知識
    Webアプリケーション開発に10年ほど携わっていたので、基本的な知識はある。
    DB管理者としての経験はほとんどなし。
    IPAの試験「データベーススペシャリスト」を学習していたので、浅く全般的な基礎知識はあり。
  • 受験時の知識
    参考書+OSS-DB Goldのサンプル例題は一通り解いて理解した状態

使用した参考書

基本的には、以下のページを参考にして選択しました。しかし、個人では高額でなかなか手を出しづらいものもあるため、個人で購入可能なレベルの参考書にすることにしました。

LPI-Japan OSS-DB Gold 認定教材 PostgreSQL 高度技術者育成テキスト

LPI-Japan OSS-DB Gold 認定教材 PostgreSQL 高度技術者育成テキスト
河原 翔
エヌ・ティ・ティ・ソフトウェア株式会社 (2014-10-27)
売り上げランキング: 7,592

メインの参考書として使用したのはこちらです。LPI-Japan公認のOSS-DB Gold学習教材であり、個人でも購入可能なレベルであったため、上記参考書を選択しました。

パワーポイントのスライドにまとめられた要点とノート部分の補足説明で構成されています。また、末尾に各出題分野の例題が掲載されています。私は、テキストを読み込んだ後、OSS-DB Goldのサイトのサンプル例題と合わせて、本書の末尾の例題を解いて、解説をじっくり読み理解するようにしました。

PostgreSQL全機能バイブル

PostgreSQL全機能バイブル
PostgreSQL全機能バイブル

posted with amazlet at 18.06.03
鈴木 啓修
技術評論社
売り上げランキング: 63,557

本書は、試験の参考書としては使用していませんが、PostgreSQLを扱うなら持っておいた方が良い一冊です。OSS-DB Silverの時もざっと眺めて、機能理解のために使用していました。表紙に記述されている通り、バージョン9.2まで対応しています。

現在のOSS-DB Goldの試験は、

本試験はOSS-DBのなかでも、特に商用データベースとの連携に優れ、エンタープライズ・システムでも多く活用されている「PostgreSQL 9.0以上」を基準のRDBMSとして採用しています。
(※)2016年現在、9.4まで対応しております。

とあるため、本書は活用できると思います。(注:試験詳細は、公式サイトで確認してください

現在、PostgreSQLの最新バージョンは10であり、新規機能やパラメータや関数名など、色々と変わっているところもあるため、最新版のPostgreSQLの内容を取り込んだ新版の出版を切望しています。

サンプル問題/例題解説|受験対策|DBスペシャリストを認定する資格 OSS-DB技術者認定試験

OSS-DBのサイトのサンプル問題と解説です。こちらも解説をしっかり読み込みました。全問正解できるようになるまで、繰り返し問題を解きました。

https://oss-db.jp/measures/sample.shtml

その他資料など

様々な上級技術者の方のスライドなども参考になりました。

『OSS-DB Exam Gold 技術解説無料セミナー』のイベント資料

LPI-Japan様が、OSS-DB技術者認定に関心のある人向けに主催されているイベントの資料です。この資料も大変有用なため、ざっと目を通して理解を深めるようにしました。

PostgreSQL公式ドキュメント

知らない機能は、公式ドキュメントを参照し、理解を深めておきます。

受験の感想

試験時間は90分ですが、時間は十分ありました。知らないと問題に解答できず、悩んでもなかなか答えが出てこないものが多々あります。そう言った問題は、原理から考えて導くようにしました。また、普段から実機を触って理解を深めておくことも重要だと思います。

試験を終えてみて、マニュアルや様々な資料に目を通したり、実機を触って動作を確認してみたりすることで、以前に比べてそれなりにスキルがついたように感じます。どの試験にも言えるとは思いますが、試験のための勉強でなく、しっかりと仕組みを理解し、実用を意識して学習することが大切と感じました。

OSS-DBとしてはGoldが最上位資格になるため、OSS-DB受験はここまでとなりますが、引き続き、開発や運用操作を通じて、さらなる技術力アップを目指して行きたいと思います。

参考リンク

 

pgpool-IIの3.6以降におけるフェイルオーバー時のクライアントセッション

この記事は【2017年11月12日】と作成から2年以上経っているため、記事の内容が古い可能性があります。最新の情報を合わせてご確認されることを推奨いたします。

pgpool-II 3.6以降のフェイルオーバー時のクライアントセッションについての検証。

ストリーミングレプリケーションモードにおいて、pgpool2では、データベース障害が発生した場合、障害の起きたデータベースサーバの切り替え(プライマリ)又は切り離し(スタンバイ)を行なってくれる。3.5より前は、pgpool2の子プロセスは全て再起動されるため、クライアントのセッションも全て切断されていた。しかし、3.6以降はセッションの切断の影響を最小限にするための改良が行われているようで、障害の起きたデータベースサーバがスタンバイである且つセッションで負荷分散対象先でない場合において、クライアントセッションは切断されないようになった(他の改善点などは、以下リリースノートを参照)。

http://www.pgpool.net/docs/latest/ja/html/release-3-6.html

ということで、この挙動について理解を深めるべく検証してみる。

環境構築

今回は、pgpool_setupコマンドを使って、同一ノード上でPostgreSQLのプライマリ及びセカンダリノード、pgpool2を動かした状態で確認した。以下環境である。

  • Mac OS X
  • pgpool2 3.6.6
  • PostgreSQL 9.5.6

psコマンドで確認しやすいよう、num_init_childrenは1、max_poolは2とした。

負荷分散先のスタンバイサーバがダウンした場合

まずプロセスツリーを確認しておく。

クライアント接続

同一ノード上からpsqlコマンドで接続した。JDBCのようなドライバでは拡張プロトコルとなるが、拡張プロトコルでは、show pool_nodesコマンドが使えない。よって、psqlコマンドを使用している。

psqlから接続した状態は以下である。showコマンドで負荷分散先が表示される。負荷分散先は、load_balance_nodetrueとなっていることがわかる。このセッションでは、PostgreSQLのセカンダリサーバが負荷分散先となっていることがわかる。

プロセスの状態は以下のようになっている。プライマリ、セカンダリサーバのそれぞれにデータベースコネクション接続があることがわかる。

スタンバイサーバダウン

上記の状態でスタンバイサーバをダウンさせる。

シャットダウン直後のプロセスツリーは以下。データベースのバックエンドプロセスは消えている。また、子プロセスは再起動され、プロセスIDが新しくなっていることが確認できる(1つは子プロセス、あとはPCPとworker)。

クライアントのセッションは切断されている、リトライでは成功する。

負荷分散先でないスタンバイサーバがダウンした場合

プロセスツリーの確認。

クライアント接続

続いて、クライアントセッションの負荷分散先を確認し、トランザクション開始。

スタンバイサーバダウン

以下のコマンドで、スタンバイサーバの即時停止。

スタンバイサーバダウン直後のプロセスツリー。pgpool2の子プロセス(PID:47185)は残っており、またプライマリサーバのバックエンドプロセスも残ったままとなっていることが確認できる。

クエリも引き続き実行でき、クライアントセッションは切断されていない。

クライアントのセッションが終了すると、該当する子プロセスは再起動される。

プライマリサーバがダウンした場合

この場合は、負荷分散先に関わらずクライアントのセッションは切断される。

プロセス状態。

クライアント接続

負荷分散先をセカンダリサーバにした状態で行なう。

プライマリサーバダウン

プライマリサーバダウン後の切り替え後のプロセス状態。pgpool2の子プロセスは再起動されている。

クライアントセッションは切断されるが、リトライで成功する。

まとめ

以上まとめると、以下のようになっている。(と思われる、3.6以降)

プライマリ セカンダリ
障害発生 ダウン ダウン
負荷分散対象 ✖️
クライアントセッション ✖️ ✖️

参考リンク

Xcodeで外部ソース(postgresql)をビルドする

この記事は【2017年1月6日】と作成から2年以上経っているため、記事の内容が古い可能性があります。最新の情報を合わせてご確認されることを推奨いたします。

PostgreSQLを少し触る機会があり,ソースも少し追っかけてみたい(が膨大で難しい。。)と思いデバッグ環境を構築することに。

ターミナルでgdbでステップ実行するのもあまり慣れていないので,Xcodeでと試した時のメモです。

環境

  • MacOS X 10.11.6
  • Xcode8.2.1
  • postgresql-9.5.5

作成手順

ソースコードジャンプ

まず適当に空のプロジェクトを作ります。

続いてソースコードなどをインデクシングしてジャンプできるようにダミーのターゲットを作成します。

続いてダミーのターゲットのCompile Sourcesでpostgresqlのsrcフォルダ下の.cファイルを追加します。Add Otherで,FinderのSearchに.cと打つとCソース一式を絞り込めます。(他に良い方法あれば教えて欲しいです。。)

すべてのソースを選択してxcodeのCompile Sourcesに表示されるまで少し時間がかかるかもしれません。

続いて,PROJECTのBuild Settingsで「User Header Search Paths」にpostgresqlのsrc/includeディレクトリを指定します。

これでソースにジャンプできるようになっているはず。

ステップ実行

続いてブレークポイントを貼ってステップ実行するための設定。

まず,TARGETSを追加してExternal Build Systemを選択します。

続いて実行ディレクトリを指定します。

次にEdit SchemeでPre-actions,Post-actionsのスクリプトを指定します。Pre-actionsではconfigureの実行,Post-actionsではmake installを実行しています。

あとはターゲットを実行します。上手くいくとprefixで指定したディレクトリに実行ファイル等一式が生成されているはずです。

続いて試しにinitdbを実行してみます。まずは,ターゲットを作ります。

続いてEdit Schemeでデバッグビルドした実行ファイルを選択します。

実行時の引数や環境変数を設定します。

以上を設定の上,ブレークポイントを適当な場所に貼ってターゲットinitdbを実行してみます。

指定した箇所でブレークできました!上手くいったようです。

プロセスにアタッチ

psqlで対話インターフェースを起動し,クエリを実行するところでブレークしてステップ実行してみます。

まずは,コンソールでpsqlを実行します。

続いてXcodeからプロセスにアタッチします。プロセスIDはpostgresで最新のプロセス番号になっていると思いますが,psでも確認しておきます。

以下のようなプロセスが見つかると思います。

IDが分かればXcodeでアタッチしてみます。

何も起こったように見えませんが,ポーズボタンを押してプログラムの実行を中断してみます。