[pgsql-jp: 35216] Re: pgpoolとLargeObject

Tsunehisa Kazawa kazawa @ ca2.so-net.ne.jp
2005年 4月 8日 (金) 22:55:14 JST


こんばんは。お返事遅くなってしまってすみません。加澤です。

皆さん貴重なご意見本当にどうもありがとうございます。その後の顛末をご報告
しますね。

// また長いメールになってしまいました。

水野さん wrote:
> どうしてもpgpoolでないとダメでしょうか。
> 単なる冗長化という目的であれば、うちではdb_mirrorを使っています。
> #mirrorのタイムラグ分、双方の不一致が発生しますが。

今回想定しているシステムの要件に「計画停止は許容出来るが非計画停止は原則
ダメ」「データロストは原則ダメ」というものが含まれておりまして (まぁどち
らもそれほど厳密なものではないのですが)、そのために dbmirror のような非
同期レプリケーションは選考から落としていました。

また人の手を介さない failover を行おうとすると、たぶん結局 pgpool の
master/slave モードなどを併用する必要があるのだと理解しているのですが、
それならば pgpool だけで完結させられればシンプルで良いと考えた、というこ
ともありました。


谷田さん wrote:
> そうです。代用としてはbytea型を使う方法があります。

bytea 型を用いる代替案、いくつか試してみました。このメールの最後で書きま
すね。

> 私の知る限り、ラージオブジェクトを正しくレプリケーションできるソリューショ
> ンはないです。なぜなら
> 
> - 問い合わせベースだと、OIDの同期が取れない
> - トリガベースだと、システムテーブルにトリガーが張れない

とても参考になります。いつもどうもありがとうございます。


石井さん wrote:
> これは,マスタとセカンダリで違うoidがアサインされてしまっているからな
> んですね.

はい。


> おっしゃる通りで,同時セッションが1ならば問題なく動作しますが,複数セッ
> ションを動かすときはシーケンスと同様,何らかの方法でマスタとセカンダリ
> でoidの生成タイミングの同期を取らないと上のようなエラーになります.
> 
> 回避策としては,ラージオブジェクトの生成を行う前に,pg_databaseのよう
> なshared catalogにテーブルロックをかけて同期を保障する方法があります.
> 詳細はインプレスさんの「丸ごとPostgreSQL」に書いたので,そちらをご覧下
> さい.

インプレスさんの「丸ごと!PostgreSQL」、たまたま知人が持っていたのを発見
しまして、早速読ませていただきました。LargeObject の生成時に「LOCK TABLE
pg_database」して oid の生成を master/secondary で同期させるのが肝なので
すね。

早速上記コードをアプリケーションに組み込みテストしてみたところ、最終的に
うまく動作させることが出来ました!本当にどうもありがとうございます。

ただ、うまく動作するようになるまでにいくつか落とし穴がありまして、

・OID を更新する処理は LargeObject の生成以外にもたくさんあるため、それ
らが起こらないようにシステムを設計する必要がある。
→当初、明示的に WITHOUT OIDS しないでテーブルを作成していたためにアプリ
ケーションで用いる全てのテーブルに暗黙の OID 列が存在し、そのために
LargeObject の生成だけでなく単なるレコードの INSERT でも OID の更新が発
生、同期が崩れてしまった。
 →アプリケーションで用いる全てのテーブルを「WITHOUT OIDS」付きとした。
 →system table にも同じように定期的に OID を更新するようなものがないか
どうか調べてみましたが、DDL などを流さない限りとりあえず大丈夫そうでした
(ほんと?)。少なくとも通常の SELECT や DML、VACUUM などでは問題ないよう
です。stat 系もそれなりに取っていますが、とりあえず大丈夫そう。でも
ちょっと不安…。

・OID は「DB」に対してではなく「DB Cluster」に対して存在するものであるの
で、利用しようとする DB Cluster 上に別のアプリケーションが利用する DB が
あったり、OID を更新するような処理のものが存在すると同期が崩れてしまう。
→ただし、LOCK する pg_database テーブルは DB Cluster で一つだけ存在する
テーブルのようなので、たとえ利用しようとしている DB Cluster に複数の DB
が存在したとしても、全て同じアプリケーションから利用するのであれば (つま
り LargeObject 生成時に必ず pg_database テーブルを LOCK することが保証さ
れているのであれば) 問題なく動作しそう。

などが検証中に問題となりました (他にも何かあるかな?)。

***

LargeObject ではなく、bytea 型を用いてデータを格納する方法を2種類ほど試
してみました。

その1.単一のレコードに全データを入れてしまう実装
その2.pg_largeobject テーブルのように、データを細切れのチャンクに分割
して格納する実装

まずその1.ですが、JDBC 経由のアプリケーションの場合、ある程度以上大き
なデータ (数 Mbytes 程度) で極端に処理速度が遅くなってしまったり、JavaVM
の Heap 消費量が激増してしまったりしたために早々に断念しました (当初はこ
の案でいくつもりだった)。

その2.については、そこそこうまく、特に読み出しと削除についてはほとんど
LargeObject と遜色ない程度にちゃんと動いたのですが、なぜか書き込み
(INSERT の大量発行) が LargeObject を用いた場合と比べて数 Mbytes のデー
タで5倍ほど遅く、実用になるかどうかぎりぎりのところだと思いました。チャ
ンクサイズをいろいろ変えて (LargeObject と同じ 2K から 64K 程度まで) 試
してみましたが、それほど劇的には改善されませんでした。
(逆に一つのチャンクに収まるような小さいデータの場合は LargeObject を用い
るよりも高速に処理できました。)

その2.は書き込みが5倍遅い、というデメリットがある反面、非常に generic
なコードで実現出来る (PostgreSQL への依存度が少なく、トリッキーなダウン
キャストや system table の LOCK などを用いる必要がない。権限管理も容易)
という大きなメリットがあるため、LargeObject を用いた方法が最終的にうまく
ない場合に利用可能な「保険」的に今のところは考えています。

ところで、JDBC ドライバのコードもざっと眺めてみても、LargeObject に対し
て bytea を用いるその2.案が5倍遅い理由がよく分かりませんでした。この
理由について心当たりのある方いらっしゃいますか?(マニアックな話で恐縮で
す…。)

それではまた。

-- 
  ◇   加澤恒央 Tsunehisa KAZAWA
◇  ◇ mailto:kazawa @ ca2.so-net.ne.jphttp://www.digitune.org/



pgsql-jp メーリングリストの案内