MySQL 5.1 のプラガブルストレージエンジンでオリジナルのストレージエンジンを作ろう

こんにちは。ガルーン開発グループの米川です。

皆さんは普段、MySQL でどのストレージエンジンを使っていますか?

ご存知の通り MySQL は、多様なストレージエンジンを用途に応じて使い分けることができます。 トランザクションが使いたければ InnoDB、Fulltext-Search を使うためには MyISAM など、特徴的なストレージエンジンが標準で搭載されています。

今回は、MySQL 5.1 で搭載されるプラガブルストレージエンジンという機構を使って、オリジナルのストレージエンジンを書く方法についてご紹介します。

プラガブルストレージエンジンとは

プラガブルストレージエンジンは、MySQL 上に任意のストレージエンジンを読み込むことができる機構です。この機構を使えば SQL のパースや最適化などを MySQL に任せて、データの取得処理に任意のストレージエンジンを使うことができます。

プラガブルストレージエンジンアーキテクチャにおいて、ストレージエンジンの実体は handler というクラスを継承した子クラスです。( 実装は MySQL と同様に C++ です。)
この handler クラスには、MySQL が SQL を実行する際に使用するメソッドが定義されています。これらのメソッドを実装して、データへのアクセスを実現します。

つまりオリジナルのストレージエンジンを書くというのは、handler クラスを継承した子クラスを定義して、その中で決められたメソッドを実装することを意味します。

Skeleton ストレージエンジン

ではストレージエンジンを書こうと思ったら一から handler クラスの子クラスを書かなければならないのでしょうか?
実はそんなことはありません。

ストレージエンジンを作る雛形として、Skeleton というストレージエンジンが用意されています。
Skeleton には必要最低限のメソッドのみが定義されており、開発者はこの Skeleton のソースコードを手に入れ、必要な機能を追加で実装していくことになります。
なお、Skeleton は MySQL AB の Director of Architecture の Brian Aker 氏が作成しています。

まずプラガブルストレージエンジンのイメージを掴むために、Skeleton ストレージエンジンを実際に使用するところまでの手順を追ってみたいと思います。

1. MySQL 5.1 をインストール
この記事の執筆時点で最新版は 5.1.19-beta です。
http://dev.mysql.com/downloads/mysql/5.1.html
2. Skeleton ストレージエンジンのソースコードのダウンロード
ソースコードをダウンロードして解凍します。
http://hg.tangent.org/skeleton-mysql-engine?ca=tip;type=gz
3. コンパイル

解凍したソースコードに含まれている config / bootstrap スクリプトを実行して configure を生成します。 あとは configure → make → make install の手順を踏めばインストールされます。

sh config/bootstrap
./configure --with-mysql=[ ( コンパイル済みの ) MySQL 5.1 のソースディレクトリ ] --libdir=[ インストール先の MySQL に設定された plugin_dir ]
make
make install

--libdir に設定する plugin_dir は、インストール先の MySQL 上で SHOW VARIABLES コマンドを打つことで確認できます。

4. プラグインを読み込む

MySQL クライアント上でプラグインを読み込むことで Skeleton ストレージエンジンが使用可能になります。

図:MySQL クライアント上でプラグインを読み込む
MySQL クライアント上でプラグインを読み込む
( 画像クリックで拡大表示 )

これで通常通り、CREATE TABLE 時に ENGINE=SKELETON と指定すれば、 Skeleton ストレージエンジンを使用するテーブルを作成することができます。

図:CREATE TABLE 時に ENGINE=SKELETON と指定
CREATE TABLE 時に ENGINE=SKELETON と指定
( 画像クリックで拡大表示 )

"Engine" の欄が SKELETON になっていることが確認できます。 次に "skel" テーブルに対して SQL を発行してみます。

mysql> SELECT * FROM skel;
Empty set (0.00 sec)

mysql> INSERT INTO skel VALUES(1, 'test');
ERROR 1031 (HY000): Table storage engine for 'skel' doesn't have this option

SELECT 文の結果は空で、INSERT 文はサポートしていないという結果が返ってきます。 これはまだ実装されていないので当然の結果と言えます。

このようにプラグインとしてインストールできる最低限の状態が用意されているため、 あとはデータアクセスに関する処理を書くだけでいいのです。
ちょっと簡単に思えてきませんか?

データを読み込んでみよう

では実際に Skeleton ストレージエンジンのソースコードを書き換えて、 SELECT 文に対してデータを読み込んで、クエリの結果として返す方法を解説します。

データを読み込む場合は、以下の 3 つのメソッドを実装します。

  • rnd_init
  • rnd_next
  • rnd_end

この中で、rnd_init はデータ読み込み時の初期化処理を行うメソッドで、 rnd_end は後処理を行うメソッドです。
例えばファイルストリームを開く処理は rnd_init に書いて、 閉じる処理は rnd_end に書く、というように使います。

このメソッドは、何も実装しなくてもデータを読み込むことができます。 実際に Skeleton エンジンには rnd_end の定義すらありません。

この中で一番重要なのが、rnd_next メソッドです。 データを 1 行分読み込んで、検索結果を生成する処理を記述するのがこのメソッドです。

int ha_skeleton::rnd_next(byte *buf)
{
  DBUG_ENTER("ha_skeleton::rnd_next");

  // データを読み込んで検索結果を生成する処理

  DBUG_RETURN(HA_ERR_END_OF_FILE);
}

しかし、データを取得するだけでは、MySQL はそれを検索結果として返してくれないので、 取得したデータを MySQL に検索結果として渡さなければなりません。それには Field クラスを使います。

Field **field=table->field;

rnd_next が呼ばれた時点で、table->field に検索結果の Field クラス ( 1 行分 ) が格納されています。
検索結果を生成するには、store メソッドを使用します。

ulonglong  number = 1
const char *value = "skeleton";

(*field)->store(number);
(*field)->set_notnull();

field++;

(*field)->store(value, strlen(value), system_charset_info);
(*field)->set_notnull();

DBUG_RETURN(0)

store メソッドの引数は型によって違います。 次のカラムの処理に移動するときは、field++ のようにします。

set_notnull というメソッドは、 NULL でない値を格納することを保証するメソッドです。
例えば number が本当に格納されているかチェックして、それによって store メソッドが呼ばれなかった場合でも、NULL でない値を格納して次のカラムの処理に進むことができます。
1 行分読んだ時点で、DBUG_RETURN(0) とすることで、table->field に次の行の Field クラスが格納され、 次の行の処理へ進むことができます。

データを読み終わったら、HA_ERR_END_OF_FILE を返して読み込み終了を MySQL に伝えます。

DBUG_RETURN(HA_ERR_END_OF_FILE);

サードパーティ製のプラガブルストレージエンジンの事例

さまざまなサードパーティやコミュニティで、ストレージエンジンの開発がすでに進んでいます。 その中のいくつかを紹介します。

solidDB
米 Solid Information Technology 社が開発している高い可用性を持ったデータベースエンジンを、 MySQL のストレージエンジンとして利用できるようにしたもの
DB2 for i5/OS
IBM System i の i5/OS 用の DB2 を MySQL のストレージエンジンとして利用できるようにしたもの
AWSS3
Amazon S3 ( オンラインストレージサービス ) のデータを SQL で操作するストレージエンジン

そのほかにも、HTTP ストレージエンジンや memcached ストレージエンジンなど、特徴的なストレージエンジンが多数開発されています。

基本的に C++ にできる範囲なら、どんなストレージエンジンでも作ることができます。 ライブラリも使えるので、例えば curl を使って Web API にアクセスしたり、libxml で XML をパースしたりもできます。

私は記事執筆のために、サンプルとして Twitter の public_timeline にアクセスする Twitter ストレージエンジンを作りました。

twitter.jpg
Twitter の public_timeline にアクセスする Twitter ストレージエンジン
( 画像クリックで拡大表示 )

おわりに

オリジナルのストレージエンジンといっても、そんなに難しいわけではありません。 Skeleton エンジンを雛形にすれば、C++ が書ける開発者なら何でもできてしまいます。
ただし、実用に足るだけのものを作るにはそれなりの労力が必要になるでしょう。

MySQL の魅力の一つは多様なストレージエンジンを選択できることにあると思います。
プラガブルストレージエンジンによって、その選択肢の幅が広がるのは良いことなのではないかと思いました。