log10(n)=3程度でのDBエンジンの検討

最大1000件程度のデータを処理するためののデータベースは何がいいか。 シェルスクリプトからも気軽に操作できて、 いざとなればテキストエディタでデータの一部をごにょっと変更できるような、 そんなデータベースを構築したい。候補は SQLite3 で、 テキストエディタでデータをいじるための wrapper である visq3 も作って準備万端にしていたのだが、ちょっと待ったを掛けて もっといいものの可能性を検討してからにしたい。

最近YAMLのスッキリした可読性に惹かれていて、 YAMLでも行けるならそれがいいかなと思ってこの実験を始めた。 YAMLとPStore(Ruby固有)にはtransactionの概念があって、 プログラマが気にしなくても排他制御やロールバックをしてくれる。 小規模データベースとしてものすごく使いやすい。

今回の要件をまとめると

となる。ざっとみた限り、シェルスクリプトからも使いやすいとなると SQLite3とYAMLの一騎打ちという感じなのだが せっかくなので、近傍のデータ処理系を実験対象に入れてみた。

候補データの可読性 トランザクション提供データの汎用性速度
SQLite3×(でもvisq3でカバー)
PStore×(要コンバータ)○(Ruby/JSON)?
YAML◎(一番好み)○(Ruby/yaml)?
JSON×(Ruby/JSON)?
CSV×(Ruby/CSV)?

意外にもJSONライブラリにトランザクションがなかった(あるの?)。 もともとデータベースとして使うと言うより シリアライズしたデータの受け渡しが主眼なのでそういうもんか。 読み込みと書き出しを個別に行なう必要があるのは CSVライブラリもいっしょで、自前で排他制御などする必要があるため面倒臭い。 将来自分のコードを忘れて機能修正するときを考えるとちょっといやん。

速度が分からないので調べてみた。

実験条件

通常1000件程度で最大でも1万件は行かないことは分かっているので 間を取ってちょっと上の7000件のデータで実験。

レコード数7000
1レコードあたりのバイト数約500B
フィールド数9

測定機は AMD Opteron 3280(8C 2.4GHz) でデータをNFS上に置いた。 データは高々数MBなのでローカルディスクに置いても大差ないだろう。

まず、mkdummy.zsh でダミーデータ(dummy.csv)を生成。これを元に SQLite3, PStore, YAML, JSON, CSV の初期データを db-test.rb で作成。

for v in SQ3 PST YAML JSON CSV; do
    time env ${v}=1 ./do-test.rb init
  done

あ、もうここで速度にかなりの差が。すでにこの段階で 更新時間のテストをやる気分が薄れてしまったが気を取り直して...

実験

7000件レコードを読み出してそのうちの第2フィールドの値を ちょっとだけ書き変えて保存する実験。

for v in SQ3 PST YAML JSON CSV; do
    time env ${v}=1 ./do-test.rb
  done
更新対象更新時間所要時間(total)
SQLite30.580.83
PStore2.642.91
YAML28.8829.26
JSON2.042.31
CSV4.214.46

ぬぬ、ひいきのYAML遅いな。残念。あとJSONはえー。 内部コードに落とすPStoreに勝ってるのはさすがだが、このくらいの差なら transactionの安心感でPStoreを選んじゃうかも。あとCSV健闘。 2倍程度遅いだけならJSONよりCSVてのもありだ。でも排他処理欲しい。 ちなみに表中のSQLite3の0.58秒てのはちょっとハンデを負わせていて、 必要ないのに7000件全レコードを読ませてから更新処理している。 通常のSQL処理のように必要なレコードのみの更新をすると以下のとおり圧勝。

time SQ3=sel ./do-test.rb
Updation took 0.05s
SQ3=sel ./do-test.rb  0.25s user 0.00s system 82% cpu 0.301 total

更新時間0.05秒。まあそういうことで、 YAMLではなくSQLite3にするふん切りが付いた。