最大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) | 
|---|---|---|
| SQLite3 | 0.58 | 0.83 | 
| PStore | 2.64 | 2.91 | 
| YAML | 28.88 | 29.26 | 
| JSON | 2.04 | 2.31 | 
| CSV | 4.21 | 4.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にするふん切りが付いた。