これは、なにをしたくて書いたもの?
ふと、RocksDBのPythonバインディングを使ってみようかなと思いまして。
python-rocksdb
RocksDB自体はC++で作られていますが、pytyon-rocksdbを使うことでPythonからも利用することができます。
Welcome to python-rocksdb’s documentation! — python-rocksdb 0.6.7 documentation
GitHub - twmht/python-rocksdb: Python bindings for RocksDB
なのですが、よくよく見るとオリジナルはこちらのリポジトリ(pyrocksdb)で、メンテナンスされたなくなったものが
forkされたようです。
GitHub - stephan-hof/pyrocksdb: Python bindings for RocksDB
APIドキュメントは、こちら。
Python driver for RocksDB — python-rocksdb 0.6.7 documentation
環境
今回の環境は、こちらです。
$ python3 -V Python 3.6.9 $ pip3 -V pip 9.0.1 from /path/to/venv/lib/python3.6/site-packages (python 3.6)
OSは、Ubuntu Linux 18.04 LTSです。
$ uname -srvmpio Linux 4.15.0-99-generic #100-Ubuntu SMP Wed Apr 22 20:32:56 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 18.04.4 LTS Release: 18.04 Codename: bionic
python-rocksdbをインストールする
インストールは、こちらを参考にしながら。OSのパッケージインストールも行う必要があるようです。
Installing — python-rocksdb 0.6.7 documentation
なのですが、けっこうハマりました。
Python 3は入っているので、ドキュメントを見て「python3-dev」、「librocksdb-dev」をインストール。
$ sudo apt install python3-dev librocksdb-dev
自分の環境では、こちらも足りなかったのでインストールすることに。
$ sudo apt install libsnappy-dev libbz2-dev liblz4-dev
そしてインストールですが、ドキュメントを見ると「python-rocksdb」だけがあればよさそうですが、実は「Cython」も必要になります。
$ pip3 install Cython python-rocksdb
Cythonが入っていないと、python-rocksdbのインストール時のビルドで失敗します。
rocksdb/_rocksdb.cpp:1:2: error: #error Do not use this file, it is the result of a failed Cython compilation. #error Do not use this file, it is the result of a failed Cython compilation. ^~~~~
あと、ライブラリが足りていなかったので、このあたりでもエラーになりました。
/usr/bin/ld: -lsnappy が見つかりません /usr/bin/ld: -lbz2 が見つかりません /usr/bin/ld: -llz4 が見つかりません
動作確認には、テストコードを使うことにするので、pytestをインスールします。
$ pip3 install pytest
バージョン。
$ pip3 freeze | grep -E 'Cython|rocksdb|pytest' Cython==0.29.17 pytest==5.4.2 python-rocksdb==0.7.0
なんとか、インストールまで完了です。
テストコードの雛形
pytyon-rocksdbを使った確認は、テストコードを使って行います。
雛形として意したのは、こちら。
tests/rocsdb_test.py
import rocksdb ## ここに、テストを書く!
import文が1行あるだけですが…。
では、Tutorialを見ながら使っていってみましょう。
Basic Usage of python-rocksdb — python-rocksdb 0.6.7 documentation
基本的な使い方
最初は、簡単なput/get/deleteから。
作成したコードは、こちら。
def test_rocksdb_getteing_started(): options = rocksdb.Options() options.create_if_missing = True # more options... db = rocksdb.DB("example-db", options) db.put(b"key1", b"value1") db.put(b"key2", b"value2") assert db.get(b"key1") == b"value1" assert db.get(b"key2") == b"value2" db.delete(b"key1") db.delete(b"key2") assert db.get(b"key1") is None assert db.get(b"key2") is None
最初に、オプションを指定してDBインスタンスを作成します。
options = rocksdb.Options() options.create_if_missing = True # more options... db = rocksdb.DB("example-db", options)
DBのインスタンスを作成する際に与えるパスは、RocksDBのファイルを作成するディレクトリになります。この中に、データを
保存するファイルが置かれていくのですが、チュートリアルを見ているとファイル名っぽく見えるように書かれている気がします…。
作成されるのは、実際はディレクトリです。
あとはほぼ見たままの操作ですが、DBとやり取りするデータはバイト表現であることに注意しましょう。
RocksDB stores all data as uninterpreted byte strings.
it is strongly recommended to use an explicit b prefix for all byte string literals in both python2 and python3 code.
バッチ操作
更新系の操作は、バッチ処理でまとめることができます。
def test_batch_operation(): db = rocksdb.DB("example-db", rocksdb.Options(create_if_missing=True)) batch = rocksdb.WriteBatch() batch.put(b"batch-key1", b"batch-value1") batch.put(b"batch-key2", b"batch-value2") batch.put(b"batch-key3", b"batch-value3") db.write(batch) values = db.multi_get([b"batch-key1", b"batch-key2", b"batch-key3"]) assert values[b"batch-key1"] == b"batch-value1" assert values[b"batch-key2"] == b"batch-value2" assert values[b"batch-key3"] == b"batch-value3" batch = rocksdb.WriteBatch() batch.delete(b"batch-key1" ) batch.delete(b"batch-key2") batch.delete(b"batch-key3") db.write(batch) values = db.multi_get([b"batch-key1", b"batch-key2", b"batch-key3"]) assert values[b"batch-key1"] is None assert values[b"batch-key2"] is None assert values[b"batch-key3"] is None
データベースの作成については、今回はOptionsを使わずに、キーワード引数で簡単に済ませました。こういった書き方も
可能です。
db = rocksdb.DB("example-db", rocksdb.Options(create_if_missing=True))
Iterator
キーと値のペア、キー、値のそれぞれのパターンで、Iteratorを利用することができます。
def test_iteration(): db = rocksdb.DB("example-db", rocksdb.Options(create_if_missing=True)) db.put(b"key1", b"value1") db.put(b"key2", b"value2") db.put(b"key3", b"value3") iterator = db.iteritems() iterator.seek_to_first() assert list(iterator) == [(b"key1", b"value1"), (b"key2", b"value2"), (b"key3", b"value3")] iterator = db.iterkeys() iterator.seek_to_first() assert list(iterator) == [b"key1", b"key2", b"key3"] iterator = db.itervalues() iterator.seek_to_first() assert list(iterator) == [b"value1", b"value2", b"value3"] db.delete(b"key1") db.delete(b"key2") db.delete(b"key3")
DB#iteritemsの場合はキーと値のタプルのIteratorが、DB#iterkeysではキーのIterator、DB#itervaluesでは値のIteratorが
返ってきます。
Iteratorは、最初にseekする必要がありseek_to_firstで最初のエントリ、seek_to_lastで最後のエントリ、seek(key)で指定の
位置からイテレーションを行うことができます。
とりあえず、基本的な使い方はこんなところでしょうか。
他にもSnapshot、Merge、PrefixEtractorなどのトピックもあるようですが、そのあたりは機会があれば。
地味に、インストールが1番大変だったように思います…。
今回はIteratorをリストに変換してAssertionしていますが、