前作
PostgreSQLを本当に高速化したい人のための10のポイント
が結構受けたようなのと、外部ツールの話が書ききれなかったので次回作を。まあこっちは受けないと思うが、ついでに備忘録をかねて。内容としては「RDBMSだけ使ってもどうしようもないよね」というのは多い。しかし残念ながらL.starは、PostgreSQL本体に比べてこの種のツールの組み合わせについて精通しているとはいいがたい。なので、「PostgreSQLとしては、こんな部分は外部ツールに任せたいよね!」という思いをまとめるように書いてみた。
外部キャッシュ
最近のRDBMSはとても早くて、シンプルなselect * from table where id=? ぐらいだと、10000tps位平然とこなしちゃう。このくらい早いと、web server数台分ぐらいのバックエンドになれるので、それで十分ではないかすら思ってしまうだろう。しかし実際は、超大規模を想定するとまったくお話しにならない。slashdottedするような負荷は、web数台+DB1台では現実問題として到底捌ききれない。
また、スループットの話だけなら上記で良いが、同時にレイテンシの話もしないといけない。実はRDBMSはトランザクション管理がからむため、結構レイテンシが高い。どちらかというと中スループット、高レイテンシ、高耐障害性のソフトウェアになる。
だから、高スループット、低レイテンシ、低耐障害性なキャッシュ用オンメモリDBと組み合わせるのがよい。こいつらが捌けるスループットは一桁違う。そして、単純なkey-valueストアであるためもともとレイテンシも低く、かつ分散もきわめて容易である。最近このような形態をサポートできるフレームワークが増えているようだ。
ここらで一つ、PostgreSQLプロトコルを使い、外部にオンメモリキャッシュを作って、このツールを経由してクエリを発行したら、自動的にキャッシュに問い合わせて返す or 返答をキャッシュしつつ返すと言う動作をするproxyを作ったら受けるだろうか?
コネクションプーリング
1-Mar-2009追記:よく分からないという指摘を受けIRCで追加説明をした。それを元に修正しようとしたら、pg_pconnectその後というところで綺麗に3行でまとめられていた。なので、以下は技術的詳細について色々書いてあるが、結論が欲しい人はそのまま上記に行って欲しい。
PostgreSQLの場合、泣き所はfork()を使ってセッションを受けているところである。これにはいくつかのメリット(ここでは割愛)と2つのデメリットがある。一つはfork()がコストの高いAPIであるため、接続確立のためのコストが大きいこと。もう一つはプロセスというのがコストの高い資源であるため、たくさん生成すると重くなってしまうことにある。
前者については、実は(スレッドを使う)MySQLよりも早いと言うデータを見たことがあるので、必ずしも今となっては当てはまらない部分がある。ただし、後者は切実である。普通、2000プロセスも立ち上げるとLinuxが(もしLinux以外でPostgreSQL2000接続以上しても平気なOSがあったら、是非教えて欲しい。)管理できない。そして実質サービスダウンに等しい状態に追い込まれる。そして、接続をしていると、検索している居ないにかかわらずプロセスがいるのである。
pg_pconnectに気をつけろ!
では、この後者の点を指摘している。pg_pconnectは前者の解決策を提供しているが、後者については考えもしないろくでもない代物である。安直な大量コネクション確保は性能劣化の原因になりかねない、と心しておくべきだろう。PostgreSQLの場合、昔は目安として256接続がこの重さに引っかかる境目だった。最近のPCではまた違う結果かもしれない。
などと下書きをしているうちに
pg_pconnectに気をつけろという話に気をつけるべき理由
という対立エントリが出ている。前半の話は「負荷が下がるとプロセスも減る」という話だが、問題は負荷が高い状況の話であるため何ら議論の本質とは関係がない。
pg_pconnect使うと障害対応で人間やめることになるよなどという意味不明な極論ではなくて、例えば「PHPスクリプトを動かすhttpdと画像やCSSを送り出すhttpdは別にしよう」ということだ。
というのは正論ではある。しかし、その後でPHPスクリプトが動いているhttpdの性能が逼迫したとき「DB接続を伴うphpスクリプトとそれ以外のスクリプトを動かすhttpdを分けよう」言い出すのだろうか。
Javaのコネクションプーリング(あるいは相当品)は比較的しっかりしているので、最大リソースをきっちり指定できるし、接続しないプロセスまでコネクションを要求することはない。そのため、確保リソースを間違えなければおそらく大丈夫だと思われる。しかし、この管理が及ぶのも、Webサーバ1台の時に限られる。複数台ある場合には、動的に最適な接続割り振りをしつつ最大数を超えない、というような芸当は無理だ。
そう言う場合、どこかで中央集権的な管理が可能な別のソフトウェアを使わなければならない。選択肢は2つ、pgpoolとpgbouncerである。ただ、上記エントリでも示されている、last.fmでの結果が、pgbouncerのほうがより大規模向けであることを如実に示している。
Russ ブログ ? Postgres Connection Pools: Pgpool vs. PgBouncer ? Last.fm
個人的にコネクションプーリングだけであれば、pgbouncerがお勧めだろう。ただし、pgpool-IIはもはやコネクションプーリングだけのソフトではないので、他のかねあいもあるだろう。
しかし、正直に白状すると、本当にコネクションプーリングを使うべきかどうか、L.starはもう分からなくなってしまった。Java+マルチプロセッサシステムにおいては、object poolingはわざわざ処理をしたりロックが発生する関係上かえって遅くこともあるそうだ。
[Java] ものをほじしてもはやくならない [Object Pooling しても速くならない]
Javaの理論と実践: ガベージコレクションとパフォーマンス(3/3追記:Thanks jzkeyさま。)
# 本当はdeveloperWorksに良い記事があったのだが、思い出せない。覚えている人はコメントかメールをください。
これは当然コネクションプーリングにも当てはまる。ポイントはコネクション生成のコストvsロック管理コストなどがどっちに軍配が上がるか、なのだ。しかしこの割合は日々変化しており、結局今のPCでコネクションプーリングすべきかどうかというと、とんと自信がない。古いシングルプロセッサ時代の昔は、確実にすべきだと言えたのだが。
3/3追記:ただし、上記dWの記事でも
確かに、データベースとの接続やスレッドの生成が必要なクラスオブジェクトの作成は、メモリ確保時に高い負荷がかかりますし、プール領域にオブジェクトを確保しておけば、データベースとの接続等に限られたリソースを有効に使うことができるため、この手法は有効と言えます。
とあるため、やはり有効なのだろう。言いたいのは、この種のパフォーマンス趨勢はどんどん変わる。そのため特に性能面の話においては、以前正しいと思っていたことが、あっさり覆されることがあるということである。
高可用性の追求
RDBMSは常に高い稼働率を求められる。なにしろデータストアの根幹であるから、落ちるわけにはいかない。しかし、RDBMS自体が99.99%の高可用性に対応しているようなことはまず無い。なにしろマシン単体で実現することが不可能だからだ。そのためのソフトウェアが用意されている。もっとも、ソリューションや運用体制まで含めて高可用性と言うべきであろう。PostgreSQLと組み合わせられるのは以下のようなものである
- Cold standby (バックアップサーバを用意しておき、クラッシュしたらそこに復旧)
- Hot standby(バックアップサーバを常時同期させる形で用意しておき、クラッシュしたら切り替え)
- レプリケーション (pgpool-IIとか)
- 共有ディスク型Active-Standby(例:iSCSI+Heartbeat)
- ネットワークRAID+Active-Standby (例:DRBD+Heartbeat)
- ノンストップ型ハードウェア
このとき注意するのは、どの程度の可用性を要求するか、とどの程度の手作業を許容するか、ということである。手作業は当然復旧時間と待機の人員コストを増加させる。また可用性追求のためのソフトウェア及びハードウェアはたいてい目が飛び出るほど高価である。そして、検証が完了した組み合わせでないとトラブル頻発しまくる、ということになる。
そして、高可用性構成はとにかくノウハウがいる。スキルの高い人材を訓練育成し、テストし、環境に適合させて、その環境がきちんと動くように現場調整して初めて真の高可用性が得られる。安くない。これも根拠のない職人感覚だが、L.starの考える高可用性構成のPostgreSQLを導入したい場合、それにかかるコストは以下のようになると考えている。
- 稼働率99% - 数十万円(特別でもないハードウェア)
- 稼働率99.9% - 数百万円(高価なハードウェア冗長構成+データセンター)
- 稼働率99.99% - 数千万円(ノンストップハードウェア+データセンター+それを支える運用体制)
ざっくり言うと「停止時間1/10にするためには、コストは10倍掛かる」という感じである。日本人は「落ちない」を当たり前のようなことと考えているが、実際にはとても金と手間のかかるものである、というのは、プログラマだけではなく、みんなに理解して欲しいものだと思っている。そして、コストと稼働率のかねあいについて、みんなが真剣に考えてくれればなあ、と思うのである。
ちなみに、今PostgreSQL界隈が一番力を入れているのがHot standby機能である。まだまだ実装の手間が大きいが、これがきっちり使えるようになったら、99.9%レベルの稼働率を従来より安価に使えるようになるだろうと期待している。ただし、残念ながら原理上わずかながらのデータロスが避けられない。
レプリケーション
レプリケーションを使うのは高可用性と負荷分散的な両面で、である。高可用性的な面では上で述べたが、正直高可用性「だけ」を追求するのであれば、実績のある共有ディスククラスタなどを使うことを強くお勧めする。ここでは負荷分散部分を話したい。
なお、負荷分散と書いているが、ここでは「検索の負荷分散」である。レプリケーションはデータを何らかの形で複数箇所に投入する仕組みであるため、ほぼ確実に導入前より更新が遅くなる。更新の負荷分散に有効なのは、shared-nothing式クラスタリングと呼ばれる、データを複数ノードに分散して配置する方法であり、この2つは組み合わせられるが相容れないものだ。
話がそれた。レプリケーションはいろいろな仕組みがあるが、以下のような部分に注目して選定すると良い。なお、高可用性のところで述べたのと同じく、これもまた「ソリューション」である。ソフトは無料化もしれないが、ノウハウや人的コストは結構かかるので、覚悟すべし。
- 範囲・・・どれだけの範囲の複製を取るか。通例クエリベースは全体を一気にできるのがメリットであり、トリガベースはテーブルごとで細かく設定できる点がよい。
- 同期or非同期 ・・・ 通常非同期のほうが色々と良い。クエリの制限が少なかったり、性能が高かったりする。ただし、データ同期が遅れるという違いだけは、アプリケーション設計で吸収しないといけない。
- 負荷分散 ・・・ 通常レプリケーション機能には負荷分散の意味はないため、別途組み合わせる必要がある。ただし、pgpool(-II)だけは、同時に負荷分散機能を持っている。アプリケーション側でラウンドロビンを実装したり、lvsを組み合わせたり、pgpoolやpgbouncerを組み合わせる、というのはあり。
- 制限・・・ たいていの場合、一部のクエリが使用できない。クエリベースはたいていシーケンスやnow(),random()のような特殊関数の取り扱いに難があり(絶対出来ないわけではない)、トリガベースではtruncateなど、トリガが動かないのにデータが変わるものがやばい。
生のファイルシステム
意外に思われるだろうか。しかし、RDBMSに数十KB以上の、例えば画像ファイルを格納するというのは賢明ではない。そもそも構造自体小さなレコードを格納検索するのに最適化しているのがRDBMSの歴史である。でかいファイルを入れる方法として、BLOB/CLOBのような手法も提供されているが、どうもどれもいまいちという印象がぬぐえない。
そので外部ツールという話になるのだが、特に大きなファイルをkey-value形式で格納する方法として、未だにたんなるファイルシステム以上のものを見たことがない。そりゃそうだろう。そう言う目的に特化しているのだから。そう言うこともあり、画像ファイル等を格納する場合に別途ファイルサーバを用意しているような設計のサイトはとても多い。
どの程度までのファイルを格納できるか、というのは難しい問題だ。L.starは特に根拠があげられるでもなく64KB程度と考えている。いずれにしても数百KB程度であればもうDBに置くのはつらい。
なお、たんなる格納だけと言うことであれば、あるいはとても遅くてもかまわないというのであれば、それほど問題にはならない。ただし、前述のエントリの通り「検索しないデータをRDBMS上に配置するのは無駄使い」はL.starの持論である。
全文検索
PostgreSQLを高速化する16のポイント
では、全文検索にはLudiaを使おう、と紹介されている。また同様にtsearch2やtextsearch-jaも紹介されていて、PostgreSQLには全文検索機能があると言っても良い。しかしなぜここに「全文検索」を上げるのか?なぜなら「全文検索したいデータは、たいていの場合大きい」からである。前述のファイルシステムで述べたとおり、大きいデータの格納は、RDBMSの専門分野ではない。
あと、これらのIndexは残念ながら未だ枯れているとは言い難い。tsearch2はcontrib歴こそ長いが、昇格は8.2とごく最近である。Ludiaは8.3と利用するとvacuumが出来ないしろものだ。インデックスアルゴリズムの優劣については何とも言えない。が、個人的には例えばtsearch2よりhyperestraierのほうが高速だなと感じる。
L.starのDB歴は結構古く、最初に真面目に使ったのは(PostgreSQLと同じルーツを持つ)Illustraである。もう10年近く前だが、日本語全文検索DataBladeを使いつつ、「うーん、あまり性能が出るものじゃないな。やっぱり外部のエンジン使った方が早いわ」と思ってた。しかしそれから10年、時代の変化はRDBMSの全文検索Indexを身近なものにしている。コネクションプーリングの項では「時代の変化で分からなくなった」と書いたが、全文検索に関しては、変化によってRDBMSに組み込めるようになった代物と言えるだろう。
さて、つらつらと書いているうちに気力が尽きてしまった。自分で思っているよりずいぶんPostgreSQLについて覚えていたな、と言う印象だが、とはいえ最新の、外部ツールまで含めた事情はやはりカバーできていないなぁ、と感じる。この辺もいずれきっちりまとめて記事にしたいと思ってはいるのだが、まだまだ文章を書く練習が必要なようだ・・・