技術でつくるモバイルの未来

HOME > 技術情報 > 

MySQL5開拓団


※こちらのページは技術評論社刊「WEB+DB PRESS Vol.33」に掲載された内容になります。

開拓の前に

前回は導入ということで、MySQL 4.1、5.0の新機能や変更点などをざっと紹介しました。今回から本格的な開拓開始ということで、ストレージエンジンを取り上げたいと思います。

ストレージエンジンとはテーブルデータを管理・格納する実装のことで、トランザクションをサポートしているもの、していないもの、ディスク上ではなくメモリ上にデータを保存するものなど、MySQLには様々なストレージエンジンがあります。多くのRDBMSではひとつのデータ格納方式しか実装されていないのに対し、MySQLではこれらのストレージエンジンを用途にあわせて使い分けることができる点が特徴的なところです。

また、ストレージエンジンが異なっていてもその違いをあまり意識せずに使用することができます。具体的には、あるデータベースの中に異なるストレージエンジンのテーブルを混在させることもできますし、ストレージエンジンが異なっていてもSQLレベルではほぼ透過的にクエリを発行することができ、異なるストレージエンジンのテーブルどうしの結合もできます。ただし、そのストレージエンジン固有の機能(トランザクションなど)は意識する必要があります。

では早速…といきたいところなのですが、前回から新しいバージョンが出ていますので、まずは最新版の環境を整えましょう。

 

最新版に入れ替える

前回は環境準備としてバージョン5.0.19をインストールしましたが、執筆時点で5.0.21がリリースされていますので最新版に入れ替えます。

5.0.21では様々なバグ修正に加え脆弱性の修正も含まれていますので、バージョンアップすることをおすすめします。もちろん変更履歴は確認しましょう。

 

最新版のダウンロード

前回はダウンロードページから「Linux(non RPM package)」の「Linux(x86, glibc-2.2, "standard" is static, gcc)」のStandardをダウンロードしてインストールしましたが、今回紹介するストレージエンジンを使うために必要なので、StandardではなくMaxをインストールします。

さて、「Standard」「Max」という言葉が出てきました。この他に、Linuxのtgz形式のバイナリ配布にはいくつか種類がありますのでここで整理しておきます。

まず、Standard、Max、Debugについて、表1にこの違いを簡潔にまとめました。MySQL ABのダウンロードページを見ると、Linuxに限らずSolaris、Mac OS Xなどいくつかのプラットフォーム向けのバイナリ配布物にこの種別がありますが、特に理由がなければStandardをインストールすればよいでしょう。

表1:Standard、Max、Debugの違い
配布種別 内容
Standard 通常(Maxが必要でなければ)これを使用します。
Max Standardでは無効化されている追加機能(例えばいくつかのストレージエンジンなど)が有効になっていますので、これらの機能が必要な場合にMax版を使用します。
詳細は『The mysqld-max Extended MySQL Server』を参照してください。
Debug 通常は使用しません。デバッグ情報つきでコンパイルされているため、他に比べて性能が劣ります。

次にLinuxの非rpm版固有の種別についてです。x86版だけを見ると、表2のように3種類があります。

表2:Linux版バイナリの種類
Linux(non RPM package)
- Linux(x86, glibc-2.2, "standard" is static, gcc) Standardはスタティックリンクされているので、外部ライブラリに影響されずに動作し、スレッドの実装はLinuxThreadになります。
- Linux (x86) 主にglibc-2.3の環境向けです。Standardもダイナミックリンクされているため、使用されるスレッドの実装は環境によりLinuxThreadかNPTL(Native POSIX Thread Library)になります。
Linux (non RPM, Intel C/C++ compiled, glibc-2.3) downloads
- Linux (x86) 同じく主にglibc-2.3向けですが、gccではなくIntelのコンパイラ(icc)でコンパイルされているため、より高い性能が期待できます。

ライブラリの影響に悩まされたくないならば「Linux(x86, glibc-2.2, "standard" is static, gcc)」のStandardを使うのがよいと思いますが、LinuxThreadに比べNPTLはスレッド生成のコストが低いとされているので、性能を追求するならばダイナミックリンクされているMySQLのバイナリでNPTLを使うのもよいと思います。

NPTLを使うには、そのようにビルドされたglibcとkernel 2.6が必要です。お使いの環境がどのスレッド実装を使用しているかは、

$ getconf GNU_LIBPTHREAD_VERSION NPTL 0.60

もしくは、glibc-2.3.2より前のバージョンの場合は、

$ /lib/libc.so.6 | egrep -i '(threads|nptl)' linuxthreads-0.9 by Xavier Leroy0

として確認できます。

また、MySQLがどちらのスレッドで稼働しているか確認するには、

$ ps auxwww | grep mysql[d]

と実行して、表示されたのがmysqld_safeとmysqldの2つだけならばNPTL、mysqld_safeとたくさんのmysqldの場合はLinuxThreadとなります。NPTLの場合、次のようにすればPIDが同じでLWPが異なるmysqldスレッドを確認することができます。

$ ps -eLfwww | grep -e PI[D] -e mysql[d]

話を元に戻します。今回はストレージエンジンの紹介をする都合で、StandardではなくMaxを使用します。また、実行環境は前回に引き続きDebian GNU/Linux 3.1を使いますので、「Linux(non RPM package)」の「Linux (x86)」(mysql-max-5.0.21-linux-i686-glibc23.tar.gz)を使用したいと思います。

 

MySQLのインストール

さて、環境に合ったファイルをダウンロードしたら、前回お話しした通り所定のディレクトリに展開します。(図1)

<図1:最新版の展開>
# cd /usr/local/app # tar zxf ~/mysql-max-5.0.21-linux-i686-glibc23.tar.gz

次に稼働中のmysqldを停止して、シンボリックリンクを張り替えます。これだけでバージョンの切り替えが完了します。切り替え後、mysqldを起動して確認してみましょう。(図2)

<図2:最新版への切り替え>
# /etc/init.d/mysql stop ←mysqldを停止します。 Shutting down MySQL.. SUCCESS! # cd /usr/local/app # ls -F ←5.0.19と5.0.21のディレクトリとシンボリックリンク(mysql)があり、 mysql@ mysql-standard-5.0.19-linux-i686/ mysql-max-5.0.21-linux-i686-glibc23/ # readlink mysql mysql-standard-5.0.19-linux-i686 ←mysqlが5.0.19を指していることを確認します。 # ln -snf mysql-max-5.0.21-linux-i686-glibc23 mysql ←続いて5.0.21への切り替えを行います。 # readlink mysql mysql-max-5.0.21-linux-i686-glibc23 $ mysql --version ←最新版に切り替わっているか確認します。 mysql Ver 14.12 Distrib 5.0.21, for pc-linux-gnu (i686) using readline 5.0 # /etc/init.d/mysql start Starting MySQL SUCCESS! $ echo 'select version()' | mysql version() 5.0.21-max-log

うまくバージョンアップできたでしょうか? 前回、mysqlなどのコマンドは/usr/local/bin/mysqlにシンボリックリンクを作り「../app/mysql/bin/mysql」を指すようにしたので、/usr/local/app/mysqlのシンボリックリンクをただひとつ切り替えるだけで、mysqlやmysqladminなどのコマンドのバージョンも自動的に切り替えられるというわけです。

ちなみに、5.0.3からmysqlmanagerというツールが付属するようになりました。これは同一マシン上の複数のMySQLインスタンスを管理するためのツールなのですが、ひとつのインスタンスをバージョンを切り替えて使うだけならば、今回紹介したようなシンボリックリンクを切り替える方式の方がお手軽だと思います。

 

取り上げるストレージエンジン

今回は、5.0で新規追加された次のエンジンを取り上げます。

  • - FEDERATED
  • - ARCHIVE
  • - CSV
  • - BLACKHOLE

前節でStandardではなくMaxをインストールしましたが、StandardとMaxでは表3の通り、使用できるエンジンに違いがあります。(注1)

表3:サポートしているエンジンの違い
配布種別 エンジン
Standard ARCHIVE
Max ARCHIVE, FEDERATED, CSV, BLACKHOLE
  • 注1:エンジン以外の相違点については、MySQLのサイトを参照してください。
 

稼働中のMySQLがどのエンジンをサポートしているか確認するには、SHOW VARIABLESかSHOW ENGINESを使います。(図3)

<図3:サポートしているエンジンの確認>
root@localhost[mysql]> SHOW VARIABLES LIKE 'have%'; +-----------------------+----------+ | Variable_name | Value | +-----------------------+----------+ | have_archive | YES | | have_bdb | NO | | have_blackhole_engine | YES | | have_compress | YES | | have_crypt | YES | | have_csv | YES | | have_example_engine | YES | | have_federated_engine | YES | | have_geometry | YES | | have_innodb | YES | | have_isam | NO | | have_ndbcluster | DISABLED | | have_openssl | DISABLED | | have_query_cache | YES | | have_raid | NO | | have_rtree_keys | YES | | have_symlink | YES | +-----------------------+----------+ 17 rows in set (0.01 sec) root@localhost[mysql]> show engines\G *************************** 1. row *************************** Engine: MyISAM Support: DEFAULT Comment: Default engine as of MySQL 3.23 with great performance *************************** 2. row *************************** Engine: MEMORY Support: YES Comment: Hash based, stored in memory, useful for temporary tables : : : : :

図3の結果でお気づきかもしれませんが、ISAM(注2)ストレージエンジンはサポートされなくなりました。ISAMの代わりとしてはMyISAMを使えばよいでしょう。

また、そのほかの変更点としては、CREATE TABLE文でストレージエンジンを指定する場合にはENGINE=InnoDBを使うように変更されました。これまでのTYPE=InnoDBも使用可能ですが、ENGINE=を使うように推奨されています。(注3)

さて、だいぶお待ちかねだと思いますが、いよいよ次節からストレージエンジンの紹介をしていきます。

 

FEDERATED

5.0.3から追加されたストレージエンジンで、その用途は他のMySQLのテーブルにアクセスできるようにするためのもの(他のRDBMSでいうところのDBLINKのようなもの)です。今回紹介するストレージエンジンの中では『使える』部類に入るものではないかと思います。また、FEDERATEDのユニークな点は、現在はMySQLとしか連携できませんが、将来的に他のRDBMS(PostgreSQLやOracleなど)との連携も計画されていることが挙げられるでしょう。

 

他のテーブルを参照する

では早速、試してみましょう。以下ではFEDERATEDテーブルを作るホストをmy5-1、FEDERATEDで参照されるホストをmy5-2とし、下準備として図4のSQLを実行しておきます。

<図4:FEDERATEDを使う準備>
●my5-2でrootユーザで実行するSQL CREATE DATABASE wd; GRANT SELECT, INSERT, UPDATE, DELETE , CREATE, ALTER, DROP, REFERENCES, INDEX , LOCK TABLES ON wd.* TO wd@'%' IDENTIFIED BY 'press2'; ●my5-2でwdユーザで実行するSQL use wd; DROP TABLE IF EXISTS user_master; CREATE TABLE IF NOT EXISTS user_master ( id SMALLINT UNSIGNED ,name VARCHAR(32) NOT NULL ,age TINYINT UNSIGNED ,PRIMARY KEY (id) ) ENGINE=InnoDB; INSERT INTO user_master VALUES (1,'ichirou', 21) ,(2,'jirou', 32) ,(3,'saburou', 43) ; ●my5-1でrootユーザで実行するSQL CREATE DATABASE wd; GRANT SELECT, INSERT, UPDATE, DELETE , CREATE, ALTER, DROP, REFERENCES, INDEX , LOCK TABLES ON wd.* TO wd@'%' IDENTIFIED BY 'press1';

準備ができたところでFEDERATEDのテーブルを作るために、my5-1にwdユーザでログインし、図5のSQLを実行します。うまくできたら参照(SELECT * FROM f_userなど)してみましょう。また、FEDERATED経由のテーブルは更新もできるので、INSERTやDELETEも試してみましょう。

<図5:FEDERATEDエンジンのテーブルを作る>
use wd; DROP TABLE IF EXISTS f_user; CREATE TABLE IF NOT EXISTS f_user ( id SMALLINT UNSIGNED NOT NULL ,name VARCHAR(32) NOT NULL ,age TINYINT UNSIGNED ,PRIMARY KEY (id) ) ENGINE=FEDERATED CONNECTION='mysql://wd:press2@my5-2/wd/user_master';
 

FEDERATEDの注意点など

とりあえずテーブルを作ってみましたが、FEDERATEDには注意点がいくつかあります。

テーブル定義

先の例でも見たように、参照する側(my5-1)のDDLは、参照される側(my5-2)のDDLと以下の点以外は同じにする必要があるとされています。

  • - テーブル名は異なってももよい。
  • - ENGINE=FEDERATEDにする。
  • - CONNECTION=を付加する。

ここで図4と図5を見比べると他にも異なる点があります。そう、idカラムの「NOT NULL」です。参照される側でCREATE TABLEした際には、PRIMARY KEYによって暗黙的にNOT NULL制約が付与されましたが、(少なくとも今回の環境のバージョンでは)FEDERATEDを使う参照する側ではNOT NULLを明示的に指定する必要があり、NOT NULLを指定しない場合は

ERROR 1121 (42000): Column 'id' is used with UNIQUE or INDEX but is not defined as NOT NULL

というエラーが発生しました。

接続ユーザのパスワード

SHOW CREATE TABLEでFEDERATEDなテーブルのテーブル定義を見ると、接続ユーザのパスワードが表示されてしまいます。将来的に改善されることが示唆されていますが、それまでは権限情報の取り扱いには注意してください。

トランザクション

FEDERATED経由のテーブルに対してSTART TRANSACTIONしてもトランザクション処理は行われず、AUTOCOMMITモードの様な動作になります。同様にFEDERATED経由のテーブルに対してLOCK TABLESを発行しても効果はありません。

なお、参照される側で実行されたトランザクションやLOCK TABLESは、参照する側のFEDERATEDなテーブルにも効果があります。例えば、my5-2でLOCK TABLES user_mastert WRITEが発行されると、my5-1でのSELECT * from f_userはブロックされて待たされます。

FEDERATEDテーブルの削除

DROP TABLE文で削除可能です。参照する側でDROP TABLEを実行しても、参照される側のテーブルは消されませんので安心してください

参照される側のテーブル定義の変更

参照される側のテーブル定義が変わった場合、例えばカラムが追加されたり削除された場合、参照する側のFEDERATEDなテーブルは作り直す必要があるのですが、FEDERATEDはALTER TABLEをサポートしていないため、DROP TABLEしてCREATE TABLEしなおす必要があります。

他のバージョンとも連携できるか

試した限りでは、MySQL 4.0.24のテーブルを参照できました。

 

FEDERATEDを開拓

さて、ここまででFEDERATEDの優等生的な解説が終わりました。好奇心旺盛な読者の方はあれやこれや疑問点が出てきたのではないでしょうか。ここからは開拓団の本領発揮ということで、あんなことやこんなことを実用性を無視して試してみたいと思います。ただ、これから紹介する挙動の中には、もしかしたら実はバグで将来のバージョンでは修正され、挙動が変わるものもあるかもしれませんので、その点、ご了承ください。

参照する側に作られるものは?

参照する側でカラム定義付きのCREATE TABLE文を発行する必要があったことから推測できる通り、参照する側では何も作られないわけではなく、拡張子.frmのファイルが作られます。*.frmファイルはFEDERATED固有のものではなくMyISAMやInnoDBのテーブルでも作られるテーブル定義が格納されているファイルです。

自動再接続するか?

当然、参照される側のmysqldが停止している間はFEDERATED経由で参照できませんが、参照される側が起動すれば参照する側が自動的に再接続してくれますので、再度CREATE TABLEを発行する必要はありません。また、wait_timeout(初期値は8時間)が過ぎて接続が切れた場合も再接続してくれます。ちなみに、再接続の機能はFEDERATEDに実装されているものではなく、FEDERATEDが接続に使用しているMySQLのCクライアントAPI(mysql_real_connect)の機能によるものです。

ローカルのテーブルと結合できるか?

できます。JOINはもちろん、UNIONなども可能です。試しにこのようなテーブルをローカルに作り、

use wd; DROP TABLE IF EXISTS linux_user; CREATE TABLE IF NOT EXISTS linux_user ( id SMALLINT UNSIGNED ,distrib VARCHAR(32) NOT NULL ,PRIMARY KEY (id) ) ENGINE=InnoDB; INSERT INTO linux_user VALUES (1,'Debian') ,(3,'Fedora') ;

FEDERATEDなf_userテーブルと結合すると、図6のように期待通り結合できることが確認できます。

<図6:FEDERATEDテーブルとの結合>
wd@my5-1[wd]> SELECT l.id AS id ,u.name AS name ,u.age AS age ,l.distrib AS distrib FROM linux_user AS l LEFT JOIN f_user AS u ON l.id = u.id; +----+---------+------+---------+ | id | name | age | distrib | +----+---------+------+---------+ | 1 | ichirou | 21 | Debian | | 3 | saburou | 43 | Fedora | +----+---------+------+---------+ 2 rows in set (0.01 sec)

クエリキャッシュにのるか?

もしFEDERATED経由のテーブルのクエリがローカルのクエリキャッシュ領域にキャッシュされれば性能向上が期待できます。そんな淡い期待を確認してみます。

まずはクエリキャッシュが有効になっているか、次のようにして確認します。

wd@my5-1[wd]> SHOW VARIABLES LIKE 'query_cache_size'; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | query_cache_size | 0 | +------------------+-------+

「Value」がゼロの場合は無効になっていますので、my.cnfの[mysqld]セクションに

query_cache_size = 1M

と追加してmysqldを再起動します。起動したら、念のためSHOW VARIABLESで非ゼロになっているか確認しましょう。

次にクエリキャッシュの状況を知る方法を確認しておきます。クエリキャッシュの状況を見るには SHOW STATUS LIKE 'Qcache%' を使います。この中で今回着目するのは

  • - Qcache_hits
  • - Qcache_inserts
  • - Qcache_not_cached
  • - Qcache_queries_in_cache

の4つです。まずはローカルのテーブルをSELECTして、これらの値がどう変化するか確認してみましょう。わかりやすいように最初にステータス情報などをリセットします。

query_cache_size = 1M root@my5-1[wd]> RESET QUERY CACHE; root@my5-1[wd]> FLUSH STATUS; wd@my5-1[wd]> SHOW STATUS LIKE 'Qcache%'; +-------------------------+---------+ | Variable_name | Value | +-------------------------+---------+ | Qcache_free_blocks | 1 | | Qcache_free_memory | 1039960 | | Qcache_hits | 0 | | Qcache_inserts | 0 | | Qcache_lowmem_prunes | 0 | | Qcache_not_cached | 1 | | Qcache_queries_in_cache | 0 | | Qcache_total_blocks | 1 | +-------------------------+---------+

続いて先ほど作ったローカルのテーブルをSELECTして、ステータスを確認します。

wd@my5-1[wd]> SELECT * FROM linux_user; +----+---------+ | id | distrib | +----+---------+ | 1 | Debian | | 3 | Fedora | +----+---------+ wd@my5-1[wd]> SHOW STATUS LIKE 'Qcache%'; +-------------------------+---------+ | Qcache_hits | 0 | | Qcache_inserts | 1 | | Qcache_not_cached | 2 | | Qcache_queries_in_cache | 1 | +-------------------------+---------+ (一部省略しています。以下同様)

Qcache_insertsとQcache_queries_in_cacheが増えました。これはSELECTの結果がキャッシュに入ったことを意味します。なお、Qcache_not_cachedが増えているのはSHOW STATUSの分のカウントです。

実際にキャッシュが使われるか確認するため、先ほど全く同じSQL文を発行します。

wd@my5-1[wd]> SELECT * FROM linux_user; (省略) wd@my5-1[wd]> SHOW STATUS LIKE 'Qcache%'; +-------------------------+---------+ | Qcache_hits | 1 | | Qcache_inserts | 1 | | Qcache_not_cached | 3 | | Qcache_queries_in_cache | 1 | +-------------------------+---------+

Qcache_hitsが増えました。これはキャッシュが使われたことを意味します。

ではこれをFEDERATEDなテーブルで同じように実行して、キャッシュが使われるか確認してみましょう。発行するSELECT文はFEDERATEDなテーブルを参照したSELECT * FROM f_userになります。(図7)

実行結果を見ての通り、クエリキャッシュは効きませんでした。これはSELECT文にSQL_CACHEオプションを指定しても変わりません。このことから、FEDERATEDなテーブルは毎回参照される側へ問い合わせることがわかりました。なお、キャッシュが効かないのは参照している側であって、参照される側では参照する側から発行されたクエリはちゃんとキャッシュされます。

<図7:FEDERATEDにクエリキャッシュは効くか?>
root@my5-1[wd]> RESET QUERY CACHE; root@my5-1[wd]> FLUSH STATUS; wd@my5-1[wd]> SHOW STATUS LIKE 'Qcache%'; +-------------------------+---------+ | Qcache_hits | 0 | | Qcache_inserts | 0 | | Qcache_not_cached | 1 | | Qcache_queries_in_cache | 0 | +-------------------------+---------+ wd@my5-1[wd]> SELECT * FROM f_user; (省略) wd@my5-1[wd]> SHOW STATUS LIKE 'Qcache%'; +-------------------------+---------+ | Qcache_hits | 0 | | Qcache_inserts | 0 |←増えない | Qcache_not_cached | 3 |←2増えた | Qcache_queries_in_cache | 0 |←増えない +-------------------------+---------+ wd@my5-1[wd]> SELECT * FROM f_user; (省略) wd@my5-1[wd]> SHOW STATUS LIKE 'Qcache%'; +-------------------------+---------+ | Qcache_hits | 0 | | Qcache_inserts | 0 |←増えない | Qcache_not_cached | 5 |←2増えた | Qcache_queries_in_cache | 0 |←増えない +-------------------------+---------+

カラム定義は同じ必要があるのか?

MySQL ABのドキュメントには『正確に同じにしなければならない』と書いてありますが、ちょっといたずらしてみましょう。

図5とは以下の点が異なるFEDERATEDなテーブルを作って遊んでみましょう。(図8)

図5とはageカラムがない点が異なるFEDERATEDなテーブルを作って遊んでみましょう。(図8)

<図8:テーブル定義が違うFEDERATED>
wd@my5-1[wd]> use wd; wd@my5-1[wd]> DROP TABLE IF EXISTS user_name; wd@my5-1[wd]> CREATE TABLE IF NOT EXISTS user_name ( id SMALLINT UNSIGNED ,name VARCHAR(32) NOT NULL ) ENGINE=FEDERATED CONNECTION='mysql://wd:press2@my5-2/wd/user_master'; wd@my5-1[wd]> SELECT * FROM user_name; +------+---------+ | id | name | +------+---------+ | 1 | ichirou | | 2 | jirou | | 3 | saburou | +------+---------+

あれ? CREATE TABLEが成功したばかりか、SELECTもできちゃいました。しかもちゃんとidとnameカラムの値だけが表示されています。これにはちょっと驚きました。

参照される側で実行されるクエリを見てみると、参照する側で「SELECT *」と実行されたクエリはFEDERATEDなテーブルのカラム定義に応じて「SELECT id,name」や「SELECT id,name,age」と展開されて、参照される側に渡されるようです。

自己FEDERATEDできるか?

残念ながら、CREATE TABLEを発行してnet_read_timeout(初期値は30秒)の間待たされた後に以下のエラーが出て失敗します。

ERROR 1434 (HY000): Can't create federated table. Foreign data src error: error: 1159 ''

ただ、同様の報告がバグとして報告されているので、将来のバージョンでは自己FEDERATEDできるようになるかもしれませんね。(注4)

 

レプリケーションで二重更新

レプリケーション構成にFEDERATEDを組み合わせて実験してみましょう。登場人物はレプリケーションしているマスタ、スレーブと、FEDERATEDで参照される3台です。

まず、マスタでCREATE TABLE ... ENGINE=FEDERATEDなテーブルを作ります。そしてこのSQLはレプリケートされてスレーブでも実行されます。

次にマスタで作ったFEDERATEDなテーブルにINSERTやUPDATEなど更新系のクエリを発行します。するとそのクエリはレプリケートされてスレーブでも実行されます。つまり、実体である参照されているテーブルには、同じ二つの更新系クエリが実行されることになってしまいます。

UNIQUE制約が課されていれば制約違反で更新が失敗しレプリケーションが停止しますし、そうでない場合は「行が二つずつINSERTされる」「カウントが2つずつ増える」といった現象が起こりますので気をつけましょう。

レプリケーションで循環更新

今度はレプリケーション+FEDERATEDで、更新処理がループするような構成を作ってみましょう。

まずは正常なレプリケーション構成を作った上で、マスタで次のようなテーブルを作ります。

use wd; DROP TABLE IF EXISTS countme; CREATE TABLE IF NOT EXISTS countme ( n INT UNSIGNED ) ENGINE=InnoDB;

そして試しにマスタでINSERTしてみます。

wd@my5-1[wd]> INSERT INTO countme VALUES (1); wd@my5-1[wd]> SELECT MAX(n) FROM countme; +--------+ | MAX(n) | +--------+ | 1 | +--------+

スレーブでも同じSELECT文を発行すれば同じ結果が得られるはずです。

ここからが本番です。マスタはそのままで、スレーブでだけcountmeテーブルを削除して、同名のFEDERATEDなテーブルを作ります。そしてこのFEDERATEDなテーブルはマスタを参照するようにします。

DROP TABLE IF EXISTS countme; CREATE TABLE IF NOT EXISTS countme ( n INT UNSIGNED ) ENGINE=FEDERATED CONNECTION='mysql://wd:press1@my5-1/wd/countme';

さて、この状態でマスタに更新系のクエリを発行するとどうなるのでしょうか。マスタで発行されたクエリはバイナリログ経由でスレーブでも実行されます。そしてスレーブのcountmeテーブルはFEDERATEDで実体はマスタにあるので、マスタのテーブルが更新され、ここで最初に戻ることになるので以下、延々とループしそうです。

では試してみましょう。まずはマスタでUPDATE文を実行してみます。

wd@my5-1[wd]> UPDATE countme SET n = n + 1; wd@my5-1[wd]> SELECT MAX(n) FROM countme; +--------+ | MAX(n) | +--------+ | 3 | +--------+

あれ。予想に反して増え続けませんね。マスタのクエリログを見てみると、

Id Command Argument 2 Query UPDATE countme SET n = n + 1 4 Query SHOW TABLE STATUS LIKE 'countme' 4 Query SELECT `n` FROM `countme` 4 Query UPDATE `countme`SET n = 3 WHERE n = 2 LIMIT 1 4 Query SHOW TABLE STATUS LIKE 'countme' 4 Query SELECT `n` FROM `countme`

となっていました。1行目のId 2が最初に発行したUPDATE文で、Id 4のはスレーブからFEDERATED経由で実行されたものです。Id 4のUPDATE文はWHERE句とLIMIT句がついている点に気づくと思います。どうやらFEDERATEDなテーブルに対して発行されたUPDATE文は、WHERE句やLIMIT句を伴ってより限定的なUPDATE文となって参照される側で実行されるようです。

それではINSERT文ではどうでしょうか。今度はAUTO_INCREMENT型のテーブルを作ってみます。

use wd; DROP TABLE IF EXISTS countme; CREATE TABLE IF NOT EXISTS countme ( n INT UNSIGNED AUTO_INCREMENT ,primary key (n) ) ENGINE=InnoDB;

そしてスレーブで、先ほどと同じようにcoutmeテーブルをDROPして同名のFEDERATEDなテーブルを作ります。これでループができたので、マスタにINSERTしてみましょう。

wd@my5-1[wd]> INSERT INTO countme VALUES (NULL); wd@my5-1[wd]> SELECT MAX(n) FROM countme; +--------+ | MAX(n) | +--------+ | 391 | +--------+ wd@my5-1[wd]> SELECT MAX(n) FROM countme; +--------+ | MAX(n) | +--------+ | 2237 | +--------+

見事に増殖しました(^^;。このままだとリソース有る限り増え続けますので、満足したところでDROP TABLEするなりmysqldを停めるなりしてループを断ち切ってください。

レプリケーションでの活用

Advanced MySQL Replication Techniques』というドキュメントに、循環構成のレプリケーションでストアドプロシージャを使った自動フェイルオーバを実現するというとてもエキサイティングな内容が書かれています。ここでもFEDERATEDが登場し、『だれがマスタか』という情報を保持するテーブルを参照するために使われています。

 

ARCHIVE

ARCHIVEは4.1.3から追加されたストレージエンジンです。ARCHIVEの特徴は、zlibによる可逆圧縮でより小さな容量でデータを格納できる点です。

そのほか、ARCHIVEには以下のような特徴があります。

  • - インデックスがつけられない。
  • - ORDER BYはできる。
  • - 実行可能なDMLはSELECTとINSERTだけで、DELETE、UPDATE、REPLACEなどは使えない。
  • - MyISAMと同じように、OPTIMIZE TABLEやREPAIR TABLEでテーブルの最適化と修復が可能。
  • - 5.1.6からAUTO_INCREMENTが使えるようになり、AUTO_INCREMENTなカラムにのみインデックスがつけられるようになった。

データの圧縮率はデータの内容によるのですが、試しに市区町村名入りの郵便番号データが入ったテーブルを、MyISAMとARCHIVEとで比較したところ、図9の通りARCHIVEはMyISAMの約27%の大きさになりました。ちなみにzipcode_myisampackはmyisampackコマンドで圧縮したもので、58%の大きさになりました。myisampack後のテーブルは参照だけで更新ができなくなりますが、インデックスをつけることができるという点がARCHIVEと違います。

<図9:ARCHIVEなテーブルの大きさ>
# du -k data/wd/zipcode_{myisam*.MYD,archive.ARZ} 4685 data/wd/zipcode_myisam.MYD 2719 data/wd/zipcode_myisampack.MYD 1285 data/wd/zipcode_archive.ARZ

このようにARCHIVEなテーブルはディスク上の容量をあまり消費せずに大きなデータを格納できるのですが、インデックスがつけられない(つまり毎回テーブルをフルスキャンする)ことをはじめとして制限事項がいくつかあります。また、環境とバージョンによっては2GBを越えられないという情報もあるので注意してください。

 

CSV

CSVは4.1.4から追加されたストレージエンジンです。これもインデックスはつけられません。

データは拡張子がCSVのデータファイルにCSV形式で格納されます。例えば、次のSQLを実行すると、

use wd; DROP TABLE IF EXISTS user_csv; CREATE TABLE IF NOT EXISTS user_csv ( id SMALLINT UNSIGNED ,name VARCHAR(32) NOT NULL ,age TINYINT UNSIGNED ) ENGINE=CSV; INSERT INTO user_csv VALUES (1,'ichirou', 21) ,(2,'jirou', 32) ,(3,'3rou"', 43) ;

データディレクトリの下にwd/user_csv.CSVというファイルができ、その中は図10のようにCSV形式となっています。また、このほかにCSVなテーブルにはテーブル定義を格納した*.frmファイルが必要です。

<図10:CSVなテーブルのデータファイル>
"1","ichirou","21" "2","jirou","32" "3","3rou\"","43"

ちょっと危険ですが、直接データファイルをエディタなどで編集した場合は、mysqldを再起動するかFLUSH TABLEを発行すれば、編集後のデータをSELECTできるようです。

 

BLACKHOLE

BLACKHOLEは4.1.11から追加されたストレージエンジンです。このストレージエンジンはちょっと変わっていて、データを一切保持せず、BLACKHOLEなテーブルを作ってもできるのはテーブル定義が保存される*.frmファイルだけで、データを格納するファイルは作られません。つまり、CREATE TABLEしたテーブル定義は保存されますが、INSERTしてもSELECTしても有効な何も結果は何も返ってきません。

こんなのなんに使うの? と思いますが、MySQL ABのドキュメントには、レプリケーションの更新受付用のダミーサーバ(BLACKHOLEなテーブルに対する更新系クエリはバイナリログには記録されるので、スレーブが実テーブルを持っていれば、スレーブにはデータが格納される)や、バイナリログのオーバーヘッド測定などに使えると紹介されています。

 

開拓のふりかえり

今回は4.1と5.0で新規に追加されたストレージエンジンについて紹介しました。ARCHIVEは用途によっては有益だと思いますし、FEDERATEDはまだMaxでしか使えませんが、使いどころが多いのではないかと思います。

一方、既存のストレージエンジンたちも着実に改良、機能追加がなされています。特にInnoDBにはいろいろとパラメータが追加されていて要チェックです。また、InterBase(オープンソースのRDBMSであるFirebirdの祖先)の産みの親、Jim Starkey氏が手がけているFalconやSolid Information Technologyが開発しているsolidDBなど、ポストInnoDBを狙う新型エンジンの登場にも目がはなせません。

そこで次号では引き続きストレージエンジンを取り上げ、既存のストレージエンジンの変更点と、新型エンジンの情報をお届けしたいと思います。

 

コラム:gadget プロンプトの変更

mysqlコマンドのデフォルトのプロンプトは『mysql>』なのですがこれは変更できます。特に、複数の接続ユーザやデータベースを使う際には、プロンプトにこれらの情報を表示させておくと便利です。

設定方法はいくつかあるのですが、永続的に設定するならば、環境変数MYSQL_PS1にセットするか、~/.my.cnfの[mysql]セクションに書いておくのがよいでしょう。例えば、今回の記事中の実行例のように、プロンプトを『ユーザ名@ホスト名[DB名]>』とするには、

$ export MYSQL_PS1='\u@\h[\d]> '

か、~/.my.cnfに

[mysql] prompt="\u@\h[\d]> "

と書きます。

そのほかのプロンプトで使える特殊文字は、MySQL ABのドキュメントを参照してください。

 
著者名:ひろせまさあき/HIROSE Masaaki
 

ページの先頭へ