postgresql」カテゴリーアーカイブ

PostgreSQL WALの中身を表示する

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

PostgreSQLのWAL周りについて調べる機会がありメモ。

pg_waldump

PostgreSQLでは、WALレコードをダンプするpg_waldumpというツールが用意されています。pg_waldumpはコマンドラインツールで、引数にWALセグメントファイルを指定することで、対象のセグメントのレコードを画面にダーっと出力してくれます。WALの内部を知りたい場合、pg_waldumpは大変便利です。

例えば、こんな感じで使えます。

pg_waldumpが出力する内容は以下の通りです。

rmgr リソースマネージャの種別です。PostgreSQLでは、レコード種別ごとにレコードを処理する関数が用意されており、レコード種別で呼び分けられるようになっています。https://doxygen.postgresql.org/rmgrlist_8h_source.html
len (rec/tot) Full-page image (FPI)を除いた長さとレコード全体長さ
tx トランザクションID
lsn 現在読み取ったレコードのLSN
prev 1つ前のレコードのLSN
desc リソースマネージャが出力するWALレコードに関する情報

他にもトランザクションIDを指定したり、WALの開始や終了位置を指定してダンプすることも可能です。

出力はテキスト形式ですので、コマンドラインで加工したりすることもできます。pg_waldumpを触っている中、ふとSQLでも操作できたら良いなと思い、walの読み取り方法や拡張機能の作り方の学習もかねて、walをSQLで出力できるツールwalreaderを作ってみました。この拡張機能は、あくまで学習用です。あまり慣れていないのでバグがあるかもしれません(きっと)。。

walreader

使い方は簡単です。以下で拡張機能をインストールします。

続いて、拡張機能を有効化して、SELECT文を実行します。

pg_waldumpと少し出力が異なります。出力内容は以下です。

timeline タイムラインID
walseg walファイル名
seg_off walファイル内での先頭からのオフセット
page ページNo
page_off ページ内の先頭からのオフセット
rmgr リソースマネージャ種別
rec_len Full-page image (FPI)を除いたレコード長
tot_len トータルレコード長
tot_rlen アライメントされたトータルレコード長
tx トランザクションID
lsn レコードの開始LSN
end_lsn レコードの終了LSN
prev_lsn 1つ前のレコードの開始LSN
identify リソースマネージャでの操作識別子
rmgr_desc リソースマネージャが出力するWALレコードに関する情報

https://github.com/moritetu/walreader

出力したいwalの範囲指定も想定していますが、タイムラインなどの切り替えは考慮されていないです。

WALの構造

walの中身は以下のような感じになっているようです。

wal structure

参考リンク

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のみを起動します。

状態を確認してみます。

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

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

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以降)

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

参考リンク

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

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

PostgreSQLを調べたりするにあたり,異なるバージョンのPostgreSQLをソースからインストールすることがある。

最初はリポジトリからダウンロードして個別に入れていたけれど,面倒になったので拙作ながらちょっとしたシェルスクリプトを書いてみた。小さなツールだが,私にとっては十分である。環境はBashを想定しており,動作確認はMac OS X上でしか試していない。

xxenvというツールにちなんでpgenvとしてみた。

moritoru81/pgenv

インストール

git cloneして,install.shを叩くかsource install.shする。

使い方

PostgreSQLをインストールする。

指定のバージョンをデフォルトにする。