LevelDB
を使用することがあったので、その使い方をメモします。
LevelDB
はGoogle
の以下のリポジトリによって公開されているデータストアの仕組みになります。
LevelDB
はKey-Valueストレージ・ライブラリ
で、文字列キーから文字列値への順序付きマッピングを提供します。
Key-ValueスタイルということなのでNoSQL的な動作をするものになるでしょうか。
- LevelDBの主な機能
- LevelDBをPythonから使用してみる
- LevelDBの操作サンプル
- 辞書データからのデータの格納
- データベース上のすべてデータの取得と削除
- LevelDBにはトランザクション機能はない
- おわりに
LevelDBの主な機能
LevelDB
の特徴は以下のようになります(GitHubより抜粋)
- キーと値は任意のバイト配列を使用可能
- データはキーでソートされて保存される
- ユーザーが独自の比較関数を提供してソート順をカスタマイズ可能
- 基本操作は以下の3つ:
Put(key,value)
: データの追加・更新Get(key)
: データの取得Delete(key)
: データの削除
- アトミックなバッチ処理をサポート
- 複数の変更を1つの単位として実行可能
- バッチ内の全操作が成功するか、全て失敗するかのどちらか
- 処理の一貫性を保証(中間状態が外部から見えない)
- パフォーマンスの向上(特に大量のデータ操作時に効果的)
- 例:複数のユーザーデータを一度に追加・更新・削除
- 一貫性のあるデータビューを得るための一時的なスナップショット作成が可能
- データに対する前方および後方のイテレーション(繰り返し処理)をサポート
- Snappy圧縮ライブラリを使用してデータを自動的に圧縮(Zstd圧縮もサポート)
- 外部アクティビティ(ファイルシステム操作など)は仮想インターフェースを通じて行われるため、ユーザーがOSとの相互作用をカスタマイズ可能
スキーマがあるタイプのデータベースではないので、作りはシンプルのようです。
LevelDB
をPythonから使用してみる
LevelDB
を使用してデータの操作を行ってみます。
動作する環境
今回実験したOSやミドルウエアは以下となります。
環境設定
PythonからLevelDB
を使用するにはpipコマンド
でplyvel
というライブラリを導入します。
では以下のようにライブラリをインストールします。pip
を使用するので、venv
環境も作成しています。
# 実験用のディレクトリ作成と移動 $ mkdir levelDB && cd $_ # 仮想環境の構築(今回は**.levelDB**としました) $ python -m venv .levelDB # 仮想環境の有効化 $ source .levelDB/bin/activate # ライブラリのインストール (.levelDB) $ pip install plyvel
LevelDB
の操作サンプル
基本的な操作のサンプルは以下となります。(以下のソースコードには型ヒントをつけています)
leveldb_sample.py
import plyvel from typing import Optional def leveldb_operations(db_path: str) -> None: """ LevelDBの基本的な操作を実行します:データベースの開封、書き込み、読み取り、削除。 この関数は以下の操作を示します: 1. LevelDBデータベースを開く(データベースがなければ作成後に開く) 2. データベースにデータを書き込む 3. データベースからデータを読み取る 4. データベースからデータを削除する 5. データベースを閉じる 引数: db_path (str): LevelDBデータベースが保存されるファイルパス。 戻り値: None """ # データベースを開く(存在しない場合は作成される) db: plyvel.DB = plyvel.DB(db_path, create_if_missing=True) try: # データの書き込み key: bytes = b'key' value: bytes = b'value' db.put(key, value) # データの読み取り retrieved_value: Optional[bytes] = db.get(key) print(f"取得された値: {retrieved_value}") # b'value' が出力される # データの削除 db.delete(key) # 削除されたことを確認 deleted_value: Optional[bytes] = db.get(key) print(f"削除後の値: {deleted_value}") # None が出力される finally: # データベースを閉じる db.close() if __name__ == "__main__": leveldb_operations('/tmp/testdb/')
LevelDB
の特徴として、Key-Value
それぞれがバイト列である点です。そのため、文字列を使用する場合はencode()メソッド
を使ってバイト列に変換する必要があります。文字列にbがついているのはそんな理由のためです。
データの扱い部分抜粋
# データの書き込み key: bytes = b'key' value: bytes = b'value' db.put(key, value)
上記のコードを実行すると以下のようになります。
(.levelDB) $ python leveldb_sample.py 取得された値: b'value' 削除後の値: None
無事に動作しました。データが格納されるとデータベースのファイルが生成されます。
辞書データからのデータの格納
使用していると1つ1つのデータを登録するのは大変なので、辞書データ(Dict形式)を使用してを登録する関数を作成します。
leveldb_insert_data_sample.py
import plyvel from typing import Dict, Union def insert_Dict(db_path: str, data: Dict[Union[str, bytes], Union[str, bytes]]) -> int: """ 辞書のデータを LevelDB に一括で登録します。 この関数は以下の操作を行います: 1. 指定されたパスの LevelDB データベースを開く 2. 与えられた辞書のすべてのキーと値のペアをデータベースに書き込む 3. データベースを閉じる 引数: db_path (str): LevelDB データベースが保存されているファイルパス data (Dict[Union[str, bytes], Union[str, bytes]]): 登録するデータを含む辞書。キーと値は文字列またはバイト列。 戻り値: int: 登録されたキーと値のペアの数 注意: - キーと値が文字列の場合、自動的に UTF-8 でエンコードされます。 - すでに存在するキーの場合、値が上書きされます。 """ db = plyvel.DB(db_path, create_if_missing=True) inserted_count = 0 try: with db.write_batch() as batch: for key, value in data.items(): # 文字列の場合、バイト列に変換 if isinstance(key, str): key = key.encode('utf-8') if isinstance(value, str): value = value.encode('utf-8') batch.put(key, value) inserted_count += 1 print(f"登録されたデータ数: {inserted_count}") finally: db.close() return inserted_count if __name__ == "__main__": db_path = './testdb/' # テストデータ test_data = { 'key1': 'value1', 'key2': 'value2', b'key3': b'value3', 'キー4': '値4', b'\xe3\x81\x82': 'あ', } # データを一括登録 inserted = insert_Dict(db_path, test_data) print(f"合計 {inserted} 件のデータが登録されました。") # 登録されたデータを確認 db = plyvel.DB(db_path, create_if_missing=True) try: print("\n登録されたデータ:") for key, value in db: print(f"キー: {key}, 値: {value}") finally: db.close()
実行すると以下のようになります。
(.levelDB) $ python leveldb_insert_data_sample.py 登録されたデータ数: 5 合計 5 件のデータが登録されました。 登録されたデータ: キー: b'key', 値: b'value' キー: b'key1', 値: b'value1' キー: b'key2', 値: b'value2' キー: b'key3', 値: b'value3' キー: b'\xe3\x81\x82', 値: b'\xe3\x81\x82' キー: b'\xe3\x82\xad\xe3\x83\xbc4', 値: b'\xe5\x80\xa44'
データベース上のすべてデータの取得と削除
取得・削除もできれば一気に行いたいので、その処理を行う関数も作成してみます。
leveldb_all_data_sample.py
import plyvel from typing import Dict, List, Tuple def extract_all_data(db_path: str) -> Dict[bytes, bytes]: """ LevelDBからすべてのデータを抽出します。 引数: db_path (str): LevelDBデータベースが保存されているファイルパス 戻り値: Dict[bytes, bytes]: 抽出されたすべてのキーと値のペアを含む辞書 """ db = plyvel.DB(db_path, create_if_missing=True) extracted_data: Dict[bytes, bytes] = {} try: with db.iterator() as it: for key, value in it: extracted_data[key] = value print(f"抽出されたデータ数: {len(extracted_data)}") finally: db.close() return extracted_data def delete_all_data(db_path: str) -> List[bytes]: """ LevelDBからすべてのデータを削除します。 引数: db_path (str): LevelDBデータベースが保存されているファイルパス 戻り値: List[bytes]: 削除されたキーのリスト """ db = plyvel.DB(db_path, create_if_missing=True) deleted_keys: List[bytes] = [] try: with db.write_batch() as batch: for key, _ in db: batch.delete(key) deleted_keys.append(key) print(f"削除されたキー数: {len(deleted_keys)}") finally: db.close() return deleted_keys if __name__ == "__main__": db_path = './testdb/' # データを抽出 extracted = extract_all_data(db_path) print("\n抽出されたデータの一部:") for i, (key, value) in enumerate(list(extracted.items())): print(f"{i+1}. キー: {key}, 値: {value}") # データを削除 deleted = delete_all_data(db_path) print("\n削除されたキーの一部:") for i, key in enumerate(deleted): print(f"{i+1}. {key}")
こちらのコードを実行すると以下のように表示されます。
(.levelDB) $ python leveldb_all_data_sample.py 抽出されたデータ数: 6 抽出されたデータの一部: 1. キー: b'key', 値: b'value' 2. キー: b'key1', 値: b'value1' 3. キー: b'key2', 値: b'value2' 4. キー: b'key3', 値: b'value3' 5. キー: b'\xe3\x81\x82', 値: b'\xe3\x81\x82' 6. キー: b'\xe3\x82\xad\xe3\x83\xbc4', 値: b'\xe5\x80\xa44' 削除されたキー数: 6 削除されたキーの一部: 1. b'key' 2. b'key1' 3. b'key2' 4. b'key3' 5. b'\xe3\x81\x82' 6. b'\xe3\x82\xad\xe3\x83\xbc4'
LevelDB
にはトランザクション機能はない
他のデータベースにはトランザクション機能があるものも多いのですが、LevelDB
には実はその機能はありません。その代わりになるものとしてバッチ機能
があります。
使い方はwith db.write_batch() as batch:
とブロックを作りその中でデータを処理する形です。以下がコード例になります。
import plyvel # データベースを開く db = plyvel.DB('./testdb/', create_if_missing=True) # バッチ処理を作成 with db.write_batch() as batch: batch.put(b'key1', b'value1') batch.put(b'key2', b'value2') batch.delete(b'key3') # ここでバッチが自動的にコミットされる # データベースを閉じる db.close()
バッチ処理
はパフォーマンスを向上させる機能で、トランザクションのような機能はありません。完全なデータ一貫性を保証する必要がある場合は、トランザクションをサポートするデータベースを採用したほうがよいでしょう。
LevelDB
の操作はこれで概ねのデータを作成することができました。
おわりに
LevelDB
のPythonでの基本的な使用方法をまとめてみました。キーバリューストアであるLevelDB
は、ログ保存などの用途に適しているように感じます。これまではSQLite
を使用することが多かったですが、スキーマなどが不要であれば、LevelDB
を使用するのもいいかなと思います。特にJSON
などのデータを使用することを考えるのであれば、こちらでも問題は出てこないかなと思います。
とはいえ、実際のアプリケーション開発での使用では、大規模データの取り扱いや並行アクセスを考えると別のアプローチをしたほうが適切だと思います。