MySQLをファイルシステムとして使う

  MySQLfs を使うと、リレーショナル・データベースMySQLの中にファイルシステムを入れることができる。このファイルシステム中のファイルに含まれるバイト列はデータベースの各行に分割して格納されるため、大きなファイルがある場合もデータベースが巨大なBLOBフィールドをサポートしている必要はない。ファイルシステムをMySQLデータベースに入れておくと、データベースが備えているバックアップやクラスタリング、レプリケーションの機能を利用してMySQLfsファイルシステムを保護することができる。

 Fedora、openSUSE、UbuntuにはMySQLfsのバイナリー・パッケージは含まれていない。したがって、MySQLfsを使うにはソースからビルドする必要がある。また、あらかじめmysql-develとfuse-develをインストールしておかなければならない(FUSEにより、一般のプログラムがLinuxカーネルを介してファイルシステムを開示できるようになる。つまり、任意のアプリケーションからFUSEファイルシステムが直接使えるようになる)。develパッケージに不足がある場合、configureでエラーが発生するが、これが若干の混乱を引き起こすことがあるので注意されたい。たとえば、私がFedora 8 64ビット・マシンで試行したときはmysqlclient_rのリンクに失敗した。これはFUSE開発パッケージがなかったのが原因だ。つまり、このエラーによりリンカーのフラグに-Lが生成されたために、mysqlclientテスト・プログラムのリンクに失敗したのだ。また、ビルドではデフォルトで-Wall -Werrorが含まれているため、警告メッセージが発生した時点でビルドは直ちに中止される。これを回避するため、-Werrorを外してコンパイルし、必要な開発パッケージがすべてインストールされていることを確認しておく。MySQLfsがビルドできたら、make installコマンドでMySQLfsを/usr/local/binにインストールする。

 MySQLfsを使用できるようMySQLデータベースを設定する手順を以下に示す。ここで、make installでは、schema.sqlファイルがファイルシステムのどこにもインストールされないことに注意。あとでほかのMySQLfsデータベースを設定する場合はこのスキーマをどこかにコピーしておかなければならない。

# mysql -p
mysql> CREATE DATABASE mysqlfs;
mysql> GRANT SELECT, INSERT, UPDATE, DELETE ON mysqlfs.* TO mysqlfs@"%" IDENTIFIED BY 'foobar';
mysql> FLUSH PRIVILEGES;
mysql> Bye
# mysql -uroot -p mysqlfs < /tmp/mysqlfs-0.4.0-rc1/schema.sql

 データベース・スキーマは、tree、inodes、data_blocksという3つのテーブルから成る。treeはinodeの子から親への対応だ。テーブルの一部を下に例示する。

 以下の例は、guten/alice13a.txtというファイルをインポートしたときのものだ。treeテーブルはファイルシステム中のファイルやディレクトリーの階層を表現しており、この場合ファイルalice13a.txtはディレクトリーgutenの中にあり、ディレクトリーgutenはMySQLfsファイルシステムのルートの下にあることを示している。inodesテーブルにはstat(2)で得られるすべての情報が、data_blocksテーブルにはファイルの内容がバイト列として格納されている。

mysql> select * from tree;
+-------+--------+----------------------+
| inode | parent | name                 |
+-------+--------+----------------------+
|     1 |   NULL | /                    |
|     2 |      1 | guten                |
|     3 |      2 | alice13a.txt         |
...
mysql> select * from inodes;
+-------+-------+---------+-------+-----+-----+------------+------------+------------+----------+
| inode | inuse | deleted | mode  | uid | gid | atime      | mtime      | ctime      | size     |
+-------+-------+---------+-------+-----+-----+------------+------------+------------+----------+
|     1 |     0 |       0 | 16877 |   0 |   0 | 1201155861 | 1201155861 | 1201155861 |        0 |
|     2 |     0 |       0 | 16888 | 500 | 500 | 1200108244 | 1200108244 | 1201156234 |        0 |
|     3 |     0 |       0 | 33184 | 500 | 500 | 1200108239 | 1200108239 | 1201156234 |   153477 |
...
mysql> describe data_blocks;
+-------+------------------+------+-----+---------+-------+
| Field | Type             | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| inode | bigint(20)       | NO   | PRI |         |       |
| seq   | int(10) unsigned | NO   | PRI |         |       |
| data  | blob             | YES  |     | NULL    |       |
+-------+------------------+------+-----+---------+-------+

 FUSEでは非正規な動作を行うファイルシステムでもマウントできるため、カスタムFUSEファイルシステムのマウントは、通常、fuseグループに明示的に追加されたユーザーに制限される。次に示す手順は、FUSEファイルシステムをマウントしようとして拒否され、対処しているところだ。この場合、自身をfuseグループに追加してからログインしなければならない。MySQLfsファイルシステムとデータベースは当初は空だが、dfコマンドを実行すると、カーネルがこのファイルシステムのマウントを認識していることがわかる。最後にfusermountコマンドで、ユーザーが作成したFUSEファイルシステムをアンマウントしている。

$ mkdir ~/mysqlfs
$ mysqlfs -ohost=localhost  -odatabase=mysqlfs ~/mysqlfs
* Opening logfile 'mysqlfs.log':  OK
fuse: failed to open /dev/fuse: Permission denied
$ su -l
root# usermod -a -G fuse ben
root# exit
$ exit
...
$ id
uid=500(ben) gid=500(ben) groups=492(fuse), ...
$ mysqlfs -ohost=localhost  -odatabase=mysqlfs ~/mysqlfs
* Opening logfile 'mysqlfs.log':  OK
$ ls -ld mysqlfs
drwxr-xr-x 1 root root 0 2008-01-24 16:24 mysqlfs
$ df -h mysqlfs .
Filesystem            Size  Used Avail Use% Mounted on
mysqlfs                  0     0     0   -  /home/ben/mysqlfs
/dev/sdc3              16G  4.4G   11G  31% /home
$ ls -l mysqlfs
total 0
$ fusermount -u mysqlfs

 以上でMySQLをファイルシステムとしてマウントする環境が整った。次に、ファイルをMySQLデータベースにコピーし、ファイルシステムを操作してみよう。下に示した例では、まず、Project Gutenbergのテキスト・ファイルをMySQLfsにコピーし、読み出してMD5チェックサムが一致することを確認。次いで、44MBもあるLinuxソースのtarballをMySQLfsにコピーしてパフォーマンスを調べている。

 ディスク・キャッシュが空の状態のとき、/tmpにあるtarballを同じ/tmp上でバックアップするには約2秒かかっている。MySQLfsにコピーした場合は約20秒だ。直後に同じテストを行うと、tarballがディスク・キャッシュに残っているため、/tmp上でのバックアップは約0.3秒に短縮したが、MySQLfsへのコピーは20秒で変わらない。これは、MySQLfsにとって書き込みのパフォーマンスが大きな障害になりうることを示している。一方、読み出しのテスト結果を見ると、MySQLでも一部のデータをキャッシュしているようだ。テストを繰り返すと処理時間が短くなる。このことから、読み出しの多いファイルシステムであれば、MySQLfsは検討に値すると思われる。MySQLのキャッシュ機能を活用できるからだ。

~]$ cp -av /.../guten ./mysqlfs/
`/.../guten' -> `./mysqlfs/guten'
`/.../guten/alice13a.txt' -> `./mysqlfs/guten/alice13a.txt'
`/.../guten/boysw10.txt' -> `./mysqlfs/guten/boysw10.txt'
`/.../guten/dmoro11.txt' -> `./mysqlfs/guten/dmoro11.txt'
~]$ cd ~/mysqlfs/guten
guten]$ ls -l
-rw-r----- 1 ben ben 153477 2008-01-12 13:23 alice13a.txt
-rw-rw---- 1 ben ben  48923 2008-01-12 13:23 boysw10.txt
-rw-rw---- 1 ben ben 259214 2008-01-12 13:23 dmoro11.txt
guten]$ md5sum *
135e06ad31b169065bccbf03ec7236f2  alice13a.txt
7dd30f1b37e32cdb5d21fe992bbf248d  boysw10.txt
87c05f11193c0e05b3d0dec0808a0450  dmoro11.txt
guten]$ md5sum /.../guten/*
135e06ad31b169065bccbf03ec7236f2  /.../guten/alice13a.txt
7dd30f1b37e32cdb5d21fe992bbf248d  /.../guten/boysw10.txt
87c05f11193c0e05b3d0dec0808a0450  /.../guten/dmoro11.txt
guten]$ cd ..
mysqlfs]$ time cp /tmp/linux-2.6.23.tar.bz2 .
real    0m16.278s
user    0m0.006s
sys     0m0.531s
mysqlfs]$ time cat linux-2.6.23.tar.bz2  >/dev/null
real    0m0.502s
user    0m0.004s
sys     0m0.035s
mysqlfs]$ time dd if=linux-2.6.23.tar.bz2 of=/tmp/junk bs=1024 count=1024
1048576 bytes (1.0 MB) copied, 0.0200973 s, 52.2 MB/s

real    0m0.058s
user    0m0.003s
sys     0m0.013s
mysqlfs]$ time dd if=linux-2.6.23.tar.bz2 of=/tmp/junk bs=1024 count=1024 skip=9000
1048576 bytes (1.0 MB) copied, 0.0214207 s, 49.0 MB/s

real    0m0.031s
user    0m0.001s
sys     0m0.011s

 FUSEファイルシステムにはきわめて不利だが、Bonnie++ファイルシステム・ベンチマーク・スイートを実行してみた。仮想マシン上で実行したため、仮想化による線形的な遅延が発生するだろうが、MySQLデータベースも/tmpも同じ仮想ディスク・デバイス上にあるためパフォーマンスの比較は可能だ。

 結果を下に示す。これからわかるように、Bonnie++(バージョン1.03a-7)の実行では、MySQLfsの処理速度は/tmp/bonnie(ext3)の10分の1程度。このパフォーマンスでは、maildirを直接MySQLfsに格納しようとは誰も思うまい。ただし、Bonnie++はネイティブ・カーネル・ファイルシステム向けに設計されておりFUSE向けではないから、この数字はおよその傾向を表すものと見るべきである。

$ /usr/sbin/bonnie++ -d /tmp/bonnie

Version  1.03       ------Sequential Output------ --Sequential Input- --Random-
                    -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
v8tsrv           2G 18155  31 16726   5 13338   6 26207  46 74527  24  9840 144
                    ------Sequential Create------ --------Random Create--------
                    -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                 16 24878  94 +++++ +++ +++++ +++ 29018  99 +++++ +++ +++++ +++

$ /usr/sbin/bonnie++ -d ~/mysqlfs/bonnie

Version  1.03       ------Sequential Output------ --Sequential Input- --Random-
                    -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
v8tsrv           2G  2615   5  1207   1  1323   1  2143   2  2363   0 138.1   0
                    ------Sequential Create------ --------Random Create--------
                    -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                 16   186   2   505   1   296   2   209   2   441   1   282   2

Ben Martin 10年以上にわたってファイルシステムを研究。博士課程を修了し、現在、libferris、ファイルシステム、検索ソリューションを中心にコンサルティングをしている。

Linux.com 原文