データナード

機械学習と自然言語処理についての備忘録 (旧ナード戦隊データマン)

超効率のいいローカルキーバリューストアkyotocabinetを試してみた

データベースを使う必要はないが、メモリ内でやるには量が多すぎる、という場合にキーバリューストアを利用することができますが、より簡単かつ高速に実行できるライブラリの一つがkyotocabinet1です。今回は、kyotocabinetをインストールし、パフォーマンスをテストします。

概要

古いライブラリにQDBMというライブラリがあり、こちらもキーバリューストアとして使えます。そして、その後継となっているのがtokyocabinetです。tokyocabinetは、空間効率・時間効率・並列性・利便性・堅牢性という点でQDBMよりも優れています。そして今回はtokyocabinetではなくkyotocabinetを使いますが、kyotocabinetはtokyocabinetよりもすべての点において優れていると説明されています2

技術的概要

Kyoto Cabinetは、データベース管理のルーチンライブラリです。データベースはレコードを含む単純なデータファイルで、各レコードはキーと値のペアです。すべてのキーと値は、可変長のシリアルバイトです。バイナリデータと文字列の両方をキーおよび値として使用できます。各キーはデータベース内で一意である必要があります。データテーブルの概念もデータ型もありません。レコードはハッシュテーブルまたはB +ツリーで整理されます。

kyotocabinetが100万件のレコードを保存する経過時間は、ハッシュデータベースでは0.9秒、B +ツリーデータベースでは1.1秒です。さらに、レコードのオーバーヘッドは、ハッシュデータベースでは16バイト、B +ツリーデータベースでは4バイトです。データベースのサイズは最大8EBです。

インストール

apt install zlib1g zlib1g-dev
test -f kyotocabinet-python-1.22.tar.gz || wget https://fallabs.com/kyotocabinet/pythonpkg/kyotocabinet-python-1.22.tar.gz 
test -f kyotocabinet-1.2.77.tar.gz || wget https://fallabs.com/kyotocabinet/pkg/kyotocabinet-1.2.77.tar.gz
test -d kyotocabinet-python-1.22 || tar xzvf kyotocabinet-python-1.22.tar.gz
test -d kyotocabinet-1.2.77 || tar xzvf kyotocabinet-1.2.77.tar.gz

type kchashmgr || (cd kyotocabinet-1.2.77; ./configure; make; make install; cd ..)
ldconfig
(cd kyotocabinet-python-1.22; make; make install; cd ..)
ldconfig

パフォーマンステスト

今回は、kyotocabinetの単純なハッシュデータベースに対して、100万件のデータを挿入する時間と、100万件のデータを取得する時間を測定します。

コード

from kyotocabinet import DB

size = 1000000

@profile
def test1():
    db = DB()
    db.open("test.kch")
    for i in range(size):
        db.set(str(i), 1)

@profile
def test2():
    db = DB()
    db.open("test.kch")
    for i in range(size):
        db.get(str(i))


if __name__ == "__main__":
    test1()
    test2()

line_profiler

pip3 install line_profiler
kernprof -l test.py
python3 -m line_profiler test.py.lprof

結果

Timer unit: 1e-06 s

Total time: 1.1944 s
File: prof.py
Function: test1 at line 5

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     5                                           @profile
     6                                           def test1():
     7         1          2.0      2.0      0.0      db = DB()
     8         1        109.0    109.0      0.0      db.open("test.kch")
     9   1000001     227005.0      0.2     19.0      for i in range(size):
    10   1000000     967280.0      1.0     81.0          db.set(str(i), 1)

Total time: 1.04471 s
File: prof.py
Function: test2 at line 12

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    12                                           @profile
    13                                           def test2():
    14         1          5.0      5.0      0.0      db = DB()
    15         1         40.0     40.0      0.0      db.open("test.kch")
    16   1000001     235215.0      0.2     22.5      for i in range(size):
    17   1000000     809446.0      0.8     77.5          db.get(str(i))
-rw-r--r-- 1 root root 29M Nov 22 04:26 test.kch

説明

  • 100万件のデータの挿入には約0.9秒, 100万件のデータの取得には約0.8秒かかっています。
  • ここで作成した100万件のデータに対するデータベースの総サイズは29Mバイトです。

考察

sqliteやredisを使うまでもない単純な目的で使うなら、kyotocabinetは非常に使いやすいライブラリであると言えます。データベースはデーモンを立ち上げる必要もなく、単一のファイルとして保存されています。しかも、保存可能な最大サイズが8EBという途方もなくでかい数字です。メモリ上に乗らないdict形式のデータがある場合は、kyotocabinetを使えば省メモリで実行できるので、利用用途は多そうです。データの取得にかかる時間はO(1)なので、空間・時間ともに優れたライブラリだと言えます。

追記: 2019-11-22 14:06

もし、ローカルだけではなく、ネットワークを通じてkyotocabinetにアクセスしたい場合は、Kyoto Tycoonが使えるようです。

Kyoto Tycoon: a handy cache/storage server

参考