Edited at

1台のサーバでMySQLのレプリケーション


概要

2016年11月8日のお話。

この設定って何のために必要なのかといいますと、レプリケーションを前提にしたWEBアプリの開発環境を作るためです。

レプリケーションってslaveへのデータ反映にタイムラグがありまして、そこを考慮せずにアプリを作ってしまうとほぼ間違いなくアプリがバグるんですね。

そういうことにならないために、レプリケーションを行っている開発環境でアプリ開発を行うことで、そういった不具合を未然に防ぎましょうというお話しです。

今回はmysqld_multiを利用します。


ハマリポイント

利用したMySQLのバージョンは5.6.34だったんですが、これちょっとアレなバグがあったみたいで結構ハマってしまいました。

https://bugs.mysql.com/bug.php?id=77227

えぇぇ~~~ って感じです。


作業手順

上記のバグは後で説明しますが、とりあえず手順を残します。

現在動いているMySQLは一旦落としておき、MySQLのmysqld_multiの設定をします。

# /etc/init.d/mysqld stop

# vi /etc/my.cnf

設定内容はこんな感じ。

[mysqld]

#datadir=/var/lib/mysql ←コメントにする
#socket=/var/lib/mysql/mysql.sock ←コメントにする
・・・

# 次の設定を追記
[mysqld_multi]
mysqld = /usr/bin/mysqld_safe
mysqladmin = /usr/bin/mysqladmin
user = multi_admin ←SHUTDOWNの権限を持っているMySQLのユーザーです
password = enter_your_password

[mysqld1]
port = 3306
datadir = /var/lib/mysql1
socket = /var/lib/mysql1/mysql.sock
pid-file = /var/lib/mysql1/mysql.pid
log-error = /var/log/mysqld1.log

[mysqld2]
port = 3307
datadir = /var/lib/mysql2
socket = /var/lib/mysql2/mysql.sock
pid-file = /var/lib/mysql2/mysql.pid
log-error = /var/log/mysqld2.log

それぞれmysqlのデータディレクトリとアクセスするときのportを設定します。

かなり直感的でわかりやすいです。

まだデータディレクトリが存在しないので、それを作成しておきます。

# mysql_install_db --datadir=/var/lib/mysql1 --user=mysql

# mysql_install_db --datadir=/var/lib/mysql2 --user=mysql
# chown -R mysql:mysql /var/lib/mysql1 /var/lib/mysql2

次のコマンドでスタートできます。

# mysqld_multi start

現在のMySQLの状態を確認するには次のコマンドでOK。

# mysqld_multi report

Reporting MySQL servers
MySQL server from group: mysqld1 is running
MySQL server from group: mysqld2 is running

ちなみに、それぞれ別々にスタートすることも可能。

# mysqld_multi start 1

# mysqld_multi start 2

ここでいう1とか2というのは、my.cnfに書いた[mysqld1]とか[mysqld2]とかの数字部分と一致しています。

ちょっと釈然としませんが、そういう仕様みたいです。

単にこれだけで動くには動くんです。

とりあえずMySQLにアクセスしてmy.cnfの[mysqld_multi]で設定したユーザーを追加。

ちなみにホスト名localhostではダメです、詳しくはG先生が理由を教えてくれます。

またportを3306に指定することで[mysqld1]へのアクセスになります。

# mysql -u root -h 127.0.0.1 -P 3306

GRANT SHUTDOWN ON *.* TO 'multi_admin'@'localhost' IDENTIFIED BY 'enter_your_password';

slaveの方でも同じ設定を行っておきます。

ついでにrootのパスワードも付けとくといいと思います。

が、ここでハマったのはstopができないという謎。

さっき設定したmulti_adminというユーザがmysqladminからストップを実行するんですが、それができないんですね。

mysqld_multiの仕様では、停止は次のコマンドになります。

# mysqld_multi stop ←全部止めるとき

# mysqld_multi stop 1 ←mysql1だけ止めるとき
# mysqld_multi stop 2 ←mysql2だけ止めるとき

そして確認・・・。

# mysqld_multi report

Reporting MySQL servers
MySQL server from group: mysqld1 is running
MySQL server from group: mysqld2 is running

止まってないし。

色々調べてみたところ、mysqld_multiの中を覗いてデバッグ出力してみたら、パスワードが’*****’という文字列になってるという、とてもアレなバグが発覚しました・・・。

これが冒頭で貼ったURLのバグです。

最初訳が分からずに色々悩んでしまったんですが、とても簡単な解決方法がありまして、my.cnfの設定を次のようにするだけでOKです。

[mysqld_multi]

mysqld = /usr/bin/mysqld_safe
mysqladmin = /usr/bin/mysqladmin
user = multi_admin
#password = enter_your_password ←passwordという項目名をやめて
pass = enter_your_password ←passという項目名にするだけ

で、stopを試します。

# mysqld_multi stop

# mysqld_multi report

Reporting MySQL servers
MySQL server from group: mysqld1 is not running
MySQL server from group: mysqld2 is not running

ですよねっ!

これが分かるまでに数時間費やしてしまいました。

次はレプリケーションの設定です。

またmy.cnfの出番です。

# vi /etc/my.cnf

[mysqld_multi]
mysqld = /usr/bin/mysqld_safe
mysqladmin = /usr/bin/mysqladmin
user = multi_admin
pass = enter_your_password

[mysqld1]
port = 3306
datadir = /var/lib/mysql1
socket = /var/lib/mysql1/mysql.sock
pid-file = /var/lib/mysql1/mysql.pid
log-error = /var/log/mysqld1.log

# masterの設定を追加
server-id = 1001
log-bin = mysql-bin
expire-logs-days = 7

[mysqld2]
port = 3307
datadir = /var/lib/mysql2
socket = /var/lib/mysql2/mysql.sock
pid-file = /var/lib/mysql2/mysql.pid
log-error = /var/log/mysqld2.log

# slaveの設定を追加
server-id = 1002
master-host = localhost
master-port = 3306
master-user = root
master-password = password
read_only

で、mysqld_multiで起動

# mysqld_multi start

# mysqld_multi report

Reporting MySQL servers
MySQL server from group: mysqld1 is running
MySQL server from group: mysqld2 is not running

slaveが動いてないし!

色々調べてみたところ、どうもMySQL5.5以降はmy.cnfにmaster-hostとか書けなくなっていまして、CHANGE MASTER TOを使わなければいけないようです。

なので単にmaster-*シリーズの設定を全部削除

# vi /etc/my.cnf

[mysqld_multi]
mysqld = /usr/bin/mysqld_safe
mysqladmin = /usr/bin/mysqladmin
user = multi_admin
pass = enter_your_password

[mysqld1]
port = 3306
datadir = /var/lib/mysql1
socket = /var/lib/mysql1/mysql.sock
pid-file = /var/lib/mysql1/mysql.pid
log-error = /var/log/mysqld1.log

# masterの設定を追加
server-id = 1001
log-bin = mysql-bin
expire-logs-days = 7

[mysqld2]
port = 3307
datadir = /var/lib/mysql2
socket = /var/lib/mysql2/mysql.sock
pid-file = /var/lib/mysql2/mysql.pid
log-error = /var/log/mysqld2.log

# slaveの設定を追加
server-id = 1002
read_only

で、もう一度mysqld_multiで起動

# mysqld_multi start 2

# mysqld_multi report

Reporting MySQL servers
MySQL server from group: mysqld1 is running
MySQL server from group: mysqld2 is running

ふむ、良さそうですね。

まずはmasterの確認

# mysql -u root -h 127.0.0.1 -P 3306 -p

mysql> SHOW MASTER STATUS\G
*************************** 1. row ***************************
File: mysql-bin.000002
Position: 780
Binlog_Do_DB:
Binlog_Ignore_DB:
1 row in set (0.00 sec)

ここでは単にmasterとして動いてることが確認できればいいです。

CHANGE MASTER TOしてスレーブの状態を確認します。

# mysql -u root -h 127.0.0.1 -P 3307 -p

mysql> CHANGE MASTER TO
MASTER_HOST = 'localhost',
MASTER_USER = 'root',
MASTER_PASSWORD = 'password',
MASTER_PORT = 3306;
Query OK, 0 rows affected (0.02 sec)

mysql> SHOW SLAVE STATUS\G;
*************************** 1. row ***************************
Slave_IO_State:
Master_Host: localhost
Master_User: root
Master_Port: 3306
Connect_Retry: 60
Master_Log_File:
Read_Master_Log_Pos: 4
Relay_Log_File: mysql-relay-bin.000001
Relay_Log_Pos: 4
Relay_Master_Log_File:
Slave_IO_Running: No
Slave_SQL_Running: No
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 0
Relay_Log_Space: 120
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 0
Master_UUID:
Master_Info_File: /var/lib/mysql2/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State:
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
1 row in set (0.00 sec)

とりあえずは何もなし。

START SLAVE して状態を確認します。

mysql> START SLAVE;

Query OK, 0 rows affected (0.00 sec)

mysql> SHOW SLAVE STATUS\G
*************************** 1. row ***************************
Slave_IO_State: Queueing master event to the relay log
Master_Host: localhost
Master_User: root
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000009
Read_Master_Log_Pos: 11995350
Relay_Log_File: mysql-relay-bin.000010
Relay_Log_Pos: 473614
Relay_Master_Log_File: mysql-bin.000009
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 473451
Relay_Log_Space: 12043325
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 93849
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1001
Master_UUID: b2e1a379-a554-11e6-88f4-9ca3ba01ee48
Master_Info_File: /var/lib/mysql2/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Reading event from the relay log
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
1 row in set (0.00 sec)

Slave_IO_RunningとSlave_SQL_RunningがYesになってて特にエラーが出てないことを確認して終了です!