レプリケーションとは、あるデータベースから他のデータベースに複製を作ることです。
これは通常、以下のような理由から使われます。
複数のデータベースが同じ内容を持つことで、一つのサーバがダウンしても
他のサーバを使うことが可能になります。
複数のデータベースを交互にアクセスすることで、一つのサーバに掛かる負担を減らすことが出来ます。
MySQLでは「一方向レプリケーション」を採用しています。
一つのサーバを「マスタ」として機能させ、残りのサーバが「スレーブ」になります。
データの複製は「マスタ→スレーブ」という方向でのみ行われます。
そのため、データの更新は必ずマスタサーバで実行する必要があります。
マスタサーバで更新を行うと、その更新内容が全てのスレーブサーバに通知され
スレーブサーバはマスタサーバと同じ更新処理を行います。これにより、マスタとスレーブの内容が一致します。
スレーブサーバで更新を行うと、その内容はマスタサーバや他のスレーブサーバに反映されないので
整合性が崩れてしまいます。
まず、ある地点でのマスタサーバの全データをダンプします。
そして、マスタサーバで「更新ログ」を出力させます。
これは、データベースへのあらゆる更新・削除処理をログ出力したものです。
スレーブサーバ上で、先ほどマスタから取ってきたダンプデータを展開します。
この時点で、スレーブサーバの内容は(ダンプを取った段階の)マスタサーバと全く同じになります。
その後、スレーブサーバはマスタサーバで出力された更新ログを随時受け取り
その内容をスレーブサーバ上で反映させます。
これによって、スレーブサーバはマスタサーバと全く同じ内容を保つことが出来ます。
何らかの事情があってスレーブサーバを止めた場合でも、スレーブサーバを再開させれば
再びマスタサーバと全く同じ内容に戻すことが出来ます。
これは、スレーブサーバが「マスタサーバの更新ログをどこまで処理したか」という情報を持っているからです。
スレーブサーバを止めている間でも、マスタサーバの更新ログは(マスタサーバ上で)蓄積されていきます。
スレーブサーバを再開したとき、スレーブはマスタに
「スレーブサーバを止めた時間以降の更新ログを送って下さい」という命令をマスタサーバに送ります。
この命令を受けて、マスタサーバはその時間から今までの更新ログをスレーブサーバに送ります。
これにより、スレーブサーバは大急ぎで今までの遅れを取り戻すのです(笑)。
早速、レプリケーションを試してみましょう。
まず、2つのデータベースサーバを用意します。
今回は簡略化のために同一サーバ上からこれらを動かします。
MySQLのバージョンはマスタサーバが「3.23.53」、スレーブサーバが「4.0.23」です。
なお、マスタサーバで使用しているテーブルは全て「MyISAM型」です。
ISAM(古い型)やInnoDB(トランザクションテーブル)を使っている場合は、ほんの少し作業が複雑になるはずです。
レプリケーションは、専用のユーザを通して行うことが推奨されています。
作業自体は簡単ですのでそれに従いましょう。
マスタサーバ上で以下を実行します。
mysql> GRANT FILE ON *.* TO repl@'%' IDENTIFIED BY '<password>';
これにより、全てのホストから接続可能な「repl」というユーザを作成してこのユーザにFILE権限を付加します。
マスタサーバがMySQL4.0.2より新しければ、代わりに以下を実行します。
mysql> GRANT REPLICATION SLAVE ON *.* TO repl@'%' IDENTIFIED BY '<password>';
マスタサーバで更新ログ(バイナリログとも言う)を有効にします。my.cnf ファイルに以下の記述があるか確認して下さい。
[mysqld] --log-bin
無ければ、この記述を追加した上でMySQLサーバを再起動します。
現段階でのスナップショットを撮ります。
マスタサーバ上で以下を実行します。
mysql> FLUSH TABLES WITH READ LOCK;
これにより、テーブルへの書き込みを禁止します。
もちろん、心配な人はMySQLサーバをシャットダウンしても良いです。
スナップショットを撮るのは非常に簡単です。
MySQLのデータディレクトリ(通常は$MYSQL_HOME/data)の内容をまるごとアーカイブするだけです。
shell> cd $MASTER_MYSQL_HOME/data shell> tar cvf /tmp/snapshot.tar .
スナップショットを撮ったら、MySQL上で以下を実行します。
mysql> SHOW MASTER STATUS;
実行したら、画面に出力された「File」と「Position」カラムの内容をメモしておきます。
これは、現時点での更新ログファイル名とそのオフセットです。
この後スレーブサーバ上でレプリケーションを有効にする際に必要になります。
ここまで来たら、テーブルロックを解除します。
mysql> UNLOCK TABLES;
マスタサーバの my.cnf ファイルで以下の記述を確認します。
[mysqld] log-bin server-id=1
server-id は、1以上の整数であれば何でも良いです。
次に、スレーブサーバの my.cnf ファイルで以下の記述を確認します。
[mysqld] server-id=2
server-id は、先ほどマスタサーバに設定した値とは異なる値を指定する必要があります。
もし複数のスレーブサーバを起動させる場合、それぞれの server-id は全て異なる値にします。
マスタサーバで撮ったスナップショットを、スレーブサーバ上で展開します。
shell> cd $SLAVE_MYSQL_HOME/data shell> tar xvf /tmp/snapshot.tar
展開したら、スレーブサーバを起動させます。
shell> cd $SLAVE_MYSQL_HOME shell> ./safe_mysqld &
スレーブサーバのMySQLで以下を実行します。
mysql> CHANGE MASTER TO
MASTER_HOST='<master host name>',
MASTER_USER='<replication user name>',
MASTER_PASSWORD='<replication password>',
MASTER_LOG_FILE='<recorded log file name>',
MASTER_LOG_POS=<recorded log offset>;
各値は、環境に応じて書き換えて下さい。
マスタサーバのホスト名
レプリケーション処理を行うマスタサーバ上のユーザ名
レプリケーション処理を行うマスタサーバ上のユーザパスワード
先ほどメモした「File」カラムの内容
先ほどメモした「position」カラムの内容
さぁ、ここまで来たらあとはレプリケーションを開始するだけです。
スレーブサーバ上から以下を実行しましょう。
mysql> START SLAVE;
これでレプリケーションが開始されます。
試しに、マスタサーバ上で何らかのテーブルにレコードを挿入してみて下さい。
そして、スレーブサーバ上でその内容が反映されていることを確認できれば
レプリケーションは確実に動作していることになります。
レプリケーションが動いていることを確認する一番簡単な方法は、
SHOW PROCESSLIST
コマンドによりMySQLで実行中のスレッドを確認することです。
MySQLは、接続(Connection)一つにつき一つのスレッドを割り当てます。
レプリケーションのマスタサーバは、各スレーブ毎に一つのスレッドを動かしています。
スレーブサーバは、レプリケーション用のスレッドを「二つ」動かしています。
一つは「I/Oスレッド」と呼ばれるもので、マスタサーバとバイナリログ等のファイル送受信を担当します。
マスタサーバからバイナリログを受け取ったら、それをスレーブサーバ上に「リレーログ」という形で保存します。
バイナリログとリレーログは全く同じ形式ですが、その役割の違いから呼び名が分かれています。
もう一つは「SQLスレッド」と呼ばれ、レプリケーションSQL文の実行を担当します。
I/Oスレッドが作成したリレーログを読み取り、それに応じたSQL文を発行します。
リレーログはSQL文を実行すれば必要が無くなるので、SQLスレッドは状況に応じてリレーログを自動的に削除します。
スレッドを二つに分けているのは、両者の処理速度にはかなりの差があるからです。
I/Oスレッドはバイナリログを受け取ってリレーログとして保存するだけですが、
SQLスレッドはそのリレーログを解析してSQL文を発行しなければなりません。
通常、SQLスレッドはI/Oスレッドよりも多くの処理時間を要します。
スレッドを分けていれば、I/OスレッドはSQLスレッドを待たずにマスタサーバからバイナリログを受け取ることが出来ます。
スレーブがバイナリログを受け取れば、マスタサーバ上ではそのバイナリログを削除することが可能です。
もしスレッドを分けていなかったら、スレッドはSQL文の実行を待ってからバイナリログを受け取る必要があるので
バイナリログの取得に相当の時間が掛かってしまいます。
その為、マスタ側でバイナリログを削除できるタイミングも大幅に遅れてしまいます。