空前の MySQL binlog API ブームですが、みなさん libreplication の examples/basic-[12] を実行するだけで満足してしまっているようです。しかし、libreplication のおもしろいのは examples/mysql2lucene の方なんです。
普段はあまり意識しないかもしれないですが mysql の binlog には statement based, row based, mixed の3種類があります。
statement based は、実際に実行された SQL が記録されます。一部の関数でちょっと危険です。
row based では、実際に変更された行のデータが記録されます。
mixed では、危険な関数をつかった場合などには row based で記録し、そうでなければ statement based で記録します。最近はこれがデフォルトです。
詳細は http://thinkit.co.jp/article/95/1?page=0,2 このへんなどをご欄ください。
SET GLOBAL binlog_format = 'ROW';
とかやると、binlog_format が row based に変更されます。
この状態で binlog API をたたくと、TABLE_MAP_EVENT, WRITE_ROWS_EVENT, UPDATE_ROWS_EVENT, DELETE_ROWS_EVENT の4種類のイベントがおくられてくるようになります。
行データが一緒におくられてくるので、それをつかって手元のデータをアップデートすればいいのです。SQL statement を解析したりする必要はありません。
以下のようなプログラムで簡単に行のデータをとれます。
use 5.016000;
use MySQL::BinLog;
my %table_map;
my $url = shift or pod2usage;
my $binlog = MySQL::BinLog->new(MySQL::BinLog::create_transport($url));
$binlog->connect();
$binlog->set_position(4);
say("connected: $binlog");
while (my $event = $binlog->wait_for_next_event()) {
my $type = $event->get_event_type();
if ($type eq TABLE_MAP_EVENT) {
$table_map{$event->table_id} = $event;
} elsif ($type ~~ [WRITE_ROWS_EVENT, UPDATE_ROWS_EVENT, DELETE_ROWS_EVENT]) {
my $table_event = $table_map{$event->table_id}
or die "Unknown table: " . $event->table_id;
my $rows = MySQL::BinLog::Row_event_set->new($event, $table_event);
my $iter = $rows->begin();
while (my $row = $iter->next()) {
if ($type eq WRITE_ROWS_EVENT) {
say("INSERT");
show_row($row);
} elsif ($type eq UPDATE_ROWS_EVENT) {
say("UPDATE BEFORE");
show_row($row);
say("UPDATE AFTER");
show_row($iter->next());
} elsif ($type eq DELETE_ROWS_EVENT) {
say("DELETE");
show_row($row);
}
}
}
}
sub show_row {
my $row = shift;
my $fields_iter = $row->begin;
while (my $field = $fields_iter->next) {
printf(" TYPE: %-10s STR: %s\n", $field->type_str, $field->as_string);
}
}
この状態で以下のような SQL を発行します。
INSERT INTO hoi (id, name) values (1, "HOGEHOGE");
UPDATE hoi SET name="FUGAFUGA" WHERE id=1;
DELETE FROM hoi WHERE id=1;
すると、以下のような結果がえられます。
INSERT
TYPE: LONG STR: 1
TYPE: VARCHAR STR: HOGEHOGE
TYPE: TIMESTAMP STR: 1341954455
UPDATE BEFORE
TYPE: LONG STR: 1
TYPE: VARCHAR STR: HOGEHOGE
TYPE: TIMESTAMP STR: 1341954455
UPDATE AFTER
TYPE: LONG STR: 1
TYPE: VARCHAR STR: FUGAFUGA
TYPE: TIMESTAMP STR: 1341954455
DELETE
TYPE: LONG STR: 1
TYPE: VARCHAR STR: FUGAFUGA
TYPE: TIMESTAMP STR: 1341954455
row based で binlog を記録することにより、行のデータを簡単に取得できます。
binlog API をつかうことで lucene, solr, groonga, rast などの全文検索エンジンにデータをうつすことが容易にできます。通常 solr を運用する場合は minutely で SELECT かけてデータをうつしたりすることが多いとおもいますが、binlog API をつかえばリアルタイムにデータをコピーできます。
mroonga のようなアプローチもありますが、こういうアプローチも面白いのではないでしょうか。mysql storage engine にくらべ制約がすくなく、開発期間も格段に短縮できそうですね。
このような、任意のストレージに replication できるというのが binlog API の主目的だとおもいますが、その他にもデータのモニタリングやらなんやら、アイディアはいろいろ考えられるのではないでしょうか。
エキスパートのためのMySQL[運用+管理]トラブルシューティングガイド