[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.jp
◇ http://www.digitune.org/
pgsql-jp メーリングリストの案内