Tokyo Cabinet第1版仕様書

Copyright (C) 2006-2007 Mikio Hirabayashi
Last Update: Mon, 16 Jul 2007 19:45:16 +0900

目次

  1. はじめに
  2. 特徴
  3. インストール
  4. ユーティリティAPI
  5. ハッシュデータベースAPI
  6. B+木データベースAPI
  7. データベースフォーマット
  8. よく聞かれる質問
  9. ライセンス

はじめに

Tokyo Cabinetはデータベースを扱うルーチン群のライブラリです。データベースといっても単純なもので、キーと値のペアからなるレコード群を格納したデータファイルです。キーと値は任意の長さを持つ一連のバイト列であり、文字列でもバイナリでも扱うことができます。テーブルやデータ型の概念はありません。レコードはハッシュ表またはB+木で編成されます。

ハッシュ表のデータベースでは、キーはデータベース内で一意であり、キーが重複する複数のレコードを格納することはできません。このデータベースに対しては、キーと値を指定してレコードを格納したり、キーを指定して対応するレコードを削除したり、キーを指定して対応するレコードを検索したりすることができます。また、データベースに格納してある全てのキーを順不同に一つずつ取り出すこともできます。このような操作は、UNIX標準で定義されているDBMライブラリおよびその追従であるNDBMやGDBMに類するものです。Tokyo CabinetはDBMのより良い代替として利用することができます。

B+木のデータベースでは、キーが重複する複数のレコードを格納することができます。このデータベースに対しては、ハッシュ表のデータベースと同様に、キーを指定してレコードを格納したり取り出したり削除したりすることができます。レコードはユーザが指示した比較関数に基づいて整列されて格納されます。カーソルを用いて各レコードを昇順または降順で参照することができます。この機構によって、文字列の前方一致検索や数値の範囲検索が可能になります。また、B+木のデータベースではトランザクションが利用できます。

Tokyo CabinetはCで記述され、CのAPIとして提供されます。Tokyo CabinetはC99およびPOSIX準拠のAPIを備えるプラットフォームで利用できます。Tokyo CabinetはGNU Lesser General Public Licenseに基づくフリーソフトウェアです。


特徴

Tokyo CabinetはQDBMの後継であり、空間効率と時間効率と使いやすさを向上させた製品です。この節ではTokyo Cabinetの特徴について説明します。

DBM業界の最右翼

Tokyo CabinetはQDBMの後継として次の点を目標として開発されました。これらの目標は達成されており、Tokyo CabinetはQDBMを置き換える製品だと言えます。

Tokyo CabinetはQDBMと同様に、伝統的なDBMが抱える三つの制限事項を回避しています。すなわち、プロセス内で複数のデータベースを扱うことができ、キーと値のサイズに制限がなく、データベースファイルがスパースではありません。さらに、QDBMが抱える二つの制限事項を回避しています。すなわち、2GB以上のデータベースファイルを扱うことができ、複数のスレッドが同時にデータベースの探索を行うことができます。

Tokyo Cabinetは高速に動作します。例えば100万件のレコードの登録にかかる時間は、ハッシュデータベースで1.5秒ほど、B+木データベースで2.2秒ほどです。そしてTokyo Cabinetのデータベースは小さいです。例えば1レコードあたりのオーバーヘッドは、ハッシュデータベースで16バイトほど、B+木データベースで5バイトほどです。さらにTokyo Cabinetで扱えるデータの規模は莫大です。最大8EB(9.22e18バイト)までのデータベースファイルを扱うことができます。

効率的なハッシュデータベースの実装

Tokyo Cabinetはレコードの探索にハッシュアルゴリズムを用います。バケット配列に十分な要素数があれば、レコードの探索にかかる時間計算量は O(1) です。すなわち、レコードの探索に必要な時間はデータベースの規模に関わらず一定です。追加や削除に関しても同様です。ハッシュ値の衝突はセパレートチェーン法で管理します。チェーンのデータ構造は二分探索木です。したがって、バケット配列の要素数が著しく少ない場合でも、探索等の時間計算量は O(log n) に抑えられます。

Tokyo Cabinetはバケット配列を全てRAM上に保持することによって、処理の高速化を図ります。バケット配列がRAM上にあれば、ほぼ1パスのファイル操作でレコードに該当するファイル上の領域を参照することができます。ファイルに記録されたバケット配列は `read' コールでRAM上に読み込むのではなく、`mmap' コールでRAMに直接マッピングされます。したがって、データベースに接続する際の準備時間が極めて短く、また、複数のプロセスでメモリマップを共有することができます。

バケット配列の要素数が格納するレコード数の半分ほどであれば、データの性質によって多少前後しますが、ハッシュ値の衝突率は56.7%ほどです(等倍だと36.8%、2倍だと21.3%、4倍だと11.5%、8倍だと6.0%ほど)。そのような場合、平均2パス以下のファイル操作でレコードを探索することができます。これを性能指標とするならば、例えば100万個のレコードを格納するためには50万要素のバケット配列が求められます。バケット配列の各要素は4バイトです。すなわち、2MバイトのRAMが利用できれば100万レコードのデータベースが構築できます。

伝統的なDBMにはレコードの追加操作に関して「挿入」モードと「置換」モードがあります。前者では、キーが既存のレコードと重複する際に既存の値を残します。後者では、キーが既存のレコードと重複した際に新しい値に置き換えます。Tokyo Cabinetはその2つに加えて「連結」モードがあります。既存の値の末尾に指定された値を連結して格納する操作です。レコードの値を配列として扱う場合、要素を追加するには連結モードが役に立ちます。また、DBMではレコードの値を取り出す際にはその全ての領域を処理対象にするしか方法がありませんが、Tokyo Cabinetでは値の領域の一部のみを選択して取り出すことができます。レコードの値を配列として扱う場合にはこの機能も役に立ちます。

一般的に、データベースの更新処理を続けるとファイル内の利用可能領域の断片化が起き、ファイルのサイズが肥大化してしまいます。Tokyo Cabinetは隣接する不要領域を連結して再利用し、またデータベースの最適化機能を備えることによってこの問題に対処します。既存のレコードの値をより大きなサイズの値に上書きする場合、そのレコードの領域をファイル中の別の位置に移動させる必要があります。この処理の時間計算量はレコードのサイズに依存するので、値を拡張していく場合には効率が悪くなります。しかし、Tokyo Cabinetはアラインメントによってこの問題に対処します。増分がパディングに収まれば領域を移動させる必要はありません。

便利なB+木データベースの実装

B+木データベースはハッシュデータベースより遅いのですが、ユーザが定義した順序に基づいて各レコードを参照できることが特長です。B+木は複数のレコードを整列させた状態で論理的なページにまとめて管理します。各ページに対してはB木すなわち多進平衡木によって階層化された疎インデックスが維持されます。したがって、各レコードの探索等にかかる時間計算量は O(log n) です。各レコードを順番に参照するためにカーソルが提供されます。カーソルの場所はキーを指定して飛ばすことができ、また現在の場所から次のレコードに進めたり前のレコードに戻したりすることができます。各ページは双方向リンクリストで編成されるので、カーソルを前後に移動させる操作の時間計算量は O(1) です。

B+木データベースは上述のハッシュデータベースを基盤として実装されます。B+木の各ページはハッシュデータベースのレコードとして記録されるので、ハッシュデータベースの記憶管理の効率性を継承しています。B+木では各レコードのヘッダが小さく、アラインメントはページの単位でとられるので、ほとんどの場合、ハッシュデータベースに較べてデータベースファイルのサイズが半減します。B+木を更新する際には多くのページを操作する必要がありますが、Tokyo Cabinetはページをキャッシュすることによってファイル操作を減らして処理を効率化します。ほとんどの場合、疎インデックス全体がメモリ上にキャッシュされるので、各レコードを参照するのに必要なファイル操作は平均1パス以下です。

B+木データベースはトランザクション機構を提供します。トランザクションを開始してから終了するまでの一連の操作を一括してデータベースにコミットしたり、一連の更新操作を破棄してデータベースの状態をトランザクションの開始前の状態にロールバックしたりすることができます。トランザクションの分離レベルは2種類あります。データベースに対する全ての操作をトランザクション内で行うと直列化可能(serializable)トランザクションとなり、トランザクション外の操作を同時に行うと非コミット読み取り(read uncommitted)トランザクションとなります。

各ページを圧縮して保存する機能も提供されます。圧縮方式はZLIBのDeflateとブロックソーティングの2種類をサポートしています。同一ページ内の各レコードは似たようなパターンを持つため、Lempel-ZivやBWTなどのアルゴリズムを適用すると高い圧縮効率が期待できます。テキストデータを扱う場合、データベースのサイズが元の25%程度になります。データベースの規模が大きくディスクI/Oがボトルネックとなる場合は、圧縮機能を有効化すると処理速度が大幅に改善されます。

単純だが多様なインタフェース群

Tokyo Cabinetはオブジェクト指向に基づいた簡潔なAPIを提供します。データベースに対する全ての操作はデータベースオブジェクトにカプセル化され、開く(open)、閉じる(close)、挿入する(put)、削除する(out)、取得する(get)といった関数(メソッド)を呼ぶことでプログラミングを進めていけます。ハッシュデータベースとB+木データベースのAPIは互いに酷似しているので、アプリケーションを一方から他方に移植することも簡単です。

Tokyo Cabinetにはデータベースに接続するモードとして、「リーダ」と「ライタ」の二種類があります。リーダは読み込み専用で、ライタは読み書き両用です。データベースにはファイルロックによってプロセス間での排他制御が行われます。ライタが接続している間は、他のプロセスはリーダとしてもライタとしても接続できません。リーダが接続している間は、他のプロセスのリーダは接続できるが、ライタは接続できません。この機構によって、マルチタスク環境での同時接続に伴うデータの整合性が保証されます。

Tokyo CabinetのAPIはリエントラントであり、マルチスレッド環境で安全に利用することができます。別個のデータベースオブジェクトに対しては全ての操作を完全に並列に行うことができます。同一のデータベースオブジェクトに対しては、リードライトロックで排他制御を行います。すなわち、読み込みを行うスレッド同士は並列に実行でき、書き込みを行うスレッドは他の読み込みや書き込みをブロックします。

メモリ上でレコードを簡単に扱うために、ユーティリティAPIが提供されます。リストやマップといった基本的なデータ構造をはじめ、メモリプールや文字列処理や符号処理など、プログラミングで良く使う機能を詰め込んでいます。

Tokyo CabinetはC言語の他にも、Java、PerlおよびRubyのAPIを提供します。C言語のAPIには、ユーティリティAPI、ハッシュデータベースAPI、B+木データベースAPIの三種類があります。各APIに対応したコマンドラインインタフェースも用意されています。それらはプロトタイピングやテストやデバッグなどで活躍するでしょう。Java用APIはJava Native Interfaceを用いてハッシュデータベースAPIとB+木データベースAPIを呼び出すものです。Perl用APIはXS言語を用いてハッシュデータベースAPIとB+木データベースAPIを呼び出すものです。Ruby用APIはRubyのモジュールとしてハッシュデータベースAPIとB+木データベースAPIを呼び出すものです。その他の言語のインターフェイスも第三者によって提供されるでしょう。


インストール

Tokyo Cabinetのソースパッケージからのインストール方法を説明します。バイナリパッケージのインストール方法についてはそれぞれのパッケージの説明書をご覧ください。

前提

Tokyo Cabinetの現在バージョンは、UNIX系のOSで利用することができます。少なくとも、以下の環境では動作するはずです。

ソースパッケージを用いてTokyo Cabinetをインストールするには、gccのバージョン3.1以降とmakeが必要です。それらはLinuxやFreeBSDには標準的にインストールされています。

Tokyo Cabinetは、以下のライブラリを利用しています。予めインストールしておいてください。

ビルドとインストール

Tokyo Cabinetの配布用アーカイブファイルを展開したら、生成されたディレクトリに入ってインストール作業を行います。

configureスクリプトを実行して、ビルド環境を設定します。

./configure

プログラムをビルドします。

make

プログラムの自己診断テストを行います。

make check

プログラムをインストールします。作業はrootユーザで行います。

make install

結果

一連の作業が終ると、以下のファイルがインストールされます。

/usr/local/include/tcutil.h
/usr/local/include/tchdb.h
/usr/local/include/tcbdb.h
/usr/local/lib/libtokyocabinet.a
/usr/local/lib/libtokyocabinet.so.1.8.0
/usr/local/lib/libtokyocabinet.so.1
/usr/local/lib/libtokyocabinet.so
/usr/local/bin/tcucodec
/usr/local/bin/tcutest
/usr/local/bin/tchmgr
/usr/local/bin/tchtest
/usr/local/bin/tchmttest
/usr/local/bin/tcbmgr
/usr/local/bin/tcbtest
/usr/local/bin/tcbmttest
/usr/local/share/tokyocabinet/...
/usr/local/man/man1/...
/usr/local/man/man3/...

configureのオプション

「./configure」を実行する際には、以下のオプションを指定することができます。

「--prefix」などのオプションも一般的なUNIXソフトウェアと同様に利用可能です。「/usr/local」以下ではなく「/usr」以下にインストールしたい場合は「--prefix=/usr」を指定してください。なお、ライブラリ検索パスに「/usr/local/lib」が入っていない環境では、Tokyo Cabinetのアプリケーションを実行する際に環境変数 `LD_LIBRARY_PATH' の値に「/usr/local/lib」を含めておくようにしてください。


ユーティリティAPI

とりあえずヘッダ(tcutil.h)読んでね。


ハッシュデータベースAPI

とりあえずヘッダ(tchdb.h)読んでね。


B+木データベースAPI

とりあえずヘッダ(tcbdb.h)読んでね。


データベースフォーマット

この節ではデータベースファイルのフォーマットに関する仕様を示します。

ハッシュデータベースのファイルフォーマット

ハッシュデータベースが管理するデータベースファイルの内容は、ヘッダ部、バケット部、フリーブロックプール部、レコード部の4つに大別されます。ファイルに記録される数値は固定長数値もしくは可変長数値として記録されます。前者は数値を特定の領域にリトルエンディアンで直列化したものです。後者は数値を可変長の領域に128進法のデルタ符号で直列化したものです。

ヘッダ部はファイルの先頭から256バイトの固定長でとられ、以下の情報が記録されます。

名前 オフセット データ長 機能
マジックナンバ 0 32 データベースファイルであることの判別。「ToKyO CaBiNeT」で始まる
データベースタイプ 32 1 ハッシュ表(0x10)かB+木(0x20)
追加フラグ 33 1 開きっぱなし(1<<0)、致命的エラー(1<<1)の論理和
アラインメント力 34 1 アラインメントに対する2の冪乗
フリーブロックプール力 35 1 フリーブロックプールの要素数に対する2の冪乗
オプション 36 1 ラージモード(1<<0)、Deflate圧縮モード(1<<1)、TCBS圧縮モード(1<<2)の論理和
バケット数 40 8 バケット配列の要素数
レコード数 48 8 格納しているレコードの数
ファイルサイズ 56 8 データベースファイルのサイズ
先頭レコード 64 8 最初のレコードのオフセット
不透明領域 128 128 ユーザが自由に使える領域

バケット部はヘッダ部の直後にバケット配列の要素数に応じた大きさでとられ、ハッシュチェーンの先頭要素のオフセットが各要素に記録されます。各要素は固定長数値で、そのサイズはノーマルモードでは4バイト、ラージモードでは8バイトです。また、オフセットはアラインメントで割った商として記録されます。

フリーブロックプール部はバケット部の直後にフリーブロックプールの要素数に応じた大きさでとられ、未使用領域のオフセットと長さが各要素に記録されます。オフセットはアラインメントで割った商に変換した上で、直前の要素の値との差分として記録されます。オフセットとサイズは可変長数値として扱われます。

レコード部はバケット部の直後からファイルの末尾までを占め、各レコードの以下の情報を持つ要素が記録されます。各レコードの領域は常にアラインメントされた位置から始まります。

名前 オフセット データ長 機能
マジックナンバ 0 1 データの識別と整合性確認に用いる。0xA1固定
ハッシュ値 1 1 チェーンの進路決定に用いるハッシュ値
左チェーン 2 4 左チェーン接続先のオフセットのアラインメント商
右チェーン 6 4 右チェーン接続先のオフセットのアラインメント商
パディングサイズ 10 2 パディングのサイズ
キーサイズ 12 可変 キーのサイズ
値サイズ 可変 可変 値のサイズ
キー 可変 可変 キーのデータ
可変 可変 値のデータ
パディング 可変 可変 パディング

ただし、フリーブロックとなった領域には、各レコードの以下の情報を持つ要素が記録される。

名前 オフセット データ長 機能
マジックナンバ 0 1 データの識別と整合性確認に用いる。0xA2固定
ブロックサイズ 1 4 ブロックのサイズ

B+木データベースのファイルフォーマット

B+木データベースが扱う全てのデータはハッシュデータベースに記録されます。記録されるデータは、メタデータと論理ページに分類されます。論理ページはリーフノードと非リーフノードに分類されます。固定長数値と可変長数値の形式はハッシューデータベースと同じです。

メタデータはハッシュデータベースのヘッダにおける不透明領域にとられ、以下の情報が記録されます。

名前 オフセット データ長 機能
リーフ内レコード数 0 4 個々のリーフノードに入れるレコードの最大数
非リーフ内インデックス数 4 4 個々の非リーフノードに入れるインデックスの最大数
ルートノードID 8 8 B+木のルートノードのページID
先頭リーフID 16 8 先頭のリーフノードのID
末尾リーフID 24 8 末尾のリーフノードのID
リーフ数 32 8 リーフノードの数
非リーフ数 40 8 非リーフノードの数
レコード数 48 8 格納しているレコードの数

リーフノードはレコードのリストを保持し、非リーフノードはページを参照する疎インデックスを保持します。レコードはユーザデータの論理的な単位です。キーが重複する論理レコードは物理的には単一のレコードにまとめられます。物理レコードは以下の形式で直列化されます。

名前 オフセット データ長 機能
キーサイズ 0 可変 キーのサイズ
値サイズ 可変 可変 最初の値のサイズ
重複数 可変 可変 キーが重複した値の数
キー 可変 可変 キーのデータ
可変 可変 最初の値のデータ
重複レコード 可変 可変 値のサイズと値のデータのリスト

リーフノードはレコードの集合を格納するための物理的な単位です。リーフノードは1からインクリメントして振られるID番号で識別されます。リーフノードはID番号を16進数の文字列として表現したデータをキーとし、以下の値を持つレコードとしてハッシュデータベースに格納されます。レコードは常にキーの昇順に整列した状態で保持されます。

名前 オフセット データ長 機能
前リーフ 0 可変 直前のリーフノードのID
後リーフ 可変 可変 直後のリーフノードのID
レコードリスト 可変 可変 ページのレコードを直列化して連結したデータ

インデックスはページを探索するためのポインタの論理的な単位です。インデックスは以下の形式で直列化されます。

名前 オフセット データ長 機能
ページID 0 可変 参照先のページのID
キーサイズ 可変 可変 キーのサイズ
キー 可変 可変 キーのデータ

非リーフノードはインデックスの集合を格納するための物理的な単位です。非リーフノードは281474976710657からインクリメントして振られるID番号で識別されます。非リーフノードはID番号から281474976710657を引いた値を16進数の文字列とにした上で「#」を接頭させた文字列をキーとし、以下の値を持つレコードとしてハッシュデータベースに格納されます。インデックスは常に昇順に整列した状態で保持されます。

名前 オフセット データ長 機能
継承ID 0 可変 最初の子ノードのID
インデックスリスト 可変 可変 ページ内のインデックスを直列化して連結したデータ

注記

データベースファイルはスパースではないので、通常のファイルと同様に複製等の操作を行うことができます。ハッシュデータベースのファイルもB+木データベースのファイルも実行環境のバイトオーダに依存しない形式なので、バイトオーダの異なる環境にデータベースファイルを移設してもそのままで利用できます。

なるべくなら、ハッシュデータベースのファイルをネットワークで配布する際には、MIMEタイプを `application/x-tokyocabinet-hash' にしてください。ファイル名の接尾辞は `.tch' にしてください。B+木データベースのファイルをネットワークで配布する際には、MIMEタイプを `application/x-tokyocabinet-btree' にしてください。ファイル名の接尾辞は `.tcb' にしてください。


よく聞かれる質問

Q. : Tokyo CabinetはSQLをサポートしますか?
A. : Tokyo CabinetはSQLをサポートしません。QDBMはRDBMS(関係データベース管理システム)ではありません。組み込みのRDBMSを求めるなら、SQLiteなどを利用するとよいでしょう。
Q. : Berkeley DBとどう違うのですか?
A. : 時間効率と空間効率の双方でTokyo Cabinetが優っています。トランザクションの機能と耐障害性についてはBerkeley DBの方が優っています。
Q. : どのAPIを使えばよいでしょうか?
A. : レコードの検索が完全一致だけで済むのなら、ハッシュデータベースAPIを試してください。レコードを順序に基づいて参照したいなら、B+木データベースAPIを試してください。メモリ上のマップやリストが使いたいならば、ユーティリティAPIを試してください。
Q. : アプリケーションの良いサンプルコードはありますか?
A. : 各APIのコマンドのソースコードを参考にしてください。`tchmgr.c' と `tcbmgr.c' が最も簡潔でしょう。
Q. : データベースが壊れたのですが、どうしてでしょうか?。
A. : 大抵の場合、あなたのアプリケーションがきちんとデータベースを閉じていないのが原因です。デーモンプロセスであろうが、CGIスクリプトであろうが、アプリケーションが終了する際には必ずデータベースを閉じなければなりません。なお、CGIのプロセスはSIGPIPEやSIGTERMによって殺されることがあることにも留意しましょう。
Q. : データベースを壊れにくくするにはどうすればよいですか?
A. : できるならハッシュデータベースを使ってください。ハッシュデータベースはB+木データベースに比べてクラッシュに強いです。それが無理なら、B+木データベースAPIのトランザクション機能を使ってください。これもアプリケーションのクラッシュに対してある程度の耐性があります。
Q. : 壊れたデータベース修復するにはどうすればよいですか?
A. : データベースファイルをロックなしオプション(HDBONOLCKかBDBONOLCK)をつけて開いて、最適化機能(tchdboptimizeかtcbdboptimize)を実行してください。コマンドラインで修復処理を行いたい場合、「tchmgr optimize -nl casket」もしくは「tcbmgr optimize -nl casket」を実行してください。
Q. : 性能を引き出すシステムの設定はどんなものがありますか?
A. : できれば、データベースのサイズと同等以上のRAMをマシンに搭載してください。そして、I/Oバッファのサイズを大きくし、ダーティバッファをフラッシュする頻度が少なくするように設定してください。ファイルシステムの選択も重要です。Linux上では、通常はEXT2が最高速ですが、EXT3の `writeback' モードの方が速いこともあります。ReiserFSもかなり高速です。EXT3のその他のモードはかなり遅いです。他のファイルシステムに関しては各自で実験してみてください。
Q. : QDBMはもうメンテナンスしないのですか?
A. : メンテナンスは続けます。積極的な機能追加の予定はありませんが、もしバグが見つかれば対処します。
Q. : Windowsで利用できませんか?
A. : 残念ながらできません。今のところ対応予定もありません。
Q. : 「Tokyo Cabinet」の名前の由来はなんですか?
A. : 作者が住んでいる街なので「tokyo」で、モノをしまうから「cabinet」です。
Q. : あなたは千葉県とどういう関係なのですか?
A. : 特に関係はありません。出身地は埼玉県です。落花生は好きです。

ライセンス

Tokyo Cabinetはフリーソフトウェアです。あなたは、Free Software Foundationが公表したGNU Lesser General Public Licenseのバージョン2.1あるいはそれ以降の各バージョンの中からいずれかを選択し、そのバージョンが定める条項に従ってTokyo Cabinetを再頒布または変更することができます。

Tokyo Cabinetは有用であると思われますが、頒布にあたっては、市場性及び特定目的適合性についての暗黙の保証を含めて、いかなる保証も行ないません。詳細についてはGNU Lesser General Public Licenseを読んでください。

あなたは、Tokyo Cabinetと一緒にGNU Lesser General Public Licenseの写しを受け取っているはずです(`COPYING' ファイルを参照してください)。そうでない場合は、Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA へ連絡してください。

Tokyo Cabinetは平林幹雄が作成しました。作者と連絡をとるには、`mikio@users.sourceforge.net' 宛に電子メールを送ってください。