2012年3月アーカイブ

松信さんが作った Replication Booster for MySQL をデータサイズが大きいデータベースに対して使ってみました。

Yoshinori Matsunobu’s blog: Making slave pre-fetching work better with SSD
github - yoshinorim/replication-booster-for-mysql

Replication Booster for MySQL をものすごく簡単に説明すると、以下のようになるでしょうか。

replication_booster_mysql.png

MySQL でレプリケーションを設定した場合、マスターのバイナリログをIOスレッドが読み取り、relay-logへ記録します。そしてSQLスレッドがrelay-logから読み取ってテーブルを更新して行きます。Replication Booster を実行するとrelay-logを読み取り、更新クエリを参照クエリに書き換えてSQLスレッドよりも先にテーブルに対してアクセスします。これにより、InnoDBのbuffer_poolなどメモリ上にこれから更新されるデータを載せ、更新クエリの実行にかかる時間を減らそうというものです。

主なユースケースとしてはスレーブサーバの再起動した際に、バッファをより速く暖めレプリケーションの遅延の解消にかかる時間を短くすることが考えられます。

検証した環境など

今回試したのは再起動後の遅延解消ではなく、メモリに対してデータサイズがとても大きな某サービスのデータベースのバックアップスレーブの遅延対策としてReplication Boosterを動かしてみることです。ちなみにデータサイズは560GB、メモリは16GB(innodb_buffer_pool=10GB)です。毎秒10弱の更新クエリがあります。DiskはSATAをBBU付きRAID1という環境です

replication_backup_slave.png

レプリケーション遅延はバックアップスレーブだけではなく参照を行うスレーブサーバでも起きているのですが、参照を行う分だけバッファのヒット率が多少高いのか、バックアップスレーブほど遅延していません。そこでReplication Boosterを使い、バックアップスレーブでも参照クエリを発行してバッファの効率をあげてみるのが今回の狙い。

Replication Booster のインストール

2013年3月時点で、Replication Boosterも依存するBinlog APIもまだpre-alphaということで注意が必要です

今回インストールしたのはCentOS 5.xのサーバ。

まずcmake、bzrのインストールとboostのアップデート。bzrはepelを利用すると簡単。boostは atrpms を利用できます。

$ sudo yum install cmake bzr
$ sudo rpm -Uvh http://dl.atrpms.net/all/boost-1.39.0-9.el5.x86_64.rpm \
  http://dl.atrpms.net/all/boost-devel-1.39.0-9.el5.x86_64.rpm http://dl.atrpms.net/all/boost-wave-1.39.0-9.el5.x86_64.rpm \
  http://dl.atrpms.net/all/boost-thread-1.39.0-9.el5.x86_64.rpm http://dl.atrpms.net/all/boost-test-1.39.0-9.el5.x86_64.rpm \
  http://dl.atrpms.net/all/boost-system-1.39.0-9.el5.x86_64.rpm http://dl.atrpms.net/all/boost-static-1.39.0-9.el5.x86_64.rpm \
  http://dl.atrpms.net/all/boost-signals-1.39.0-9.el5.x86_64.rpm http://dl.atrpms.net/all/boost-serialization-1.39.0-9.el5.x86_64.rpm \
  http://dl.atrpms.net/all/boost-regex-1.39.0-9.el5.x86_64.rpm http://dl.atrpms.net/all/boost-python-1.39.0-9.el5.x86_64.rpm \
  http://dl.atrpms.net/all/boost-program-options-1.39.0-9.el5.x86_64.rpm http://dl.atrpms.net/all/boost-math-1.39.0-9.el5.x86_64.rpm \
  http://dl.atrpms.net/all/boost-iostreams-1.39.0-9.el5.x86_64.rpm http://dl.atrpms.net/all/boost-graph-1.39.0-9.el5.x86_64.rpm \
  http://dl.atrpms.net/all/boost-filesystem-1.39.0-9.el5.x86_64.rpm http://dl.atrpms.net/all/boost-date-time-1.39.0-9.el5.x86_64.rpm \
  http://dl.atrpms.net/all/boost-doc-1.39.0-9.el5.x86_64.rpm

epelのyum repository利用しにくい環境の場合は、

$ sudo yum install python-pycurl
$ sudo rpm -Uvh  http://dl.fedoraproject.org/pub/epel/5/x86_64/bzr-2.1.4-2.el5.x86_64.rpm \
  http://dl.fedoraproject.org/pub/epel/5/x86_64/python-paramiko-1.7.6-1.el5.noarch.rpm \
  http://dl.fedoraproject.org/pub/epel/5/x86_64/python-crypto-2.0.1-4.el5.2.x86_64.rpm

これでも大丈夫です。

次にBinlog APIのインストール。repositoryは https://code.launchpad.net/mysql-replication-listener にあります

$ bzr branch lp:mysql-replication-listener
$ cd mysql-replication-listener/
$ cmake .
$ make -j4
$ sudo make install

最後にreplication_boosterのビルド

$ git clone git://github.com/yoshinorim/replication-booster-for-mysql.git
$ cd replication-booster-for-mysql/
$ cmake .
$ make

検証したサーバではMySQLが通常とは異なるパスに入っていたので、CMakeLists.txtをごにょごにょしました。あと今回は検証目的なので、replication_boosterコマンドはmake installしていません。

コマンドのヘルプ

$ ./replication_booster --help
Usage: 
 replication_booster [OPTIONS]

Example: 
 replication_booster --user=mysql_select_user --password=mysql_select_pass --admin_user=mysql_root_user --admin_password=mysql_root_password --socket=/tmp/mysql.sock 

Options (short name):
 -t, --threads=N                :Number of worker threads. Each worker thread converts binlog events and executes SELECT statements. Default is 10 (threads).
 -o, --offset-events=N          :Number of binlog events that main thread (relay log reader thread) skips initially when reading relay logs. This number should be high when you have faster storage devices such as SSD. Default is 500 (events).
 -s, --seconds-prefetch=N       :Main thread stops reading relay log events when the event's timestamp is --seconds-prefetch seconds ahead of current SQL thread's timestamp. After that the main thread starts reading relay logs from SQL threads's position again. If this value is too high, worker threads will execute many more SELECT statements than necessary. Default value is 3 (seconds).
 -m, --millis-sleep=N           :If --seconds-prefetch condition is met, main thread sleeps --millis-sleep milliseconds before starting reading relay log. Default is 10 milliseconds.
 -u, --user=mysql_user          :MySQL slave user name. This user should have at least SELECT privilege on all application tables (default: root)
 -p, --password=mysql_pwd       :MySQL slave password (default: empty)
 -a, --admin_user=mysql_user    :MySQL administration user for the slave. This user should have at least SUPER and REPLICATION CLIENT (for SHOW SLAVE STATUS) privileges. (default: root)
 -b, --admin_password=mysql_pwd :MySQL password for the administration user. (default:empty)
 -h, --host=mysql_host          :MySQL slave hostname or IP address. This must be local address. (default: localhost)
 -P, --port=mysql_port          :MySQL slave port number (default:3306)
 -S, --socket=mysql_socket      :MySQL socket file path

Replication Booster の実行と結果

replication_boosterは以下のように実行しました

$ ./replication_booster  -t 8 -o 10 -u booster -p xxxxxxx -a booster -p xxxxxxx -h 127.0.0.1

スレッドは8個、relay-logはあまりskipせずに頻繁にクエリを実行させてみました。

結果は以下

replicationbooster.png

グラフは一つ前のエントリ「CloudForecastでMySQLのレプリケーション監視」によるもの。上が秒数、下がバイナリログのポジション。明らかな差はでてないですが、Seconds Behind Masterの平均を取ると、導入前が「0.5」、実行後は「0.2」と遅延幅は小さくなり、レプリケーション遅延のアラートの回数も減りました。

今後は別のサーバも含めてもうちょっと検証していくつもり

MySQLのレプリケーションの遅延状況を取得するプラグインをCloudForecast本体に追加しました。

host_configで

---
component_config:
resources:
  - traffic:eth0
  - traffic:eth1
  - basic
  - mysql
  - innodb
  - mysqlreplication

このように追加すれば使えます。

作られるグラフな下のような感じで、秒数による表示とバイナリログのポジションの2つが作成されます。

cloudforecast_mysqlreplication.png

グラフは某サービスの実際のレプリケーション遅延状況です。データが非常に多いので結構遅れてる事が多いですね。蛇足ですがMySQL4系の場合は「Seconds Behind Master」がないので秒数は常に0になります^^

大きくレプリケーションが遅延するタイミングがあれば、その際に発行されているクエリを見直してみる等の用途にこのグラフが使えるでしょうか。

「Webエンジニアのためのデータベース技術[実践]入門」を技術評論社様から献本頂きました。ありがとうございます。

mysql.jpg

このblogで張ってるAmazonのアフィリエイトリンクを通して一番売れている本は、写真奥に写っていますが、同じく松信さんによる「Linux-DB システム構築/運用入門 (DB Magazine SELECTION) 」です。

blog記事の中やWebアプリケーションエンジニアにお勧めの本を聞かれる度に紹介してきましたが、大体「前半の高可用性の所は呼び飛ばしてもいいよ」という若干残念な一言を加えていました。この「Webエンジニアのためのデータベース技術[実践]入門」ではその心配がなくなって一安心です

Webアプリケーションエンジニアがデータベースを使う上で押さえておいて欲しいポイントは以下の3つだと個人的に考えています

  • インデックスの構造についてビジュアライズして想像できること
  • データの量と性能に関する感覚
  • ハードウェアのトレンド、メモリ・SSD・CPU・クラウド

上述の「Linux-DB システム構築/運用入門」でもこれらの点が丁寧に紹介されていて、非常に参考となりました。

今回の「Webエンジニアのためのデータベース技術[実践]入門」の内容は、インデックス、テーブル設計、SQL、トランザクションといった基礎知識をおさえつつ、DeNAの現場で使われているだろう可用性、レプリケーション、ハードウェアのトレンド、性能改善手法といった実践的な事柄が網羅的に取り上げられています。

データベースの基礎と実践を押さえることができる良本として、これからオススメしていきます。

この機能欲しかったんだよねー。

CustomLogで、pipeしてrotatelogsを使ってログ分割を行う場合、

CustomLog "|/path/to/rotatelogs /path/to/log/access_log.%Y%m%d%H 7200 540"

ログファイルは、

$ ls -1
access_log.2012030116
access_log.2012030118

の様に最新のファイルが変更になります。tail -f で追いかけていた場合は、途中でファイルを手動で切り替えないとならないのでとても不便です。また、fluentdのtail pluginも利用できません。

Apache 2.4.1 のrotatelogsでは、最新のファイルに対してハードリンクを張る機能が追加されたので、とりあえず試してみました。

$ wget http://ftp.riken.jp/net/apache//httpd/httpd-2.4.1.tar.gz
$ tar zxf httpd-2.4.1.tar.gz
$ cd httpd-2.4.1

2.4.1のソースコード中にはapr,apr-utilが含まれていないのでこちらもダウンロード

$ cd srclib
$ wget http://ftp.kddilabs.jp/infosystems/apache//apr/apr-1.4.6.tar.gz \
    http://ftp.kddilabs.jp/infosystems/apache//apr/apr-util-1.4.1.tar.gz       
$ tar zxf apr-1.4.6.tar.gz
$ tar zxf apr-util-1.4.1.tar.gz  
$ mv apr-1.4.6 apr
$ mv apr-util-1.4.1 apr-util
$ cd ..

そしてbuild。

$ ./configure --prefix=/path/to/httpd24 --enable-static-rotatelogs --with-included-apr
$ make

enable-static-rotatelogs を使うと、依存の無い(コピーして使える) rotatelogs コマンドが作れるので便利です。今回は rotatelogs を試したいだけなので、make install はしません

ビルドできたらrotatelogsを試しに実行

./support/rotatelogs 
Incorrect number of arguments
Usage: ./support/rotatelogs [-v] [-l] [-L linkname] [-p prog] [-f] [-t] [-e] [-c] <logfile> {<rotation time in seconds>|<rotation size>(B|K|M|G)} [offset minutes from UTC]

Add this:

TransferLog "|./support/rotatelogs /some/where 86400"

or 

TransferLog "|./support/rotatelogs /some/where 5M"

to httpd.conf. By default, the generated name will be
<logfile>.nnnn where nnnn is the system time at which the log
nominally starts (N.B. if using a rotation time, the time will
always be a multiple of the rotation time, so you can synchronize
cron scripts with it). If <logfile> contains strftime conversion
specifications, those will be used instead. At the end of each
rotation time or when the file size is reached a new log is
started.

Options:
  -v       Verbose operation. Messages are written to stderr.
  -l       Base rotation on local time instead of UTC.
  -L path  Create hard link from current log to specified path.
  -p prog  Run specified program after opening a new log file. See below.
  -f       Force opening of log on program start.
  -t       Truncate logfile instead of rotating, tail friendly.
  -e       Echo log to stdout for further processing.
  -c       Create log even if it is empty.

The program is invoked as "[prog] <curfile> [<prevfile>]"
where <curfile> is the filename of the newly opened logfile, and
<prevfile>, if given, is the filename of the previously used logfile.

「-L」オプションがどうやらそれの様子。

次にログを rotatelogs にログを渡す perl スクリプトを作って実際に切り替わるか試してみます。

#!/usr/bin/perl

use strict;
use warnings;

open(my $fh, '|-:unix', './support/rotatelogs',
    '-L','./log/access_log.link',
    './log/access_log.%Y%m%d%H%M','60','540') or die $!;
while(1){
    print $fh "[".localtime()."] foo bar\n";
    sleep 1;
}

上記のスクリプトでは access_log.%Y%m%d%H%M がログファイルで、access_log.link をリンクとして、60秒ごとにログファイルを切り替えるようにrotatelogsを起動し、1秒ごとにログを出力します。

こいつをtest.plとして保存し実行して、

$ perl test.pl

ログを tail -F(大文字) で観察。

$ tail -F log/access_log.link
[Thu Mar  1 18:21:55 2012] foo bar
[Thu Mar  1 18:21:56 2012] foo bar
[Thu Mar  1 18:21:57 2012] foo bar
[Thu Mar  1 18:21:58 2012] foo bar
[Thu Mar  1 18:21:59 2012] foo bar
tail: `access_log.link' has been replaced;  following end of new file
[Thu Mar  1 18:22:00 2012] foo bar
[Thu Mar  1 18:22:01 2012] foo bar
[Thu Mar  1 18:22:02 2012] foo bar
[Thu Mar  1 18:22:03 2012] foo bar

59秒と00秒の間で切り替わりました。よさそう。

staticでビルドすれば、Apache 2.4系を使わなくても rotatelogsだけコピーして、2.2系のサーバに使ったりできるので、今すぐにでもrotatelogsでローカルのログを管理しつつ、fluentも使うことができるんじゃないでしょうか。