はじめに
レプリケーションとは、2台以上のDBMSにデータを分けて配置する構成であり、Master/Slaveの構成で成り立っています。
- Master DBMS : データの登録、修正、削除要求時にバイナリログを生成し、Slaveサーバーに送信します。(高い同時性が求められるトランザクション部分を担当します)
- Slave DBMS : Master DBMSから受信したバイナリログを利用して、データに反映します。(データの要求時に使用され、読み取り専用です)
活用方法
- Master DBMSに障害が発生した場合、代わりにSlave DBMSを使用することが可能です(フェイルオーバー)。
- DBMSの負荷分散を目的とし、ユーザーの急増により1台のDBサーバーが対応しきれなくなった場合に使用します。
注意事項
- 互換性を確保するために、レプリケーションに使用するMySQLのバージョンは同一にしてください。
- バージョンが異なる場合、Slaveを上位バージョンで設定してください。
- レプリケーションの起動時には、MasterからSlaveの順で起動してください。
Dockerを活用したMySQLレプリケーション
ディレクトリの構成は以下の通りです。
master/Dockerfile
FROM mysql:5.7
ADD ./master/my.cnf /etc/mysql/my.cnf
master/my.cnf
[mysqld]
log_bin = mysql-bin
server_id = 10
default_authentication_plugin=mysql_native_password
slave/Dockerfile
FROM mysql:5.7
ADD ./slave/my.cnf /etc/mysql/my.cnf
slave/my.cnf
[mysqld]
log_bin = mysql-bin
server_id = 11
relay_log = /var/lib/mysql/mysql-relay-bin
log_slave_updates = 1
read_only = 1
default_authentication_plugin=mysql_native_password
docker-compose.yml
version: "3"
services:
db-master:
build:
context: ./
dockerfile: master/Dockerfile
restart: always
environment:
MYSQL_DATABASE: 'db'
MYSQL_USER: 'user'
MYSQL_PASSWORD: 'password'
MYSQL_ROOT_PASSWORD: 'password'
ports:
- '3306:3306'
# Where our data will be persisted
volumes:
- my-db-master:/var/lib/mysql
- my-db-master:/var/lib/mysql-files
networks:
- net-mysql
db-slave:
build:
context: ./
dockerfile: slave/Dockerfile
restart: always
environment:
MYSQL_DATABASE: 'db'
MYSQL_USER: 'user'
MYSQL_PASSWORD: 'password'
MYSQL_ROOT_PASSWORD: 'password'
ports:
- '3307:3306'
# Where our data will be persisted
volumes:
- my-db-slave:/var/lib/mysql
- my-db-slave:/var/lib/mysql-files
networks:
- net-mysql
# Names our volume
volumes:
my-db-master:
my-db-slave:
networks:
net-mysql:
driver: bridge
実行手順
- master-slaveの2つのサーバーを起動します。
docker-compose up -d
- internal IPを探します。
- まず、ネットワークIDを見つけます。
❯ docker network ls
NETWORK ID NAME DRIVER SCOPE
c936b9b7cc42 repli_net-mysql bridge local
- docker inspect {NETWORK ID}を使用して、IPv4Addressに該当する値を確認します。
❯ docker inspect c936b9b7cc42
[
{
"Name": "repli_net-mysql",
"Id": "c936b9b7cc4214b978aa64d7397d218c0fe62dd76389971ef2134ea8a8560765",
"Created": "2022-01-11T14:36:21.9461848Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.19.0.0/16",
"Gateway": "172.19.0.1"
}
]
},
"Internal": false,
"Attachable": true,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"31b2a62702a6249c666076d7182a34c30ac3e2939ea6924b84b237fb6a57e915": {
"Name": "repli_db-slave_1",
"EndpointID": "afbd9ee57c687c50fab1b39be1b97919229fff04fc418269612c058cbff06554",
"MacAddress": "02:42:ac:13:00:03",
"IPv4Address": "172.19.0.3/16",
"IPv6Address": ""
},
"43bb2e42864eaa10c730ae53c01ab9817a49c5c34db2eba62ce3e14f92779cdb": {
"Name": "repli_db-master_1",
"EndpointID": "6e2d8cc63e3fc5d6322ab2de3d6bf414ee49a885ded7e26d66b4afb9188e6090",
"MacAddress": "02:42:ac:13:00:02",
"IPv4Address": "172.19.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {
"com.docker.compose.network": "net-mysql",
"com.docker.compose.project": "repli",
"com.docker.compose.version": "1.29.0"
}
}
]
- SlaveからMasterに接続します。
実際、この部分で多少苦労しました。直面したエラーは「Error 1236 - 'Could not find first log file name in binary log index file'」というものでしたが、slaveサーバーでMASTER_LOG_FILEを設定する際に、masterサーバーに存在するログファイルを基準に設定する必要がありました。
これを確認するためには、masterサーバーで以下のコマンドを入力します。
mysql> SHOW BINARY LOGS;
+------------------+-----------+
| Log_name | File_size |
+------------------+-----------+
| mysql-bin.000001 | 177 |
| mysql-bin.000002 | 3072319 |
| mysql-bin.000003 | 177 |
| mysql-bin.000004 | 154 |
+------------------+-----------+
存在しないログファイルインデックスを選択すると、エラーが発生します。
その後、以下のように設定を合わせました。結論として、Slave_IO_Running: Yes / Slave_SQL_Running: Yesと表示されれば同期が完了したことになります。
mysql> CHANGE MASTER TO MASTER_HOST='172.19.0.3', MASTER_USER='root', MASTER_PASSWORD='password', MASTER_LOG_FILE='mysql-bin.000003',MASTER_LOG_POS=0;
Query OK, 0 rows affected, 2 warnings (0.00 sec)
mysql> stop slave;
Query OK, 0 rows affected (0.00 sec)
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)
mysql> show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 172.19.0.3
Master_User: root
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000004
Read_Master_Log_Pos: 154
Relay_Log_File: mysql-relay-bin.000004
Relay_Log_Pos: 367
Relay_Master_Log_File: mysql-bin.000004
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: 154
Relay_Log_Space: 787
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: 0
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: 10
Master_UUID: fd513db6-72eb-11ec-890b-0242ac130002
Master_Info_File: /var/lib/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
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
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)
テスト
最終的にテストを行うために、DBとテーブルにデータを直接投入してみました。
Master DB
mysql> use bootex;
Database changed
mysql> create table Member (ID int not null primary key, name varchar
(100));
Query OK, 0 rows affected (0.01 sec)
mysql> insert into Member(id, name) values (1,"song");
Query OK, 1 row affected (0.01 sec)
Slave DB
mysql> show tables;
+------------------+
| Tables_in_bootex |
+------------------+
| Member |
+------------------+
1 row in set (0.00 sec)
mysql> select * from Member;
+----+------+
| ID | name |
+----+------+
| 1 | song |
+----+------+
1 row in set (0.00 sec)