2017年12月24日

pg_prewarmについて

無駄に長い前置き
 これは、PostgreSQL Advent Calendar 2017の16日目の記事です。
 昨日はnuko_yokohamaさんの記事でした。

 PostgreSQL10では9.6で実装されたパラレルクエリが強化されており、集計・分析業務への適合性が高まっております。

 また、ある程度増減するとはいえ、CPUやメモリの価格も時の流れとともにどんどん安くなっています。
 数百GBくらいのデータであれば、データを全てメモリに乗せてしまって、マルチコアでパラレルクエリをぶん回してしまえば、大抵の場合はどうにかなってしまうのではないか……と、昔から考えております。
 当然、数十TBやPB級は難しいでしょうし、PostgreSQLはインメモリDBでもなければ列指向型DBでもありませんので、高速に集計をすることを前提に作られたDBMSと比較すると効率は良くないでしょう。

 ただ、記事執筆時点で、例えばDELL社のサイトでサーバを見積もると、CPU物理18コア2ソケットで物理36コア、メモリ32GBを8枚刺しの256GBくらいのマシンであれば、150万円〜200万円程度で購入できてしまいます。
 この値段が高いかはケースバイケースではありますが、過去に構築した集計用システムを高速化したいだけであれば、ハードの一新とPostgreSQLのバージョンアップをするだけで十分に高速化する可能性があるわけで、工数を考慮すると、一概に高いとは言えないのではないかと思われます。

 ですが、ここまでのお話は、あくまでもデータがメモリに乗っていることが前提であって、そうでなければストレージがボトルネックになってしまいますので、いくらCPUがマルチコアであってもあまり意味がありません。(超高速SSDも台頭してきておりますが)

 ……と、前置きが長くなりましたが、データをメモリに乗せるために、pg_prewarmを使いましょう、というのがこの記事の主題です。
 (思いがつくままに書き連ねたため、大分内容が発散しておりますが、予めご了承ください……)


pg_prerwarmの概要
 pg_prewarmはVer9.4から追加で提供されるようになったモジュールで、ざっくり言ってしまえばPostgreSQLのデータをメモリに乗せるためのモジュールです。公式ドキュメントでも、「プレウォームはキャッシュが主に空のとき、一般的には起動時にもっとも有用です。」と記載されています。

 CREATE EXTENSIONで一発で導入できますし、SELECT pg_prewarm('リレーション名')と実行するだけでメモリにデータが乗ります。
 この時、メモリへのデータの乗せ方を第2引数で「buffer」「prefetch」「read」の3種類から選ぶことができます。


キャッシュされる場所の違い
 「buffer」が指定された場合はPostgreSQLのshared_buffersにキャッシュされ、「prefetch」「read」の場合はOSのバッファキャッシュにデータがロードされます。

 shared_buffersの中身はpg_buffercacheモジュールで確認可能ですし、OSのバッファキャッシュも、Linuxの場合はlinux-ftools等でファイル単位に細かく確認可能です。
 (PostgreSQLのリレーションは$PGDATA/base配下にファイルで管理されますので、oid2name等でリレーションのFilenodeを確認できれば、どのリレーションがどのくらいOSにバッファキャッシュされているかも確認できます)

 データがキャッシュされていると一言にいっても、ざっくり「shared_buffersにだけキャッシュされている」「OSバッファだけにキャッシュされている」「shared_buffersとOSバッファに重複してキャッシュされている」の3パターンが考えられることになります。
 (最後のパターンは明らかに無駄が多いので、検討からは割愛します)


shared_buffersとOSのどちらにキャッシュさせるべきなのか?
 pg_prewarmのオプションで指定可能なことからも察せられるように、用途によって異なるというのが一般解ではあるかと思われます。

 手元の環境で、CPUがボトルネックになるような参照系クエリの処理時間を比較した限りでは、shared_buffersだろうがOSだろうが、どちらのキャッシュに乗っていても明確な処理時間の差は確認できませんでした。
 (OSキャッシュの場合は、shared_buffersにデータをコピーしてから処理をすることになる筈ですので、その分の影響が出るかとも思いましたが、特に遅延は確認されませんでした)

 今回の前置きに書いたように、主に参照系の分析用途にメモリを数百GBも積んで、全データをメモリに乗せてしまおう……という用途であれば、深く検証はできておりませんが、記事執筆時点ではOSのバッファキャッシュの方が「無難」ではないかと考えております。

 shared_buffersが極端に大きい状態ですと、チェックポイント処理が走った場合の負荷量に不安がありますし、ホストマシンのメモリ枯渇時のOOM Killerも考慮する必要が出てきます。ただ、チェックポイント処理もチューニングできますし、PostgreSQL関連プロセスをOOM Killerの対象外にすることも可能ですので、それらの対処コストを上回るメリットがあるのならば、shared_buffersに乗せてしまうのもありかもしれません。

 ただし、「buffer」を指定してprewarmした場合、既にshared_buffersに格納されている分のデータは再読み込みされないようですが、そうでない分のデータは、shared_buffersに乗ると同時に、OSのキャッシュにも同時に乗る模様です。
 「データはなるべくメモリに乗せたい。しかし同一サーバ上でPostgreSQL以外も動作していて、OSのキャッシュにも色々と乗せておきたい」……というようなケースでは、PostgreSQLのデータを重複してメモリに乗せるほど余裕はないのではないかと思われます。


「prefetch」「read」はどちらを使うべきなのか?
 公式ドキュメントにも記載がありますが、「prefetch」が動作するならば「prefetch」の方が処理速度が速いです。
 CentOS7上でltraceを使って確認した限りでは、「prefetch」だと内部的にposix_fadvise関数が、「read」はそのままread関数が呼び出されているようです。

 ストレージから初回読み込みをする際はストレージがボトルネックになりますので、どちらを選んでも速度的に大差はないかと思われますが、cronか何かでメモリに乗せる処理を定期的に実行するような場合は、「prefetch」の恩恵が大きくなるかと思われます。


pg_prewarm_all自作関数
 ここまでツラツラと書いてみて、大して有益な情報を書けていない感が強かったため、自身が接続しているDBのpublicスキーマ全体を問答無用でメモリに乗せようとする関数を書いてみました。(当然、pg_prewarmが使えることが前提です)
 pg_classからリレーションを取得してLOOPでpg_prewarmを回しているだけですので、少しクエリをいじれば条件は変更可能です。
---
CREATE OR REPLACE FUNCTION pg_prewarm_all()
RETURNS text AS $BODY$
DECLARE
sql text;
relnames text;

BEGIN

FOR relnames IN
  SELECT relname::text FROM pg_class
  WHERE
    relnamespace = (
      SELECT oid FROM pg_catalog.pg_namespace
      WHERE
        nspname = 'public'
    )
  AND 
    relkind IN('r','i')

LOOP
  sql := 'SELECT pg_prewarm(''' || relnames || ''',''buffer'')';
  EXECUTE sql;
END LOOP;

RETURN 'OK';

END $BODY$
LANGUAGE plpgsql;
---

駄文でしたが、最後までお読み頂きありがとうございました。

最終日は、soudai1025さんです。

posted by hahasoha at 00:00| Comment(0) | PostgreSQL

2017年04月25日

さくらのレンタルサーバライトでContent-Encoding: gzipの無効化について

 このブログがさくらのブログであることからも明らかですが、さくらのレンタルサーバライトを利用して、いくつかのWebサービスを運用しております。
 何年も前に青空文庫形態素解析データ集というサイトを作っておりまして、このサイトもさくらのレンタルサーバ上にコンテンツを配置しております。

 その名の通り、青空文庫で公開されているデータを形態素解析した結果をzipやらgzipでダウンロードできるサイトなのですが、先日gzip圧縮されているファイルをダウンロードしたところ、拡張子はgzなのに、実際は解凍済みのファイルがダウンロードされるという事象が発生しました。

 一括ダウンロードのページでは、わざわざ解凍前と解凍後のサイズを明記しているというのに…

 オペミスか何かで解凍済みのファイルを拡張子をgzとして配置してしまったのかとも思いましたが、サーバに格納されているファイルを確認すると、ちゃんとgzip圧縮されたファイルが配置されていました。

 どうも、Content-Encodingのgzipが有効になっていて、圧縮転送がされているとブラウザ(Chrome)が判断して、自動的に解凍してしまっている模様です。

 はて、Content-Encodingのgzipとは、意図的に設定しないでも勝手につくものだったのかしら…ということも気になりますが、とりあえずはブラウザで解凍されないようにすることが先決ですので、方法を調べてみました。

 で、思ったより苦戦しましたが、とりあえず、.htaccessに以下を追記することで、Chromeでは解凍されずにダウンロードできるようになりました。



<FilesMatch "\.gz$">
  AddEncoding identity .gz
</FilesMatch>


 さて、以下は上記に至るまでの経過を示していきます。内容に不備があるようであれば、有識者の方は突っ込みをお願いいたします。

 そもそも、ブラウザがgzファイルを解凍してしまうのは、HTTPレスポンスヘッダのContent-Encodingにgzipが含まれているからではないかと推測し、Wiresharkでパケットを確認した結果、確かに「Content-Encoding: gzip」が含まれていました。

 ちなみに、他のサイトでgzファイルをダウンロードしても自動的に解凍されませんでした(レスポンスにContent-Encodingそのものがなかった)ので、やはりサーバの設定の問題のようです。


 で、ブラウザに解凍をさせたくない場合は、Content-Encodingヘッダそのものをつけないのが正しいようですので、初めはmod_headersを用いて以下のようにしてみましたが、どうにもContent-Encodingヘッダが削除されませんでした。

<FilesMatch "\.gz$">
  Header unset Content-Encoding
</FilesMatch>

 ETagヘッダで試してみると問題なく削除できましたので、機能が動作していないということはない筈なのですが…
 あまり自信のない推測ですが、Content-Encodingヘッダはレスポンスヘッダの末尾についておりますので、除去された後にもう一度付与されている…のでしょうか。(私の単なる勘違いだったら申し訳ないですが)

 次は、Content-Encodingを削除することは一旦あきらめて、内容を変更する方法で検討しました。
 AddEncodingで変更できることはわかったのですが、何の文字列に変更すべきかがわからない

 RFC2616を見る限りでは…以下のような記載がありますので、やはり、identityが一番近いように読めます。

identity The default (identity) encoding; the use of no transformation whatsoever. This content-coding is used only in the Accept- Encoding header, and SHOULD NOT be used in the Content-Encoding header.


(長時間かけてググったわけではないですが、同じ事象で困っている人は見つけられませんでしたので、何か私が根本的な勘違いをしている気もしておりますが…)
posted by hahasoha at 23:04| Comment(0) | TrackBack(0) | その他

2017年03月12日

BVH WEB VIEWERを公開しました

BVH WEB VIEWERを公開しました。
http://motion.hahasoha.net/


 ひょんなことからVRに触れることになり、関連しそうな情報を色々と調べていたところ、カーネギーメロン大学が公開しているモーションデータをbvh形式に変換して公開しているサイトを見つけました。

 必要なツールをダウンロードしてbvhファイルを読み込めば、色々なモーションが見れてなかなか楽しいです。
 …が、もっと気軽に検索できて、パパッとモーションを閲覧できる方法がないか調べた結果、どうも適合するサイトはなさそうでしたので、冒頭のBVH WEB VIEWERを作成し、公開しました。

 作成したといっても、three.jsのサンプルコードも殆ど流用させてもらっていますし、手抜き感は否めませんが…
 パパッとモーションを検索して、ブラウザで動作を確認してそのままbvhファイルをダウンロードできるようにしましたので、使い勝手は悪くはないかと思います。(手元のスマホで表示をしたらiframeが荒ぶったため、とりあえずPC限定としていますが)

 ただ、実際に動きを見ても、何の動きなのかわからないのも多数ありますが…(Alaskan vacationって何なんでしょう…?)

 3Dコンテンツの作成は完全に門外漢なので、どれくらいの需要があるのか把握できておりませんが、bvhファイルはBlenderではそのまま使えるようですし、ドリームファクトリーのLiveAnimationで変換すればMMDでも使える模様です。

 2016年はVR元年と言われていますが、3D系の素材に対する需要・供給も増加していきそうですね。
posted by hahasoha at 16:31| Comment(0) | TrackBack(0) | その他