LevelDBとは?
ファイルに保存するタイプのkey-value storeで、key => valueのひも付けを高速に読み書きできるGoogle製のライブラリです。PythonのここではLevelDBをplyvelというライブラリを使ってpythonで使ってみるチュートリアルをご紹介します。(plyvel以外にもPythonのLevelDBライブラリは存在しますが、使ってみた感じplyvelの出来がよさそうでしたので)
こういうたぐいのものでは古くはBerkeleyDBや、モダンなものだと日本の平林さんが作成されたKyotoCabinetが有名です。サーバ型ではmemcachedやRedisがありますが、LevelDBはサーバ型ではなく、ローカルのファイルを操作するライブラリです。たくさんのサーバ・プロセスなどからkey-valueのデータを共有して保存したいときはmemcachedやKyotoTycoon, Redisなどの方が向いているでしょう。
Pythonにはキーに値をひもづけて管理するデータ構造としてdict
がありますが、メモリ上に乗り切らない大量のデータなどを扱いたいときに便利なライブラリです。
事前知識
- LevelDBはあるバイト列であるkeyに, あるバイト列であるvalueをひもづけて管理するkey-value store。keyからvalueを高速にルックアップすることができる。
- LevelDBのデータベースはディレクトリになっている。
- LevelDBデータベースは同時にたかだか1つのプロセスでしか開けない。(既に別のプロセスが開いているとエラーになる)
- サーバではないので、通信でkey-valueを操作するものではない。(ローカルのファイルを操作するライブラリである。)
- plyvelはnativeのleveldbのライブラリを使っているC拡張で実装されている(ゆえに速いがライブラリのインストールとコンパイルが必要)
- LevelDBのキーと値にはバイナリ値を与えることになる。
- 保存したい値が
string
ではない場合はpickleやmsgpackなど、なんらかの方法でシリアライズする必要がある - pythonの
unicode
型の値はval.encode('utf-8')
などでバイナリにエンコードしてあげる必要あり
- 保存したい値が
インストール(1/2)
まずleveldbのネイティブライブラリが必要なのでインストールします。MacでHomebrewを使っている人はbrewで入れると楽です。
$ brew install leveldb
他のプラットフォームではMakefile
があるようですので、make
とsudo make install
でインストールできるのではないでしょうか。
インストール(2/2)
plyvel
をインストールしましょう。なんでこんな綴りかよくわからないですが、とにかくplyvelです。
$ pip install plyvel
システムのincludeパスにPython.hがなかったりすると拡張ライブラリがコンパイルできなくてエラーになるかもしれません。Linuxならpython-devel
などのパッケージが入ってるか確認してみるとよいでしょう。
DBをopenしてcloseしてみる
LevelDBのデータベースはディレクトリになってるので注意。
import plyvel
my_db = plyvel.DB('/tmp/test.ldb', create_if_missing=True) # なければつくる
my_db.close()
create_if_missing=False
の場合、DBファイルが存在しなければ例外が発生します。
key => valueを登録する
LevelDBはバイト列にバイト列をひもづけることしかできないので基本は以下のようになります。
my_db.put('key1', 'value1')
my_db.put(u'ほげ'.encode('utf-8'), u'ほげバリュー'.encode('utf-8')) # unicodeを使うときはバイト列にする
あるキーに、複雑なデータ構造をひもづけて保存したいときは下記のpickleやMessagePackを使う方法を試してみてください。
keyから値を取り出す
そのまんま、get
してください。
value1 = my_db.get('key1')
value2 = my_db.get(u'ユニコードのキーはencodeでバイト列にする'.encode('utf-8'))
keyを削除
delete
メソッドで消します。
my_db.delete('key1')
my_db.delete(u'ユニコードのキーはencodeでバイト列にする'.encode('utf-8'))
保存されている項目をイテレーション
キーと値をたくさん登録して、最後に全部をCSVに出したいとか、そういうことってありますよね。LevelDBでも当然保存されてるキーと値をイテレータで取り出すことができます。
my_db.put('key1', '1')
my_db.put('key2', '1')
my_db.put('key3', '3')
for key, value in my_db:
print '%s => %s' % (key, value)
# 出力:
# key1 => 1
# key2 => 2
# key3 => 3
キーの範囲を指定したり、キーのprefixを指定して取り出すこともできるようです。詳しくはplyvelのドキュメントのiteratorsのセクションを見て見てください。
構造化されたvalueを登録してみる: pickle
冒頭にも書きましたが、valueはバイト列でなければいけないので、keyに対してdictやlist/などの構造化されたデータをひもづけてLevelDBに保存したい場合、シリアライズする必要があります。Pythonにpickleというシリアライズ用のライブラリが標準であるので、これを使うと簡単です。Pickleは基本的なデータ型だけでなく、関数やオブジェクトなどもシリアライズすることができ、非常に強力です。ただ、Python以外の言語ではpickleデータをデシリアライズ(元のデータ構造に戻す)ことが難しいので他の言語でもデータを流用したい場合は後述のMessagePack形式でシリアライズするとよいでしょう。
fukuzatsu1 = dict(a=10, b=20, c=[123, 234, 456])
my_db.put('key1', fukuzatsu1) # エラーになる
import pickle
serialized1 = pickle.dumps(fukuzatsu1)
my_db.put('key1', serialized1) # OK
# valueを使うとき
serialized1 = my_db.get('key1')
fukuzatsu1 = pickle.loads(serialized1)
print fukuzatsu1['a'] # => 10
pickleの使い方は詳しくはpickleのドキュメントを参照してください。
構造化されたvalueを登録してみる: msgpack
pickleの他にはMessagePackという日本発のシリアライジングフォーマットがおすすめです。MessagePackはデータをコンパクトかつ高速にシリアライズできることが特徴です。MessagePackをPythonで使うときはmsgpack-pythonというパッケージをインストールします。
$ pip install msgpack-python
msgpackではpickleのdumps
/loads
に変わってpackb
/unpackb
を使います。
fukuzatsu1 = dict(a=10, b=20, c=[123, 234, 456])
import msgpack
serialized1 = msgpack.packb(fukuzatsu1, encoding='utf-8')
my_db.put('key1', serialized1)
# valueを使うとき
serialized1 = my_db.get('key1')
fukuzatsu1 = msgpack.unpackb(serialized1, encoding='utf-8')
print fukuzatsu1['a'] # => 10
詳しくはmsgpack-pythonのAPIドキュメントを参照してください。
参考リンク
- グーグルがNoSQL軽量ライブラリ「LevelDB」をオープンソース化。SQLiteとの比較ベンチマークも公開
- LevelDB
- Plyvel Document
- シリアライズ関連
- サーバ型 key-value store