Hatena::ブログ(Diary)

日々の記録 別館

2015-02-17

PostgreSQL JSONB/MongoDB 更新性能

先日のno titleの発表で、自分のノートPC上で測定したpg_nosql_benchmarkの結果を発表したのですが、そのときにはPostgreSQL JSONB型がMongoDBと比較して、
格納、検索、いずれもマッハ!!
みたいな感じの結果を出したわけですが、pg_nosql_benchmark自体には更新を測定するクエリがなかったんですよね。
ということで、そのときには更新性能は提示しなかったのですが、正直、更新に関してはPostgreSQLの方が分が悪そうだよなーとは思ってました。
じゃあ、どのくらい悪いのか一応確認してみようかと。

環境

今回はノートPC上のVMで測定。


マシンLet's note CF-SX4 SSDモデル
実メモリ8GB
VM動作環境VMWare player 6.6
ゲストOS CentOS 7.0
ゲストOSメモリ4GB

PostgreSQLMongoDBもほぼノーチューニング。というか、この環境だと、たぶんチューニングをしてもあんまり変わらない気がする。

モデル

今回の測定モデルでは、YCSB(Yahoo! Cloud Servicr Benchmark)を参考にして構築した。
YCSBでは以下の様な感じの1つのキーと10個のほどほどに長いテキストフィールドを持っている。

{"ycsb_key":"user00000000000000000001","field1":"f03560cbc9e18b3d62106646f5ac3533f03560cbc9e18b3d62106646f5ac3533f03560cbc9e18b3d62106646f5ac3533f03560cbc9e18b3d62106646f5ac3533f03560cbc9e18b3d62106646f5ac3533","field2":"f63c7ac594907fd5ae161a646148464af63c7ac594907fd5ae161a646148464af63c7ac594907fd5ae161a646148464af63c7ac594907fd5ae161a646148464af63c7ac594907fd5ae161a646148464a","field3":"6a3957fe03711c6ee46c47a3017a120f6a3957fe03711c6ee46c47a3017a120f6a3957fe03711c6ee46c47a3017a120f6a3957fe03711c6ee46c47a3017a120f6a3957fe03711c6ee46c47a3017a120f","field4":"1a2fe84a6f3b0d3b3db704261b453f911a2fe84a6f3b0d3b3db704261b453f911a2fe84a6f3b0d3b3db704261b453f911a2fe84a6f3b0d3b3db704261b453f911a2fe84a6f3b0d3b3db704261b453f91","field5":"6545bbec3c99e54034b20b142519915a6545bbec3c99e54034b20b142519915a6545bbec3c99e54034b20b142519915a6545bbec3c99e54034b20b142519915a6545bbec3c99e54034b20b142519915a","field6":"58209cb84597bea4c575eaee3480fb3e58209cb84597bea4c575eaee3480fb3e58209cb84597bea4c575eaee3480fb3e58209cb84597bea4c575eaee3480fb3e58209cb84597bea4c575eaee3480fb3e","field7":"60284bccd9ba15f12f6993c6ad8433cc60284bccd9ba15f12f6993c6ad8433cc60284bccd9ba15f12f6993c6ad8433cc60284bccd9ba15f12f6993c6ad8433cc60284bccd9ba15f12f6993c6ad8433cc","field8":"941ac5f05860137fe6c96dcdbe1d1e73941ac5f05860137fe6c96dcdbe1d1e73941ac5f05860137fe6c96dcdbe1d1e73941ac5f05860137fe6c96dcdbe1d1e73941ac5f05860137fe6c96dcdbe1d1e73","field9":"70b9c9871fb2043a4ff979adbb2dfd7770b9c9871fb2043a4ff979adbb2dfd7770b9c9871fb2043a4ff979adbb2dfd7770b9c9871fb2043a4ff979adbb2dfd7770b9c9871fb2043a4ff979adbb2dfd77","field10":"bff6f1e3a1ed6af047c5c17920e70b16bff6f1e3a1ed6af047c5c17920e70b16bff6f1e3a1ed6af047c5c17920e70b16bff6f1e3a1ed6af047c5c17920e70b16bff6f1e3a1ed6af047c5c17920e70b16"}

  • キー名はycsb_key。キー値は24文字のユニークな文字列。
  • field1〜field10という文字列フィールドを持つ。それぞれのフィールドは百数十文字のテキスト値。
  • YCSBベンチマークの更新処理では、ycsb_keyを条件としてヒットした1件の文書のfield1の値を更新する。

さて、今回はMongoDBの強み?の一つである部分更新の機能を使えるような更新をしてみる。更新時にfield11という新規のキーと32文字の値をセットするというものだ。

PostgreSQL

  • カラムとしてはjsonbカラムを1つだけ定義する。
  • 10万件をCOPYでロードする。
  • COPY後にbtree式インデックスを設定する。

CREATE TABLE usertable(data jsonb);
COPY usertable(data) FROM '/tmp/json.txt';
CREATE INDEX ycsb_key_idx ON usertable USING btree ((data->>'ycsb_key'));

  • この状態で以下のようなUPDATE文を発行する。

UPDATE usertable
  SET data =
  json_build_object(
    'ycsb_key', data->>'ycsb_key',
    'field1',  data->>'field1',
    'field2',  data->>'field2',
    'field3',  data->>'field3',
    'field4',  data->>'field4',
    'field5',  data->>'field5',
    'field6',  data->>'field6',
    'field7',  data->>'field7',
    'field8',  data->>'field8',
    'field9',  data->>'field9',
    'field10', data->>'field10',
    'field11',  'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
  )::jsonb
  ;

  • もうね、うんざりしますよね。
    • PostgreSQLのJSONBに部分更新的なインタフェースがないので、field11を追加した文書をjson_build_object()を使って再生成する必要がある。
    • で、その過程で各キーの値で"->>"演算子を使って値を取り出してセットしないといけない。
    • しかも、この関数JSON型を返却するので、JSONBに型変換が必要。(´・ω・`)
      • なお、PostgreSQL 9.5-develではjsonb_build_object()が追加されるので、型変換が不要になる。どうして9.4に入らなかった。

MongoDB

db.usertable.ensureIndex({ycsb_key : 1})

  • 更新は以下の様に行う。
    • 記述もPostgreSQLと比較すると非常にシンプルですねー。

db.usertable.update({},{$set : {"field11":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}},false,true)

測定

測定結果


DBMS/Type/index 100K rows update(sec)
PostgreSQL/JSONB/btree 8.321
PostgreSQL/JSONB/gin" 20.415
PostgreSQL/JSON/btree" 16.457
MongoDB 1.765

f:id:nuko_yokohama:20150217215655j:image

  • むう・・・予想通りとはいえ、更新処理に関してはMongoDBのほうが圧倒的に高速だな・・・
  • あと、これも予想通りではあるけど、ginインデックスの更新が結構遅いなあ。
  • JSON型がJSONB型より遅いのは、"->>"演算子の処理時間が響いているのだと思う、

JSONB更新のどこが遅いのか?

f:id:nuko_yokohama:20150217215656j:image

測定結果からの考察

  • JSONB型の更新性能はあまりよろしくない。
  • ついでにいうと、このスキーマの場合、HOT更新が効かない。不要領域も一気に増大する。非常によろしくない。
    • もちろんycsb_keyの値を別カラムに展開し、これを通常のbtreeインデックスを設定すればHOT更新が効くようになるのだろうけど、それはJSONB使いとしては、なんか負けな気がw
  • 元々の更新自体も結構遅いが、"->>"演算子による処理も結構なオーバヘッドになっている。
    • 故に、JSONB型でなくJSON型にするとそれが顕著に現れる。
  • やっぱり、PostgreSQLのJSONB型は更新がかかるような用途にはあまり向かなくて、ログ蓄積モデルのように、一旦挿入したあとは検索のみ行うというモデルに向いているのかも。

AlexAlex 2016/09/06 08:27 Postgresql 9.5ではmongoと同じぐらいシンプルになったかな
update usertable set data=data || '{"field11":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}'
https://www.postgresql.org/docs/9.5/static/functions-json.html
vs
db.usertable.update({},{$set : {"field11":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}},false,true)

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/nuko_yokohama/20150217/1424177642
リンク元