このエントリーをはてなブックマークに追加

ejdb

ejdbを使ってみたので書き留めておく。

ejdbって?

ejdbはmongodbのsqliteみたいなもの。 server, clientではなくファイルにデータを保存する。 mongodbと同じデータ形式を扱えるので、jsonデータをそのまま保存できる。 クエリーもmongodbと同じようなものが用意されている。

中身はtokyo cabinetのコードをベースにしたCのライブラリになっていて、 中でmongodbのmongo-c-driverを使っているみたい。 それにnodejsとpythonのbindingをしている。 ちなみに、githubのissue眺めてるとkyoto cabinetのコードを使った方が良くない?的な質門に、 kyoto cabinetはGPLでtokyo cabinetはLGPLだからこっち選んだ的なことが書いてあった。

また、現状ではnode.jsだと0.8.0以上、pythonだと3.2以上で動作する。

とりあえず、使ってみる

普通にmakeとかやってもうまく行かなかったので、 以下のようにインストール

  • tcejdbのインストール
$ cd tcejdb
$ ./configure --prefix=/usr/local
$ make
$ sudo make install
$ cd ..
  • pyejdbインストール (python3のヘッダ必須)
$ cd pyejdb
$ python3 setup.py build
$ sudo python3 setup.py install
$ cd ..
  • node.jsモジュール
$ sudo npm install -g node-gyp
$ npm build .
(build/Releaseにできてる)

とりあえず、ビルドとインストールが一通り終わったところで、 pythonのサンプルを改造してあまり意味はないかも知れないけど、 ejdbとsqliteをシンプルな検索で性能比較してみた。

  • ejdb テストコード (ejdb.py)
import pyejdb
import datetime
import os

if not os.path.exists("zoo"):
    ejdb = pyejdb.EJDB("zoo", pyejdb.DEFAULT_OPEN_MODE | pyejdb.JBOTRUNC)
    ejdb.ensureCollection("parrots2")
    ejdb.ensureStringIndex("parrots2", "extra1")
    ary = []
    for i in range(1, 100000):
        ary.append({
            "name": "Grenny" + str(i),
            "type": "African Grey",
            "male": True,
            "age": 100,
            "likes": ["green color", "night", "toys"],
            "extra1": "hogehoge" + str(i),
            "extra2": "abc"
        })
    ejdb.save("parrots2", *ary)
    ejdb.close()

start = datetime.datetime.now()
ejdb = pyejdb.EJDB("zoo", pyejdb.DEFAULT_OPEN_MODE)
for i in range(1, 10000):
    with ejdb.find("parrots2", {"extra1" : "hogehoge%d" % (i) }) as cur:
        for p in cur:
            #print(p["name"])
            pass
ejdb.close()
end = datetime.datetime.now()
print(str(end-start))
  • sqlite テストコード (sqlite.py)
import sqlite3
import datetime
import os

if not os.path.exists("data.db"):
    con = sqlite3.connect("data.db")
    sql = """
    create table test (
      name varchar(32),
      type varchar(32),
      age integer,
      male integer,
      likes varchar(64),
      extra1 varchar(32),
      extra2 varchar(32)
    );
    """
    con.execute(sql)
    sql = "create INDEX extra1index on test(extra1);"
    con.execute(sql)
    for i in range(1, 100000):
        sql = "insert into test values ('Grenny%d', 'African Grey', 1, 100, 'green color, night, toys', 'hogehoge%d', 'abc');" % (i,i)
        con.execute(sql)
    con.commit()
    con.close()

start = datetime.datetime.now()
con = sqlite3.connect("data.db")
c = con.cursor()
for i in range(1, 10000):
    c.execute("select * from test where extra1 == 'hogehoge%d';" % (i))
    for row in c:
        #print(row[0])
        pass
con.close()
end = datetime.datetime.now()
print(str(end-start))
  • 実行結果
$ python3 ejdb.py
0:00:02.397433
$ python3 sqlite.py
0:00:00.408522

まぁ、予想通りだけど、sqliteの方が6倍ぐらい速かった。 こういうのは、格納するデータの特性に応じて使い分けるのが良いと思う。

あと、ejdbは複雑な検索にはindexが効かなくなるようで、 $strorとか使うとindexが意味をなしてなかった。

ちなみに、lsをやると

$ ls -al

合計 35648
drwxrwxr-x  2 user user     4096  3月 10 04:56 .
drwxrwxr-x 12 user user     4096  2月 17 16:13 ..
-rw-r--r--  1 user user 10903552  3月 10 04:56 data.db
-rw-rw-r--  1 user user      926  3月 10 04:56 ejdb.py
-rw-rw-r--  1 user user      972  3月 10 04:53 sqlite.py
-rw-r--r--  1 user user   529008  3月 10 06:14 zoo
-rw-r--r--  1 user user 21702184  3月 10 06:14 zoo_parrots2
-rw-r--r--  1 user user  3344896  3月 10 06:14 zoo_parrots2.idx.sextra1.lex

結果から分かるように、データベース名、コレクション名でファイルが出来ていて、 1つのindexに対して、ファイルが1つ出来る仕様になってるみたい。 容量もsqliteより大きめ。

補足

ejdbのCLIツールも用意されてるようで、以下のようにすればさっき作ったファイルをCLIで操作できる。

$ cd pyejdb
$ cd samples
$ node ../../node/bin/cli.js
Welcome to EJDB CLI v1.0.63
ejdb> db.open("zoo")
{ file: '/home/hiro/Download/Softmotions-ejdb-3fd8658-2/pyejdb/samples/zoo',
  collections:
   [ { name: 'parrots2',
       file: '/home/hiro/Download/Softmotions-ejdb-3fd8658-2/pyejdb/samples/zoo_parrots2',
       records: 99999,
       options:
        { buckets: 131071,
          cachedrecords: 0,
          large: false,
          compressed: false },
       indexes:
        [ { field: 'extra1',
            iname: 'sextra1',
            type: 'lexical',
            records: 99999,
            file: '/home/hiro/Download/Softmotions-ejdb-3fd8658-2/pyejdb/samples/zoo_parrots2.idx.sextra1.lex' } ] } ] }
ejdb> db
.
.
.

追記

ejdbはquery objectはずして, sqliteはwhereを外して、99999件全部取るだけにして比較したらejdb半端なく遅かった。

$ python3 sqlite.py
0:00:00.386605
0:00:00.414767
$ python3 ejdb.py
0:00:02.387387
0:00:30.644258

でも、$fieldsで取得するフィールドを絞ると速くなるから、中のデータ変換処理か何かかが遅いのかも

$ python3 ejdb.py
0:00:02.407641
select all field
0:00:31.256630
select name field only
0:00:08.942136
name:
email:
comment: