tag:blogger.com,1999:blog-91376977378172796072024-02-21T00:26:36.256+09:00Fujii Masao's BlogFujii Masaohttp://www.blogger.com/profile/16606001129433709869noreply@blogger.comBlogger3125tag:blogger.com,1999:blog-9137697737817279607.post-64278639918973394302013-01-11T02:29:00.002+09:002013-01-11T02:29:51.705+09:00pg_hint_planを試してみる昨年12月にOSS公開されたばかりの新ツールpg_hint_planを試してみます。このツールは、PostgreSQLでHINT機能を使えるようにするものです。<br />
<br />
<h2>
<u>前置き</u></h2>
これまでは、どのSQL実行計画を選ぶかはPostgreSQLのプランナ任せでした。プランナが期待どおりの実行計画を選んでくれずに、性能が出ずに困ったという場面があったと思います。例えば、インデックスを使って検索してほしいのに、なぜかシーケンシャルスキャンが選ばれたしまったというようなケースです。<br />
<br />
pg_hint_planを使うと、PostgreSQLのプランナに対して、どのような実行計画を選ぶべきか指示(HINT)を与えることができます。つまり、どのような実行計画でSQLを実行するかユーザがほぼ思いどおりに制御できるということです。これにより、プランナが望ましくない実行計画を選んでしまう、上記の性能問題を解決することができます。<br />
<br />
ただし、HINTは魔法の杖ではありません。OracleにもHINT機能はあり、使っている人は多いですが、HINTを使ったがために性能問題にはまるというケースをよく聞きます(具体的にどんなトラブルがあるかはまた別の日に)。PostgreSQLコミュニティもHINTは基本的にトラブルの素と考えており、PostgreSQL本体ではHINTをサポートしていません。将来的にサポートの予定も一切ありません。コミュニティのHINTに対する考え方はWiki(<a href="http://wiki.postgresql.org/wiki/OptimizerHintsDiscussion">http://wiki.postgresql.org/wiki/OptimizerHintsDiscussion</a>)に整理されています。pg_hint_planを使おうと考えている方は、一度このWikiページを読んで、本当にHINTが必要なのか検討するとよいと思います。<br />
<br />
コミュニティには反対意見が多いですが、やはりチューニングの最終兵器としてHINTを使いたいというようなユーザからのアツい要望があり、pg_hint_planは開発されたという経緯になっています。HINTはトラブルの素になるかもというリスクを理解した上で、pg_hint_planを使いましょう。<br />
<br />
<h2>
<u>インストール</u></h2>
前置きが長くなりましたが、以下、pg_hint_planを試してみます。<br />
<br />
pg_hint_planのサイトは<a href="http://en.sourceforge.jp/projects/pghintplan/">http://en.sourceforge.jp/projects/pghintplan/</a>です。 ここからpg_hint_planのソースのtarボールをダウンロードします。今回はPostgreSQL9.2でHINTを使うので、pg_hint_plan92-1.0.0.tar.gzをダウンロードします。<br />
<br />
tarボールを展開します。<br />
<br />
<pre> $ tar zxf pg_hint_plan92-1.0.0.tar.gz
$ ls pg_hint_plan92-1.0.0
COPYRIGHT core-9.1.c expected pg_hint_plan.c
COPYRIGHT.postgresql core-9.2.c input sql
Makefile data make_join_rel.c
SPECS doc output</pre>
<br />
pg_hint_planのマニュアルは、docディレクトリ内にあります。使い方や制約などが詳しく載っているので、見ておきましょう。<br />
<br />
次にpg_hint_planをインストールします。<br />
<br />
<pre> $ cd pg_hint_plan92-1.0.0
$ make USE_PGXS=1 PG_CONFIG=/Users/postgres/pgsql/9.2/pgsql-9.2.2/bin/pg_config
$ make USE_PGXS=1 PG_CONFIG=/Users/postgres/pgsql/9.2/pgsql-9.2.2/bin/pg_config install
</pre>
<br />
USE_PGXSを使ってインストールできます。PG_CONFIGの指定は、PostgreSQLのbinディレクトリにPATHが通っているのであれば不要です。私の環境では、PATHを通していないために、PostgreSQL9.2のpg_configコマンドを指定しています。<br />
<br />
次にpg_hint_planをロードします。postgresql.confでshared_preload_librariesにpg_hint_planを設定します。<br />
<br />
<pre> $ emacs $PGDATA/postgresql.conf
shared_preload_libraries = 'pg_hint_plan'
</pre>
<br />
これでpg_hint_planを使うための準備が整いました。PostgreSQLを再起動すれば、pg_hint_planがロードされてHINTが使えるようになります。<br />
<br />
<pre> $ pg_ctl -D $PGDATA restart
</pre>
<br />
<h2>
<u>HINTを使ってみる</u></h2>
HINTを使って、プランナにインデックススキャンを選ばせてみましょう。<br />
<br />
<pre> $ psql
=# CREATE TABLE tbl (i int, j int);
=# INSERT INTO tbl SELECT x, x * 2 FROM generate_series(1, 10) x;
=# CREATE INDEX idx ON tbl (i);
=# VACUUM ANALYZE tbl;
※テーブル内のデータ件数が少ないため、単純な検索ではシーケンシャルスキャンが選ばれる
=# EXPLAIN SELECT * FROM tbl WHERE i = 7;
QUERY PLAN
---------------------------------------------------
Seq Scan on tbl (cost=0.00..1.12 rows=1 width=8)
Filter: (i = 7)
(2 rows)
※HINTでインデックススキャンを選ぶように指定
=# /*+ IndexScan(tbl) */ EXPLAIN SELECT * FROM tbl WHERE i = 7;
QUERY PLAN
---------------------------------------------------------------
Index Scan using idx on tbl (cost=0.00..8.27 rows=1 width=8)
Index Cond: (i = 7)
(2 rows)
</pre>
<br />
HINTは、/*+ */のコメント内に記述します。これは、OracleのHINTに似た構文なので、Oracleユーザも取っ付きやすいかもしれません。インデックススキャンをプランナに選ばせたい場合は、検索対象のテーブル名を括弧で囲みながら、IndexScanのキーワードを指定します。括弧内で、テーブル名に続けてインデックス名を指定することで、インデックスが複数定義されていたときに、どのインデックスでインデックススキャンをしたいのかを指定することもできます。<br />
<br />
<pre> ※インデックスhogeidxを使ってインデックススキャンをするようにHINT指定
=# CREATE INDEX hogeidx ON tbl (i);
=# /*+ IndexScan(tbl hogeidx) */ EXPLAIN SELECT * FROM tbl WHERE i = 7;
QUERY PLAN
-------------------------------------------------------------------
Index Scan using hogeidx on tbl (cost=0.00..8.27 rows=1 width=8)
Index Cond: (i = 7)
(2 rows)
</pre>
<br />
pg_hint_planは、OracleのHINTで特に使われることの多いものをサポートしています。例えば、以下です。<br />
<ul>
<li>スキャン方法の指定</li>
<ul>
<li>シーケンシャルスキャンの選択 → SeqScan(テーブル)</li>
<li>インデックススキャンの選択 → IndexScan(テーブル[ インデックス...])</li>
<li>インデックスオンリースキャンの選択 → IndexOnlyScan(テーブル[ インデックス...]) </li>
</ul>
<li>結合順序の指定</li>
<ul>
<li>テーブルの結合順序を指定 → Leading(テーブル テーブル[ テーブル...]) </li>
</ul>
<li>結合方法の指定</li>
<ul>
<li>ネストループの選択 → NestLoop(テーブル テーブル[ テーブル...])</li>
<li>ハッシュ結合の選択 → HashJoin(テーブル テーブル[ テーブル...])</li>
<li>マージ結合の選択 → MergeJoin(テーブル テーブル[ テーブル...])</li>
</ul>
</ul>
<br />
指定できるHINTの詳細については、pg_hint_plan付属のマニュアルを参照してください。<br />
<br />
<h2>
<u>まとめ</u></h2>
pg_hint_planを使うことで、チューニングの幅が大きく広がります。また、現状のPostgreSQLではどうしても解決できなかった性能問題を解決できるかもしれません。非常に強力なツールなので、ぜひ使ってみてください!Fujii Masaohttp://www.blogger.com/profile/16606001129433709869noreply@blogger.com0tag:blogger.com,1999:blog-9137697737817279607.post-8889258268033423342012-12-09T00:32:00.004+09:002012-12-09T00:37:14.589+09:00pg_receivexlogで、リアルタイムにWALアーカイブこの記事は、<a href="http://atnd.org/events/34176">PostgreSQL Advent Calendar 2012</a>の12/8担当分です。<br />
<br />
今日は、PostgreSQL9.2の新機能pg_receivexlogを使って、リアルタイムにWALファイルをアーカイブする方法を検討してみたいと思います。<br />
<br />
<h4>
pg_receivexlogとは?</h4>
<br />
pg_receivexlogは、バージョン9.2からPostgreSQLに同梱されたコマンドです。このコマンドは、レプリケーションのスタンバイ側で、WALの受信と書き込みを繰り返すwalreceiverプロセスをツールとして切り出したものです。このコマンドにより、レプリケーションと同様に、リアルタイムにWALを任意の場所に転送し続けることができます。<br />
<br />
pg_receivexlogの詳細は、<a href="http://www.postgresql.jp/document/9.2/html/app-pgreceivexlog.html">pg_receivexlogの日本語ドキュメント</a>と<a href="http://lets.postgresql.jp/documents/technical/9.2/1">Let's PostgresのPostgreSQL9.2新機能紹介の記事</a>を参考にしてください。<br />
<br />
<br />
<iframe allowfullscreen="allowfullscreen" frameborder="0" height="356" marginheight="0" marginwidth="0" mozallowfullscreen="mozallowfullscreen" scrolling="no" src="http://www.slideshare.net/slideshow/embed_code/14799273?startSlide=21" style="border-width: 1px 1px 0; border: 1px solid #CCC; margin-bottom: 5px;" webkitallowfullscreen="webkitallowfullscreen" width="427"> </iframe> <br />
<div style="margin-bottom: 5px;">
<b> <a href="http://www.slideshare.net/MasaoFujii/postgresql-14799273" target="_blank" title="PostgreSQLレプリケーション徹底紹介">PostgreSQLレプリケーション徹底紹介</a> </b> from <b><a href="http://www.slideshare.net/MasaoFujii" target="_blank">Masao Fujii</a></b> </div>
<br />
<h4>
WALアーカイブの問題点</h4>
<h4>
</h4>
WALアーカイブには、ディスク故障時にデータが失われるかもしれないという問題点があります。以下、簡単に説明します。<br />
<br />
まず、WALの書き込みとアーカイブは、以下のように行われます。<br />
<br />
<ol>
<li>16MBいっぱいになるまで、pg_xlogディレクトリ内のWALファイルにWALを書き込み続ける</li>
<li>WALファイルが16MBいっぱいになったら、archive_commandによりそのWALファイルをアーカイブする</li>
<li>WALの書き込み先をpg_xlog内の次のWALファイルに切り替えて、1.に戻る</li>
</ol>
<br />
WALファイルがアーカイブされるのは、WALが16MB書き込まれるごとになります。このため、もしWALを16MB書き終わる直前に、ディスク故障などによりpg_xlogが破損すると、 バックアップから復旧できるのは前回アーカイブされたWALファイルまでになり、故障直前まで書き込んでいた16MB分のデータは完全に失われてしまいます。これがWALアーカイブの問題点です。<br />
<br />
今回は、pg_receivexlogを使ってWALをリアルタイムにアーカイブ先に転送することで、このようなデータ損失が発生するのを回避します。<br />
<br />
<br />
<h4>
pg_receivexlogによるリアルタイムなWALアーカイブ</h4>
<h4>
</h4>
では、リアルタイムにWALアーカイブを行う環境を構築していきましょう。pg_receivexlogを使う必要があるため、当然環境構築にはPostgreSQL9.2を使います。まずは、通常どおりDBクラスタとWALアーカイブ先のディレクトリを作成します。<br />
<br />
$ initdb -D data --encoding=UTF8 --locale=C<br />
$ mkdir /mnt/archive <br />
<br />
次に、WALアーカイブの設定を行います。<br />
<br />
$ vi data/postgresql.conf<br />
wal_level = archive <br />
archive_mode = on<br />
archive_command = 'pg_check_archive.sh /mnt/archive %f %p'<br />
<br />
archive_commandに設定するスクリプトpg_check_archive.shを以下のとおり用意します。<br />
<br />
$ vi my_archive_command.sh<br />
#!/bin/sh<br />
<br />
ARCHIVE=$1<br />
WALFILE=$2<br />
WALPATH=$3<br />
<br />
case $WALFILE in<br />
*.*) <br />
cp $WALPATH $ARCHIVE/$WALFILE<br />
;;<br />
*)<br />
sleep 5 && test -f $ARCHIVE/$WALFILE<br />
;;<br />
esac<br />
<br />
$ chmod 744 my_archive_command.sh<br />
<br />
このスクリプトの特徴は次のとおりです。<br />
<ul>
<li>このスクリプトには、第1引数にアーカイブ先のパスを、第2引数にWALファイル名(%f)を、第3引数にアーカイブするWALファイルのパス(%p)を指定する。</li>
<li>WALファイルのアーカイブはpg_receivexlogが行うため、このスクリプトにアーカイブのためのコマンドを設定する必要はない。このスクリプトには、pg_receivexlogがWALファイルをアーカイブしたことを確認するコマンドを設定する。</li>
<li>WALファイルのアーカイブを確認する前に、5秒間のスリープを追加する。archive_commandは、WALファイルが16MB書き終わるとすぐに実行される。一方、16MB書き終わってから、pg_receivexlogによるWAL転送が終わるまでの間には若干のタイムラグがある可能性がある。このタイムラグの間にアーカイブの完了確認が走らないように、5秒間スリープするようにする。</li>
<li>pg_receivexlogは、WAL以外のファイル(バックアップ履歴ファイルやタイムライン履歴ファイル)を転送しない。このため、それらのファイルについては、このスクリプトがアーカイブを行うようにする。 </li>
</ul>
<br />
次に、pg_receivexlogからのレプリケーション接続を受け付けられるように、マスタサーバとしての設定をpostgresql.confとpg_hba.confに行います。<br />
<br />
$ vi data/postgresql.conf<br />
max_wal_senders = 2<br />
<br />
$ vi data/pg_hba.conf<br />
local replication postgres trust <br />
<br />
PostgreSQLとpg_receivexlogを起動します。なお、PostgreSQLを停止するときは、pg_receivexlogも忘れずに停止するようにしましょう。pg_receivexlogは、Ctrl-Cで停止できます。<br />
<br />
$ pg_ctl -D data -w start<br />
$ pg_receivexlog -D /mnt/archive<br />
<br />
以降、WALは、pg_receivexlogによりリアルタイムに/mnt/archiveに転送されます。実際にWALを発生させて、どのようにWALがアーカイブされるのか確認してみましょう。WALの生成には、pgbenchの初期データ作成を使います。<br />
<br />
$ pgbench -i -s5<br />
$ ls data/pg_xlog<br />
000000010000000000000001 000000010000000000000003 000000010000000000000005<br />
000000010000000000000002 000000010000000000000004 archive_status<br />
<br />
$ ls /mnt/archive<br />
000000010000000000000001 000000010000000000000003 000000010000000000000005.partial<br />
000000010000000000000002 000000010000000000000004 <br />
<br />
拡張子が.partialのWALファイルは、WALを書き込み途中(転送途中)であることを意味します。WALが16MBいっぱい書き込まれると、そのWALファイル名からは.partialは取り除かれ、次のWALファイルが.partialの拡張子で作成されます。<br />
<br />
<h4>
リカバリ</h4>
<br />
リアルタイムにWALをアーカイブする環境でも、バックアップからのリカバリ手順は基本的に通常のものと同じです。ただし、pg_xlogが破損した状況でバックアップからリカバリを行う場合(アーカイブのWALファイルのみでリカバリを行う場合)では、書き込み途中のWALファイルもリカバリ対象とするために、リカバリ開始前に.partialの拡張子をファイル名から取り除く必要があります。 <br />
<br />
<h4>
まとめ</h4>
<br />
pg_receivexlogによるリアルタイムなWALアーカイブは、archive_command用の特別なスクリプトを用意する必要があったり、PostgreSQLの起動/停止と同時にpg_receivexlogも起動/停止する必要があるなど、面倒な点がまだ多いです。しかし、故障時にデータが失われるのを回避できるため、トライしてみる価値はありそうです。Fujii Masaohttp://www.blogger.com/profile/16606001129433709869noreply@blogger.com0tag:blogger.com,1999:blog-9137697737817279607.post-36124510377893365852011-12-08T23:30:00.000+09:002011-12-08T23:34:49.533+09:00PostgreSQL レプリケーションの Q&A PostgreSQL Advent Calendar #8この記事は、<a href="http://atnd.org/events/21994">PostgreSQL Advent Calendar</a> の 12/8 担当分です。<br />
<br />
只今 12/8 21 時。12/8 も残り 3 時間というところで何もいいネタが思い浮かばなかったので、とりあえずレプリケーション関係について Q&A を殴り書きしていきます。<br />
<br />
<ul>
<li>Q. PostgreSQL 内蔵のレプリケーションってある?</li>
<li>A. あります。バージョン 9.0 から使えます。</li>
</ul>
<ul>
<li>Q. レプリケーションの同期モードは?</li>
<li>A. v9.0 は非同期のみ、v9.1 以降は同期と非同期を選択できます。</li>
</ul>
<ul>
<li>Q. スタンバイでは SQL を実行できる?</li>
<li>A. 参照形の SQL だけですが、スタンバイで実行できます。実行できる/できない SQL は、<a href="http://www.postgresql.jp/document/current/html/hot-standby.html">マニュアル</a>に記載されています。</li>
</ul>
<ul>
<li>Q. どうやってフェイルオーバさせるの?</li>
<li>A. スタンバイで、trigger_file というパラメータに設定したファイルを作成することで、スタンバイをマスタに切り替えることができます。v9.1 であれば、trigger_file の作成以外に、pg_ctl という監視コマンドに対して promote というサブコマンドを指定して実行するだけで切り替えることができます。 </li>
</ul>
<ul>
<li>Q. マスタが故障したときに自動的にフェイルオーバさせるとかできる?</li>
<li>A. PostgreSQL だけではできません。pgpool-II や Pacemaker などのクラスタ製品と組み合わせる必要があります。</li>
</ul>
<ul>
<li>Q. Pacemaker と組み合わせることってできるの?</li>
<li>A. PostgreSQL のレプリケーションを制御するための RA (リソースエージェント) を作成しないと組み合わせられません。ただ、Linux-HA Japan のコミュニティで、<a href="http://sourceforge.jp/projects/linux-ha/lists/archive/japan/2011-July/000775.html">そんなような RA を作る動き</a>があります。 </li>
</ul>
<ul>
<li>Q. 一部のテーブルだけレプリケーションさせるってできる?</li>
<li>A. できません。データベース全体がレプリケーション対象になります。ただ、v9.1 では UNLOGGED TABLE という WAL を発生させないテーブルを定義でき、このテーブルはレプリケーションされません。UNLOGGED TABLE のテーブルは WAL を一切発生させないため、PostgreSQL がクラッシュしたとき、リカバリをしてもデータは復元できません。クラッシュ時にデータが失われてもいいから、レプリケーションさせたくないといったテー ブルがあれば、UNLOGGED TABLE を使えます。 </li>
</ul>
<ul>
<li>Q. 同期レプリケーションってどこまでデータを保証するの?</li>
<li>A. マスタで実行されたトランザクションは、そのトランザクションの WAL (トランザクションログ) がスタンバイのディスクに同期書き込み (fsync) されるまで待ちます。つまり、クライアントから見ると、トランザクションのコミットの成功応答を受け取ったとき、マスタとスタンバイの両方のディスクに WAL が書き込まれていることを保証できます。WAL がスタンバイのディスクに書き込まれているということは、マスタが故障しても、スタンバイにフェイルオーバすることで、コミットされたデータを見ることが できます。 </li>
</ul>
<ul>
<li>Q. 同期レプリケーションで、コミットされたデータをすぐにスタンバイで見られる?</li>
<li>A. 基本的にはすぐに見られます。けど、見られないこともあります。↑で回答しているように、同期レプリケーションでは、コミットが完了した時点で、そのトラ ンザクションの WAL がスタンバイのディスクに書き込まれていることまでしか保証しません。その WAL がリカバリされ、スタンバイで参照 SQL を実行して見られるようになっているかどうかまでは責任を持ちません。ただし、ディスクに書き込まれた WAL は速やかにリカバリされるので、基本的にはすぐに参照 SQL を投げてもスタンバイからデータを見られます。 </li>
</ul>
<ul>
<li>Q. WAL がリカバリされるまで待つ同期モードって作らないの?</li>
<li>A. v9.1 の開発途中にはそんなような話もありました。けど、まずは一番シンプルな (WAL を同期書き込みするまで待つ) 同期モードを開発して、徐々に複雑なモードを入れていこうということで、開発は一旦保留になっています。 </li>
</ul>
<ul>
<li>Q. スタンバイが WAL を同期書き込みまでじゃなくて、受信するまでトランザクションを待たせるっていう同期モードを作ったら、同期レプリケーションのオーバーヘッドって下がるのでは?</li>
<li>A. たぶん下がります。そんなパッチのプロトタイプは作成済で、後は完成させて性能検証してコミュニティに投稿するだけなのですが、時間がありません。。。。 </li>
</ul>
<ul>
<li>Q. 一部のトランザクションだけ同期レプリケーションさせるってできる?</li>
<li>A. synchronous_commit というパラメータを使ってできます。synchronous_commit は、トランザクションごとに設定変更できるパラメータです。synchronous_commit が on に設定されている場合、トランザクションは、マスタとスタンバイに WAL が同期書き込みされるまでトランザクションを完了させません。設定値を local にすると、マスタに WAL が同期書き込みされた時点でトランザクションは完了となり、レプリケーションを待ちません。設定値を off にすると、トランザクションはマスタとスタンバイの両方について WAL の同期書き込みを待ちません。postgresql.conf では、synchronous_commit を local に設定しておいて、同期レプリケーションしたいトランザクションについてだけ SET synchronous_commit TO on と設定した上で実行することで、そのトランザクションだけレプリケーションを待つようにできます。 </li>
</ul>
<ul>
<li>Q. カスケードレプリケーションってできる?</li>
<li>A. v9.1 以前ではできません。来年夏頃リリース予定の v9.2 から使える予定です。</li>
</ul>
<ul>
<li>Q. レプリケーションってどんな経路でデータがマスタからスタンバイに伝わるの?</li>
<li>A. 次のような経路です。 </li>
<ul>
<li>1. バックエンド (SQL を実行するプロセス) が、トランザクションをコミットするときに、そのトランザクションの WAL をマスタのディスクに同期書き込みする </li>
<li>2. バックエンドは、walsender (WAL をマスタからスタンバイに送信するプロセス) に WAL の転送を要求する ・非同期レプリケーションだと、この時点でトランザクションは完了し、コミットの成功応答がクライアントに返る。以降の WAL の転送などは、トランザクションとは独立に実行される ・同期レプリケーションだと、スタンバイから応答があるまでレプリケーション待ちの状態になる</li>
<li>3. 要求を受けて、walsender は WAL をディスクから読み込み、WAL をスタンバイに送信する</li>
<li>4. スタンバイにおいて、マスタから送信された WAL を walreceiver (WAL を受信するプロセス) が受信する</li>
<li>5. walreceiver は、受信した WAL をディスクに同期書き込みする</li>
<li>6. walreceiver は、startup (WAL をリカバリするプロセス) に WAL のリカバリを要求する</li>
<li>7. 要求を受けて、startup は WAL をディスクから読み込み、リカバリする (7 の実行順序は 8 や 9 の後になることもある)</li>
<li>8. walreceiver は、どこまで WAL を書き込んだのか応答をマスタに返す</li>
<li>9. マスタにおいて、スタンバイから返された応答を walsender が受信する ・同期レプリケーションだと、応答を受けて、walsender はレプリケーション待ちのトランザクションを再開させ、完了させる </li>
</ul>
</ul>
<ul>
<li>Q. WAL を送信するたびに walsender が WAL をディスクから読み込んでるけど、I/O 負荷高くならない?</li>
<li>A. あまり高くなりません。基本的に walsender が読み込むのは、バックエンドが書き込んだばかりの WAL です。そのような WAL はデータがファイルキャッシュに残っているため、読み込んでも I/O 負荷はそれほど高くなりません。ただし、古いデータのスタンバイをマスタに組み込むときなど、ファイルキャッシュから追い出されているような古い WAL を転送する必要がある状況では I/O 負荷は増えてしまいます。 </li>
</ul>
<ul>
<li>Q. 同期レプリケーションで、スタンバイが故障したらどうなる?</li>
<li>A. マスタで実行中のトランザクションは停止します。同期レプリケーションは、マスタとスタンバイの両方に WAL を書き込むまでトランザクションを完了させないことを確実に保証しています。この保証を遵守するために、スタンバイが故障して WAL を書き込めない状況でも、スタンバイに WAL を書き込めるまでトランザクションを待たせます。つまり、新しいスタンバイが現れて、WAL を書き込めるようになるまでトランザクションは待たされます。 </li>
</ul>
<ul>
<li>Q. それだと、ノード2台で可用性構成を組んでるとき、ノード1台が故障しただけで処理が止まって、可用性下がるんじゃ?</li>
<li>A. はい、そのような状況だと可用性は下がります。ノード1台でも処理を続行したいのであれば、一時的にレプリケーションを非同期モードに設定する必要があり ます。pgpool-II や Pacemaker のクラスタ製品と組み合わせて使っているのであれば、スタンバイ切り離し時やフェイルオーバ時の動作として、同期モードを変更するように作り込んでおけ ば、故障発生時でもノード1台でスムーズにトランザクションを続行できそうです。 </li>
</ul>
<ul>
<li>Q. なんで↑みたいな仕様にしたの?</li>
<li>A. 実はこの仕様については v9.1 の開発で散々どうすべきかコミュニティ内で揉めました。結局、最も信頼性の高い仕様でまずは同期レプリケーションをリリースして、より柔軟なものは後で 徐々に入れていこうという話になり、↑の仕様となりました。ノード1台で処理を続行させる仕様だと、その1台が故障したときに完全にコミットされたデータ が失われてしまうリスクがあります。このデータ損失のリスクと、可用性が下がるリスクを天秤にかけたとき、データ損失の方がリスクが高いということにな り、データ損失を回避する信頼性の高い仕様がまずは選ばれたということになります。柔軟な動作モードについては、v9.2 以降に期待してください。 </li>
</ul>
<ul>
<li>Q. スタンバイで SQL 実行したとき、リカバリと競合することがあるって聞いたけど? </li>
<li>A. はい、スタンバイでは SQL 実行とリカバリが競合することがあります。競合が発生すると、SQL が終わるまでリカバリが止まってしまったり、競合した SQL が途中でキャンセルされたりします。競合の詳細は、<a href="http://www.postgresql.jp/document/current/html/hot-standby.html#HOT-STANDBY-CONFLICT">マニュアル</a>を参照してください。 </li>
</ul>
<ul>
<li>Q. 競合を回避するには?</li>
<li>A. 競合しないように PostgreSQL のパラメータを設定します。v9.0 では設定が非常に難しいのですが、v9.1 では簡単で hot_standby_feedback というパラメータをスタンバイ側で有効にするだけです。ただし、この設定では1つ注意があります。スタンバイでロングトランザクション (開始されたまま長時間完了しないままとなっているトランザクション、SQL) が発生すると、その間、マスタで VACUUM が実行されても不要領域が回収されなくなります。スタンバイでロングトランザクションが発生しないように注意しましょう。 </li>
</ul>
<ul>
<li>Q. スタンバイがどこまで WAL を受信した (ディスクに同期書き込みした) か知りたい </li>
<li>A. スタンバイで pg_last_xlog_receive_location() 関数を呼んでください。v9.1 だと、マスタの pg_stat_replication ビューの flush_location 列からも分かります。 </li>
</ul>
<ul>
<li>Q. スタンバイがどこまで WAL をリカバリしたか知りたい</li>
<li>A. スタンバイで pg_last_xlog_replay_location() 関数を呼んでください。v9.1 だと、マスタの pg_stat_replication ビューの replay_location 列からも分かります。ただし、replay_location だけは情報が古い (PostgreSQL のパラメータ wal_receiver_status_interval の設定時間だけ古くなる) ため注意してください。あと、v9.1 だと、pg_last_xact_replay_timestamp() という関数を呼んで、どの時刻のトランザクションまでリカバリされたか知ることができます。 </li>
</ul>
<ul>
<li>Q. ↑の関数やビューで表示される文字列の意味が分からない</li>
<li>A. その文字列は、WAL のバイト位置を "8桁16進数/8桁16進数" という形式で表した LSN というものです。LSN だとよく分からないという人は、マスタで、LSN を引数に pg_xlogfile_name() という関数を呼んで、LSN を WAL ファイル名に変換しましょう。 LSN 同士の引き算については、<a href="http://postgresql.g.hatena.ne.jp/pgsql/20100613">ここ</a>の記事が参考になります。 </li>
</ul>
<ul>
<li>Q. スタンバイでリカバリを一時停止できる?</li>
<li>A. できます。pg_xlog_replay_pause() を呼んででリカバリを一時停止したり、pg_xlog_replay_resume() を呼んでリカバリを再開したりできます。 </li>
</ul>
<ul>
<li>Q. フェイルオーバ後、故障したノードをどうやってレプリケーションに復帰させるの? </li>
<li>A. 新しいマスタからオンライン物理バックアップを取得・展開して、スタンバイとしての設定をした上でノードを起動します。そうすると、自動的にスタンバイはマスタに接続し、レプリケーションが再開されます。 </li>
</ul>
<ul>
<li>Q. 復帰させるのにバックアップの取得って必須なの? 面倒。。</li>
<li>A. 必須です。バックアップの取得は確かに v9.0 以前では面倒ですが、v9.1 だと pg_basebackup というコマンド一発で簡単に取得できます。 </li>
</ul>
<ul>
<li>Q. けど、データベースサイズが大きいからバックアップしたくない</li>
<li>A. rsync で差分バックアップしましょう。 </li>
</ul>
<ul>
<li>Q. レプリケーション中にスタンバイが落ちたとき、そのスタンバイをどう復帰させる? </li>
<li>A. 基本的には、その落ちたスタンバイを再起動するだけです。自動的に前回受信したところからレプリケーションが再開されます。ただし、スタンバイとマスタの 差が非常に大きい場合、スタンバイが必要とする WAL ファイルがマスタから既に消えている可能性があります。この場合は、マスタからオンライン物理バックアップを取得・展開して、スタンバイを再構成しなけれ ばなりません。</li>
</ul>
今日のところは、この辺で Q&A 打ち止めにします。また、Q&A の要望があれば、書き足していきたいと思います。。。<br />
<br />
明日の <a href="http://atnd.org/events/21994">PostgreSQL Advent Calendar</a> 担当は、高津英輔さんです。よろしく~Fujii Masaohttp://www.blogger.com/profile/16606001129433709869noreply@blogger.com0