MySQL 8.0.19 new features roundup! の記事でも触れさせて頂きましたが、MySQL のバージョン 8.0.19 で、InnoDB ReplicaSet 機能が追加されました。
これにより、これまで何度か当ブログでも紹介させて頂いている InnoDB Cluster のように MySQL Shell を使用して、容易に MySQL Router +非同期レプリケーション環境が構築できるようになりました。
今回は、InnoDB ReplicaSet の構築と動作を簡単に確認したいと思います。
1.サーバ構成
サーバ用途 | ホスト名 | IP |
---|---|---|
MySQL Router サーバ | router-server | 192.168.33.131 |
レプリケーション・マスターサーバ | master-server | 192.168.33.132 |
レプリケーション・スレーブサーバ | slave-server | 192.168.33.133 |
検証に使用した各サーバのOSは、CentOS 7.5 です。
2.構築
2-1. hosts ファイルの設定
各サーバのhostsファイルに今回使用するサーバの情報を追加します。
1 2 3 4 5 |
# cat <<EOF >> /etc/hosts 192.168.33.131 router-server 192.168.33.132 master-server 192.168.33.133 slave-server EOF |
2-2. MySQL Server のインストールと設定
まずは、MySQL Community Server をyumインストールします。
1 2 |
# yum install -y https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm # yum install -y mysql-community-server |
firewalld が起動している場合は使用するポートを解放します。
1 2 3 |
# firewall-cmd --add-port=3306/tcp # firewall-cmd --add-port=33060/tcp # firewall-cmd --runtime-to-permanent |
my.cnf を新たに作成し、最低限の設定をしておきます。
server_id
の設定はスレーブサーバでは server_id=20
と設定します。
1 2 3 4 5 6 7 |
# cp /etc/my.cnf{,.org} # cat <<EOF > /etc/my.cnf [mysqld] server_id=10 gtid_mode=ON enforce_gtid_consistency=ON EOF |
MySQL サーバを起動します。
1 |
# systemctl start mysqld |
root ユーザのパスワードを設定し、MySQL Shell から操作するユーザを作成します。
1 2 3 4 5 |
# mysql -u root -p$(grep "temporary password" /var/log/messages | tail -n 1 | rev | cut -f 1 -d " " | rev) mysql> SET PASSWORD = 'Password1!'; mysql> CREATE USER shelluser@'192.168.33.%' IDENTIFIED BY 'Password1!'; mysql> GRANT ALL ON *.* TO shelluser@'192.168.33.%' WITH GRANT OPTION; mysql> \q |
2-3. MySQL Shell と MySQL Router のインストール
MySQL Router, MySQL Shell, MySQL Client をインストールします。
1 2 |
# yum install -y https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm # yum install -y mysql-{router,shell,community-client} |
2-4. InnoDB ReplicaSet の作成
ここまでの作業で、InnoDB ReplicaSet を作成する準備が整ったので、MySQL Shell を使用して InnoDB ReplicaSet を作成します。
まずは、MySQL Shell で レプリケーションのマスターサーバに接続します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# mysqlsh shelluser@master-server Please provide the password for 'shelluser@master-server': ********** Save password for 'shelluser@master-server'? [Y]es/[N]o/Ne[v]er (default No): Y MySQL Shell 8.0.19 Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type '\help' or '\?' for help; '\quit' to exit. Creating a session to 'shelluser@master-server' Fetching schema names for autocompletion... Press ^C to stop. Your MySQL connection id is 10 (X protocol) Server version: 8.0.19 MySQL Community Server - GPL No default schema selected; type \use <schema> to set one. MySQL master-server:33060+ ssl JS > |
dba.createReplicaSet()
で ReplicaSet を作成します。
1 2 3 4 5 6 7 8 9 10 11 12 |
MySQL master-server:33060+ ssl JS > var rs = dba.createReplicaSet("example") A new replicaset with instance 'master-server:3306' will be created. * Checking MySQL instance at master-server:3306 This instance reports its own address as master-server:3306 master-server:3306: Instance configuration is suitable. * Updating metadata... ReplicaSet object successfully created for master-server:3306. Use rs.addInstance() to add more asynchronously replicated instances to this replicaset and rs.status() to check its status. |
<ReplicaSetオブジェクト>.status()
で確認します。
topology にマスターサーバの情報が表示され、"status": "ONLINE"
であることを確認します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
MySQL master-server:33060+ ssl JS > rs.status() { "replicaSet": { "name": "example", "primary": "master-server:3306", "status": "AVAILABLE", "statusText": "All instances available.", "topology": { "master-server:3306": { "address": "master-server:3306", "instanceRole": "PRIMARY", "mode": "R/W", "status": "ONLINE" } }, "type": "ASYNC" } } |
次にスレーブサーバを <ReplicaSetオブジェクト>.addInstance()
で追加します。
引数には、MySQL Shell用のユーザに作成したユーザとスレーブサーバホスト名を指定しています。
また、リカバリ方法については、当ブログでもご紹介したCLONEプラグインを選択しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
MySQL master-server:33060+ ssl JS > rs.addInstance('shelluser@slave-server') Adding instance to the replicaset... * Performing validation checks This instance reports its own address as slave-server:3306 slave-server:3306: Instance configuration is suitable. * Checking async replication topology... * Checking transaction state of the instance... NOTE: The target instance 'slave-server:3306' has not been pre-provisioned (GTID set is empty). The Shell is unable to decide whether replication can completely recover its state. The safest and most convenient way to provision a new instance is through automatic clone provisioning, which will completely overwrite the state of 'slave-server:3306' with a physical snapshot from an existing replicaset member. To use this method by default, set the 'recoveryMethod' option to 'clone'. WARNING: It should be safe to rely on replication to incrementally recover the state of the new instance if you are sure all updates ever executed in the replicaset were done with GTIDs enabled, there are no purged transactions and the new instance contains the same GTID set as the replicaset or a subset of it. To use this method by default, set the 'recoveryMethod' option to 'incremental'. Please select a recovery method [C]lone/[I]ncremental recovery/[A]bort (default Clone): C * Updating topology Waiting for clone process of the new member to complete. Press ^C to abort the operation. * Waiting for clone to finish... NOTE: slave-server:3306 is being cloned from master-server:3306 ** Stage DROP DATA: Completed ** Clone Transfer FILE COPY ############################################################ 100% Completed PAGE COPY ############################################################ 100% Completed REDO COPY ############################################################ 100% Completed ** Stage RECOVERY: \ NOTE: slave-server:3306 is shutting down... * Waiting for server restart... ready * slave-server:3306 has restarted, waiting for clone to finish... * Clone process has finished: 59.64 MB transferred in about 1 second (~1.00 B/s) ** Configuring slave-server:3306 to replicate from master-server:3306 ** Waiting for new instance to synchronize with PRIMARY... The instance 'slave-server:3306' was added to the replicaset and is replicating from master-server:3306. |
再度、<ReplicaSetオブジェクト>.status()
で確認します。
topology にスレーブサーバの情報が設定され、"status": "ONLINE"
であることを確認します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
MySQL master-server:33060+ ssl JS > rs.status() { "replicaSet": { "name": "example", "primary": "master-server:3306", "status": "AVAILABLE", "statusText": "All instances available.", "topology": { "master-server:3306": { "address": "master-server:3306", "instanceRole": "PRIMARY", "mode": "R/W", "status": "ONLINE" }, "slave-server:3306": { "address": "slave-server:3306", "instanceRole": "SECONDARY", "mode": "R/O", "replication": { "applierStatus": "APPLIED_ALL", "applierThreadState": "Slave has read all relay log; waiting for more updates", "receiverStatus": "ON", "receiverThreadState": "Waiting for master to send event", "replicationLag": null }, "status": "ONLINE" } }, "type": "ASYNC" } } |
余談ではありますが、 mysql_innodb_cluster_metadata.v2_ar_members
テーブルからも、どのサーバがマスターサーバ(PRIMARY)もしくはスレーブサーバ(SECONDARY)であるかは確認できそうです。
1 2 3 4 5 6 7 8 9 |
MySQL master-server:33060+ ssl JS > \sql Switching to SQL mode... Commands end with ; MySQL master-server:33060+ ssl SQL > SELECT label, member_role FROM mysql_innodb_cluster_metadata.v2_ar_members; +--------------------+-------------+ | label | member_role | +--------------------+-------------+ | master-server:3306 | PRIMARY | | slave-server:3306 | SECONDARY | +--------------------+-------------+ |
2-5. MySQL Router の bootstrap
最後に MySQL Router を --bootstrap
オプションを指定して実行し、サービス起動を行います。
1 2 |
# mysqlrouter --bootstrap shelluser@master-server --user=mysqlrouter # systemctl start mysqlrouter |
これまでの手順で MySQL Router + マスター・スレーブ構成の非同期レプリケーション環境が構築できました。
非常に簡単な手順で構築が可能であることをご確認頂けたのではないでしょうか。
3.確認
3-1. MySQL Router の 接続の振り分け
まずは、MySQL Router の設定ファイルを確認しておきます。
アプリケーションから接続するポート番号は、[routing:XXX_rw]
セクションの bind_port
の設定値がマスターサーバ、[routing:XXX_ro]
セクションの bind_port
の設定値がスレーブサーバとなります。
今回はローカルアクセスするので、firewalld の設定は行いませんが、他ホストからアクセスする場合には、これらのポートを解放する必要があります。
また、[metadata_cache:XXX]
セクションに見慣れない cluster_type=rs という設定がありますが、MySQL Rouer 8.0.19 から追加された設定で、InnoDB ReplicaSet の場合は rs
InnoDB Cluster の場合は gr
とするようです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# cat /etc/mysqlrouter/mysqlrouter.conf # File automatically generated during MySQL Router bootstrap [DEFAULT] name=system user=mysqlrouter keyring_path=/var/lib/mysqlrouter/keyring master_key_path=/etc/mysqlrouter/mysqlrouter.key connect_timeout=15 read_timeout=30 dynamic_state=/var/lib/mysqlrouter/state.json [logger] level = INFO [metadata_cache:example] cluster_type=rs router_id=1 user=mysql_router1_zwd4rpt1ss2d metadata_cluster=example ttl=0.5 [routing:example_rw] bind_address=0.0.0.0 bind_port=6446 destinations=metadata-cache://example/?role=PRIMARY routing_strategy=first-available protocol=classic [routing:example_ro] bind_address=0.0.0.0 bind_port=6447 destinations=metadata-cache://example/?role=SECONDARY routing_strategy=round-robin-with-fallback protocol=classic [routing:example_x_rw] bind_address=0.0.0.0 bind_port=64460 destinations=metadata-cache://example/?role=PRIMARY routing_strategy=first-available protocol=x [routing:example_x_ro] bind_address=0.0.0.0 bind_port=64470 destinations=metadata-cache://example/?role=SECONDARY routing_strategy=round-robin-with-fallback protocol=x |
それでは、MySQL Client でマスターサーバに接続してみます。
接続先ポート番号に 6446 を指定します。
1 2 3 4 5 6 7 |
# mysql -u shelluser -p -h 127.0.0.1 -P 6446 -e "SELECT @@hostname" Enter password: +---------------+ | @@hostname | +---------------+ | master-server | +---------------+ |
スレーブサーバにも接続してみます。
接続先ポート番号に 6447 を指定します。
1 2 3 4 5 6 7 |
# mysql -u shelluser -p -h 127.0.0.1 -P 6447 -e "SELECT @@hostname" Enter password: +--------------+ | @@hostname | +--------------+ | slave-server | +--------------+ |
想定通りに振り分けられていることが確認できました。
3-2. レプリケーション確認
基本的な動作確認ではありますが、マスターサーバの更新内容がスレーブサーバへ反映されるかも確認してみます。
マスターサーバでデータベースを作成します。
1 |
# mysql -u shelluser -p -h 127.0.0.1 -P 6446 -e "CREATE DATABASE example;" |
スレーブサーバへアクセスして、作成したデータベースが存在することが確認できます。
1 2 3 4 5 6 7 8 9 10 11 12 |
# mysql -u shelluser -p -h 127.0.0.1 -P 6447 -e "SHOW DATABASES;" Enter password: +-------------------------------+ | Database | +-------------------------------+ | example | | information_schema | | mysql | | mysql_innodb_cluster_metadata | | performance_schema | | sys | +-------------------------------+ |
3-3. スレーブサーバへの更新
スレーブサーバに誤って更新クエリを発行してしまった場合には、エラーとなります。
スレーブサーバでは、--super-read-only
が設定され、直接更新することはできません。
1 2 3 |
# mysql -u shelluser -p -h 127.0.0.1 -P 6447 -e "CREATE DATABASE example2;" Enter password: ERROR 1290 (HY000) at line 1: The MySQL server is running with the --super-read-only option so it cannot execute this statement |
3-4. スレーブサーバダウン時の振り分け
スレーブサーバのMySQLサービスを事前に停止した後に、スレーブサーバへアクセスしてみます。
スレーブサーバへアクセスするポート(6447)へ接続したはずが、マスターサーバへ振り分けられました。
もし、スレーブサーバがダウンした場合には、スレーブサーバへのアクセスをマスターサーバへ振り分けるという InnoDB ReplicaSet の一番喜ばしい機能であるように思います。
1 2 3 4 5 6 7 |
# mysql -u shelluser -p -h 127.0.0.1 -P 6447 -e "select @@hostname" Enter password: +---------------+ | @@hostname | +---------------+ | master-server | +---------------+ |
3-5. マスターサーバダウン時の振り分け
今度はマスターサーバのMySQLサービスを事前に停止した後に、マスターサーバへアクセスしてみます。
残念ながら、InnoDB ReplicaSet はフェールオーバー機能はないので、マスターサーバへのアクセスはエラーとなります。
(この状態でもスレーブサーバへのアクセスは可能です。)
1 2 3 |
# mysql -u shelluser -p -h 127.0.0.1 -P 6446 -e "select @@hostname" Enter password: ERROR 2003 (HY000): Can't connect to remote MySQL server for client connected to '0.0.0.0:6446' |
まとめ
MySQL Router+非同期レプリケーションの環境が非常に簡単に構築できました。
MySQL Routerを使用する事で、スレーブサーバへのアクセスが障害時にマスターサーバへ切り替わるのも需要のある機能であると思います。
また、スレーブサーバを追加したい場合、MySQL Shell で <ReplicaSetオブジェクト>.addInstance()
を実行すれば、MySQL Router の設定変更も必要なく、参照アクセスが自動的に追加サーバへも割り振られるので、スレーブサーバの追加も簡単に行えます。
しかしながら、リファレンスの InnoDB ReplicaSet の制限にも記載されていますが、以下の観点からも InnoDB ReplicaSet よりは、InnoDB Cluster の使用を推奨されています。
- マスターノードの障害時には、フェールオーバーはされない。
- クラッシュセーフではない。
とはいえ要件次第ではありますが、高可用性は求めてなく、アクセスの分散だけを考慮したい場合等には、一考の価値がある機能ではないでしょうか。
今後も、より便利な機能がでてくる事を楽しみにしたいと思います。