pg_nosql_benchmarkの改善案

かなり今更感のある話ではあるが・・・
EnterpriseDB社がGithubで公開しているpg_nosql_benchmark、これまでも手元の環境で何回か測定して、その結果を第七回 中国地方DB勉強会 in 松江で発表とかしてたんだが、このベンチマーク、実はPostgreSQL的にちょっとアレな感じのインデックス設定とクエリになってるんだよな。

何が問題?

簡単に言えば
GINインデックスを設定しているけど、
クエリはそのインデックスを使用していない

という、アレゲな感じになっている。

このベンチマークではSELECTの前に、

CREATE INDEX ${F_TABLE}_idx ON ${F_TABLE} USING gin(data);

みたいにginインデックスを設定しているんだけど、検索時には

SELECT data FROM json_tables WHERE (data->>'brand') = 'ACME';

のように、 ->>'キー名' = 値 のような条件を与えている。
これだとSeqScanになる。

正直言って、このレギュレーション(?)で、よくMongoDBのfind()よりも速い結果が出たんだなと。
(なお、MongoDBに設定したensureIndex()が有効に使われているのは、別途、 lib/mongo_func_lib.sh を書き換え、find().explain()の出力結果で確認した)

少なくともMongoDBがインデックスを(たぶん)使っていて、PostgreSQL側が使っていないのはどうなのよ、というのもあったので、勝手に改善案を考えてみた。

改善案1(btree1式インデックス設定)

最初に考えつくのは発行されるクエリパターンに合わせて、インデックスの作成方式を変えるというもの。
元々あったginインデックスは無用の長物なので、ばっさり削除。
pg_nosql_benchmarkでは、"name", "brand", "type"の3つのキーから取り出した値を、文字列と比較している。
なので、(カラム名->>'キー') の式インデックスを3種類設定すれば良い。

pg_nosql_benchmark内だと lib/pg_func_lib.sh の pg_create_index_collection () という関数を変更。

#   typeset -r F_SQL="CREATE INDEX ${F_TABLE}_idx ON ${F_TABLE} USING gin(data);"
   typeset -r F_SQL1="CREATE INDEX ${F_TABLE}_idx_name ON ${F_TABLE} USING btree((data->>'name'));"
   typeset -r F_SQL2="CREATE INDEX ${F_TABLE}_idx_brand ON ${F_TABLE} USING btree((data->>'brand'));"
   typeset -r F_SQL3="CREATE INDEX ${F_TABLE}_idx_type ON ${F_TABLE} USING btree((data->>'type'));"

   process_log "creating index on postgreSQL collections."
#   run_sql "${F_PGHOST}" "${F_PGPORT}" "${F_DBNAME}" "${F_PGUSER}" \
#           "${F_PGPASSWORD}" "${F_SQL}" \
#            >/dev/null
   run_sql "${F_PGHOST}" "${F_PGPORT}" "${F_DBNAME}" "${F_PGUSER}" \
           "${F_PGPASSWORD}" "${F_SQL1}" \
            >/dev/null
   run_sql "${F_PGHOST}" "${F_PGPORT}" "${F_DBNAME}" "${F_PGUSER}" \
           "${F_PGPASSWORD}" "${F_SQL2}" \
            >/dev/null
   run_sql "${F_PGHOST}" "${F_PGPORT}" "${F_DBNAME}" "${F_PGUSER}" \
           "${F_PGPASSWORD}" "${F_SQL3}" \
            >/dev/null

改善案2(ginインデックス用にクエリ変更)

次の方式は、ginインデックスに合わせて発行するクエリを変更するというもの。
元々のクエリが

SELECT data FROM json_tables WHERE  (data->>'brand') = 'ACME';

のような感じになっているので、これを

SELECT data FROM json_tables WHERE  data @> '{"brand":"ACME"}';

のような感じに変更する。

pg_nosql_benchmark内だと lib/pg_func_lib.sh の pg_select_benchmark () という関数を変更。

   typeset -r F_SELECT1="SELECT data
                         FROM ${F_COLLECTION}
                           WHERE  data @> '{\"brand\":\"ACME\"}';"
   typeset -r F_SELECT2="SELECT data
                         FROM ${F_COLLECTION}
                           WHERE  data @> '{\"name\":\"Phone Service Basic Plan\"}';"
   typeset -r F_SELECT3="SELECT data
                         FROM ${F_COLLECTION}
                          WHERE  data @> '{\"name\":\"AC3 Case Red\"}';"
   typeset -r F_SELECT4="SELECT data
                          FROM ${F_COLLECTION}
                            WHERE  data @> '{\"type\":\"service"}';"

修正版を動かしてみた。

元の pg_nosql_benchmark, 修正案1, 修正案2をそれぞれ動かしてみた。
予想では、修正案1でわずかにCOPY/INSERTは改善、SELECTは修正案1/修正案2とも大きく改善するはず。

バルクロード


  • btree式インデックス使った場合にも、それほど大きな性能差はないかな。
INSERT


  • btree式インデックス使った場合にも(以下略)
SELECT


  • オリジナル版と比較すると、改善案1,改善案2とも大きく検索性能が向上している。
  • ホントはこれとMongoDBとを比較すべき。
SELECT(改善版どうしの比較)


  • まあ、微妙な差ではあるけど、btree式インデックスによる検索のほうがちょい速いか。
  • 昨日試したpgbenchモデルだと、btree式インデックスを使ったパターンのほうが、ginよりもかなり速い印象だった。
  • このモデルだとあまり大きな差異はない。
    • 文書の複雑さが何か関係している?

で、どうするかね・・・

Githubで公開しているんだから、修正方式(EDB社の人が知らないわけないと思うのだが・・・)を添えてrequestを送るべきなのだろうか・・・。