MySQL Shell 8.0.19 で InnoDB ReplicaSet をちょっとだけ試してみる

先日 MySQL 8.0.19 がリリースされ、あわせて MySQL Shell 8.0.19 もリリースされました。

MySQL Shell には「AdminAPI」という管理 API が実装されていますが、8.0.19 でこの AdminAPI が機能強化されて、InnoDB ReplicaSet の構築ができるようになりました。

今回、この InnoDB ReplicaSet を構築する作業を試してみたいと思います。

※おそらく近日中にもっと詳しい（マニアックな？）エントリがこちらに掲載されることでしょう。

 - **[日々の覚書](https://yoku0825.blogspot.com/)**

# MySQL InnoDB ReplicaSet とは？

簡単にいうと**「GTID と非同期レプリケーションを利用した、Master(Writer) - Slave(Reader) 型のクラスタ」**です。Master(Writer) は 1 台のみですが、Slave(Reader) は複数台の構成にすることができます。

 - **[The all new MySQL InnoDB ReplicaSet](https://mysqlserverteam.com/introducing-mysql-innodb-replicaset/)（MySQL Server Blog）**

これまで、MySQL（InnoDB）には InnoDB Cluster が存在しましたが、

 - グループレプリケーションではなく非同期レプリケーションを使う
 - InnoDB ReplicaSet のフェイルオーバは手動のみ可能（スクリプト等で自動化することは可能）
 - マルチ Master(Writer) の構成は不可

などの点が InnoDB Cluster と異なります。

要は、グループレプリケーションではない、MySQL としては「従来型」のレプリケーションを使うクラスタが簡単に構築できるようになった、ということです。

 - **[21.6 InnoDB ReplicaSet](https://dev.mysql.com/doc/refman/8.0/en/mysql-innodb-replicasets.html)（MySQL 8.0 Reference Manual）**

# 簡単に試してみる

細かい調査はプロの方々にお任せして、ここでは簡単に試してみます。

 - 構成は Master(Writer) 1 台＝サーバ名`rs-1`、Slave(Reader) 1 台＝サーバ名`rs-2`
 - InnoDB ReplicaSet を構築して、Master(Writer) で行った変更が Slave(Reader) に伝搬することを確認する
 - 今回は MySQL Router の設定は行わない

### 準備

以下のような環境を用意します。

 - OS：Linux × 2 台（サーバ名`rs-1`と`rs-2`）
 - `rs-1`・`rs-2`双方に mysql-community-server の 8.0.19 をインストールし、`mysql_secure_installation`で初期設定しておく
 - `rs-1`に MySQL Shell 8.0.19 をインストールしておく
 - `rs-1`・`rs-2`双方に`root@rs-1`ユーザを作成し、`WITH GRANT OPTION`付きで全権限を許可しておく
 - 双方の IP アドレスを /etc/hosts ファイルで名前解決できるようにしておく

### Master(Writer) インスタンスを設定する

まず MySQL Shell を起動します。

```
# mysqlsh -u root -p
Please provide the password for 'root@localhost': ************
※パスワードを入力

Save password for 'root@localhost'? [Y]es/[N]o/Ne[v]er (default No):
※[Enter]を入力
```

お好みでパスワードを保存（`Y`）／一切保存しない（`v`）を選択しても良いでしょう。

```
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 'root@localhost'
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 16 (X protocol)
Server version: 8.0.19 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
```

あらためて Master(writer) インスタンスにネットワーク接続します。

```
 MySQL  localhost:33060+ ssl  JS > \connect root@rs-1:3306
※rs-1(Master)に接続

Creating a session to 'root@rs-1:3306'
Please provide the password for 'root@rs-1:3306': ************
※パスワードを入力

Save password for 'root@rs-1:3306'? [Y]es/[N]o/Ne[v]er (default No):
※[Enter]を入力

Fetching schema names for autocompletion... Press ^C to stop.
Closing old connection...
Your MySQL connection id is 18
Server version: 8.0.19 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
```

Master(Writer) インスタンスを設定します。

```
 MySQL  rs-1:3306 ssl  JS > dba.configureReplicaSetInstance('root@rs-1:3306', {clusterAdmin: "'rsadmin'@'rs-1%'"});
※Masterインスタンスを設定

Please provide the password for 'root@rs-1:3306': ************
※パスワードを入力

Save password for 'root@rs-1:3306'? [Y]es/[N]o/Ne[v]er (default No):
※[Enter]を入力

Configuring local MySQL instance listening at port 3306 for use in an InnoDB ReplicaSet...

This instance reports its own address as rs-1:3306
Clients and other cluster members will communicate with it through this address by default. If this is not correct, the report_host MySQL system variable should be changed.
Password for new account: ************
※ReplicaSet管理アカウント（新規追加）用パスワードを入力

Confirm password: ************
※同上

NOTE: Some configuration options need to be fixed:
+--------------------------+---------------+----------------+--------------------------------------------------+
| Variable                 | Current Value | Required Value | Note                                             |
+--------------------------+---------------+----------------+--------------------------------------------------+
| enforce_gtid_consistency | OFF           | ON             | Update read-only variable and restart the server |
| gtid_mode                | OFF           | ON             | Update read-only variable and restart the server |
| server_id                | 1             | <unique ID>    | Update read-only variable and restart the server |
+--------------------------+---------------+----------------+--------------------------------------------------+
```

設定は動的適用されませんので、再起動が必要です。

```
Some variables need to be changed, but cannot be done dynamically on the server.
Do you want to perform the required configuration changes? [y/n]: y
※「y」を入力

Do you want to restart the instance after configuring it? [y/n]: y
※「y」を入力

Cluster admin user 'rsadmin'@'rs-1%' created.
Configuring instance...
The instance 'rs-1:3306' was configured to be used in an InnoDB ReplicaSet.
Restarting MySQL...
NOTE: MySQL server at rs-1:3306 was restarted.
```

### ReplicaSet を作成する

Master(Writer) インスタンス設定後は、ReplicaSet を作成します。

ここからは先ほどの MySQL Shell 操作の続きとなります（以降同じ）。

念のため Master(Writer) インスタンスに再接続します（おそらく不要だと思いますが）。

```
 MySQL  rs-1:3306 ssl  JS > \connect root@rs-1:3306
※rs-1(Master)に再接続

Creating a session to 'root@rs-1:3306'
Please provide the password for 'root@rs-1:3306': ************
※パスワードを入力

Save password for 'root@rs-1:3306'? [Y]es/[N]o/Ne[v]er (default No):
※[Enter]を入力

Fetching schema names for autocompletion... Press ^C to stop.
Closing old connection...
Your MySQL connection id is 9
Server version: 8.0.19 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
```

`example`という名前の ReplicaSet を作成します。

```
 MySQL  rs-1:3306 ssl  JS > var rs = dba.createReplicaSet("example")
※ReplicaSetを作成

A new replicaset with instance 'rs-1:3306' will be created.

* Checking MySQL instance at rs-1:3306

This instance reports its own address as rs-1:3306
rs-1:3306: Instance configuration is suitable.

* Updating metadata...

ReplicaSet object successfully created for rs-1:3306.
Use rs.addInstance() to add more asynchronously replicated instances to this replicaset and rs.status() to check its status.
```

作成できたようなので、画面の指示通り ReplicaSet のステータスを確認してみます。

```
 MySQL  rs-1:3306 ssl  JS > rs.status()
※ReplicaSetのステータスを確認

{
    "replicaSet": {
        "name": "example",
        "primary": "rs-1:3306",
        "status": "AVAILABLE",
        "statusText": "All instances available.",
        "topology": {
            "rs-1:3306": {
                "address": "rs-1:3306",
                "instanceRole": "PRIMARY",
                "mode": "R/W",
                "status": "ONLINE"
            }
        },
        "type": "ASYNC"
    }
}
```

Master(Writer) インスタンス`rs-1`の情報が表示されました。

### Slave(Reader) インスタンスを設定する

引き続き、Slave(Reader) インスタンスを設定します。

今度は Slave(Reader) インスタンスにネットワーク接続します。

```
 MySQL  rs-1:3306 ssl  JS > \connect root@rs-2:3306
※rs-2(Slave)に接続

Creating a session to 'root@rs-2:3306'
Please provide the password for 'root@rs-2:3306': ************
※パスワードを入力

Save password for 'root@rs-2:3306'? [Y]es/[N]o/Ne[v]er (default No):
※[Enter]を入力

Fetching schema names for autocompletion... Press ^C to stop.
Closing old connection...
Your MySQL connection id is 15
Server version: 8.0.19 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
```

Slave(Reader) インスタンスを設定します。

```
 MySQL  rs-2:3306 ssl  JS > dba.configureReplicaSetInstance('root@rs-2:3306', {clusterAdmin: "'rsadmin'@'rs-2%'"});
※Slaveインスタンスを設定

Please provide the password for 'root@rs-2:3306': ************
※パスワードを入力

Save password for 'root@rs-2:3306'? [Y]es/[N]o/Ne[v]er (default No):
※[Enter]を入力

Configuring MySQL instance at rs-2:3306 for use in an InnoDB ReplicaSet...

This instance reports its own address as rs-2:3306
Clients and other cluster members will communicate with it through this address by default. If this is not correct, the report_host MySQL system variable should be changed.
Password for new account: ************
※ReplicaSet管理アカウント用パスワードを入力

Confirm password: ************
※同上

NOTE: Some configuration options need to be fixed:
+--------------------------+---------------+----------------+--------------------------------------------------+
| Variable                 | Current Value | Required Value | Note                                             |
+--------------------------+---------------+----------------+--------------------------------------------------+
| enforce_gtid_consistency | OFF           | ON             | Update read-only variable and restart the server |
| gtid_mode                | OFF           | ON             | Update read-only variable and restart the server |
| server_id                | 1             | <unique ID>    | Update read-only variable and restart the server |
+--------------------------+---------------+----------------+--------------------------------------------------+
```

Master(Writer) と同様に再起動が必要です。

```
Some variables need to be changed, but cannot be done dynamically on the server.
Do you want to perform the required configuration changes? [y/n]: y
※「y」を入力

Do you want to restart the instance after configuring it? [y/n]: y
※「y」を入力

Cluster admin user 'rsadmin'@'rs-2%' created.
Configuring instance...
The instance 'rs-2:3306' was configured to be used in an InnoDB ReplicaSet.
Restarting MySQL...
NOTE: MySQL server at rs-2:3306 was restarted.
```

### Slave(Reader) インスタンスを ReplicaSet に追加する

Slave(Reader) インスタンスを ReplicaSet に追加します。

今度は Master(Writer) インスタンスにネットワーク接続します。

```
 MySQL  rs-2:3306 ssl  JS > \connect root@rs-1:3306
※rs-1(Master)に接続

Creating a session to 'root@rs-1:3306'
Please provide the password for 'root@rs-1:3306': ************
※パスワードを入力

Save password for 'root@rs-1:3306'? [Y]es/[N]o/Ne[v]er (default No):
※[Enter]を入力

Fetching schema names for autocompletion... Press ^C to stop.
Closing old connection...
Your MySQL connection id is 14
Server version: 8.0.19 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
```

Slave(Reader) インスタンス`rs-2`を追加します。

※2020/01/19 現在、公式リファレンスマニュアルの実行例が間違っている可能性があります（`addInstance()`ではパラメータとして`サーバ（インスタンス）名:ポート番号`を指定）。

```
 MySQL  rs-1:3306 ssl  JS > rs.addInstance('rs-2:3306')
※ReplicaSetにrs-2(Slave)を追加

Adding instance to the replicaset...

* Performing validation checks

This instance reports its own address as rs-2:3306
rs-2:3306: Instance configuration is suitable.

* Checking async replication topology...

* Checking transaction state of the instance...

NOTE: The target instance 'rs-2: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 'rs-2: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'.
```

Master(Writer) からのクローンを行います。

```
Please select a recovery method [C]lone/[I]ncremental recovery/[A]bort (default Clone): C
※「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: rs-2:3306 is being cloned from rs-1:3306
** Stage DROP DATA: Completed
** Clone Transfer
    FILE COPY  ####################################################  100%  Completed
    PAGE COPY  ####################################################  100%  Completed
    REDO COPY  ####################################################  100%  Completed
NOTE: rs-2:3306 is shutting down...

* Waiting for server restart... ready
* rs-2:3306 has restarted, waiting for clone to finish...
** Stage RESTART: Completed
* Clone process has finished: 59.63 MB transferred in about 1 second (~1.00 B/s)

** Configuring rs-2:3306 to replicate from rs-1:3306
** Waiting for new instance to synchronize with PRIMARY...

The instance 'rs-2:3306' was added to the replicaset and is replicating from rs-1:3306.
```

完了したようなので ReplicaSet のステータスを確認します。

```
 MySQL  rs-1:3306 ssl  JS > rs.status()
※ReplicaSetのステータスを確認

{
    "replicaSet": {
        "name": "example",
        "primary": "rs-1:3306",
        "status": "AVAILABLE",
        "statusText": "All instances available.",
        "topology": {
            "rs-1:3306": {
                "address": "rs-1:3306",
                "instanceRole": "PRIMARY",
                "mode": "R/W",
                "status": "ONLINE"
            },
            "rs-2:3306": {
                "address": "rs-2: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"
    }
}
```

今度は Slave(Reader) インスタンス`rs-2`の情報も表示されました。

MySQL Shell を終了します。

```
 MySQL  rs-1:3306 ssl  JS > \q
※MySQL Shellからログアウトする

Bye!
```

### Master(Writer) インスタンスにデータを入れてみる

ここからは、Master(Writer) → Slave(Reader) 間のレプリケーションの確認です。

まずは Master(Writer) でデータベース・テーブルを作成し、テストデータを挿入します。

```sql:
# mysql -u root -p
Enter password:
※パスワードを入力

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 25
Server version: 8.0.19 MySQL Community Server - GPL

Copyright (c) 2000, 2020, 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 '\h' for help. Type '\c' to clear the current input statement.

mysql> CREATE DATABASE example;
※データベースを作成

Query OK, 1 row affected (0.00 sec)

mysql> USE example;
※データベースを選択

Database changed
mysql> CREATE TABLE sample1 (id INT PRIMARY KEY AUTO_INCREMENT, str VARCHAR(100));
※テーブルを作成

Query OK, 0 rows affected (0.02 sec)

mysql> INSERT INTO sample1 SET str = 'line 1';
※データを挿入

Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO sample1 SET str = 'line 2';
※同上

Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO sample1 SET str = 'line 3';
※同上

Query OK, 1 row affected (0.00 sec)
```

データベース`example`にテーブル`sample1`を作成し、3 行のデータを挿入しました。

### Slave(Reader) インスタンスで確認してみる

Slave(Reader) 側で挿入データの到達を確認します。

```sql:
# mysql -u root -p
Enter password:
※パスワードを入力

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 13
Server version: 8.0.19 MySQL Community Server - GPL

Copyright (c) 2000, 2020, 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 '\h' for help. Type '\c' to clear the current input statement.

mysql> SHOW DATABASES;
※データベースを確認

+-------------------------------+
| Database                      |
+-------------------------------+
| example                       |
| information_schema            |
| mysql                         |
| mysql_innodb_cluster_metadata |
| performance_schema            |
| sys                           |
+-------------------------------+
6 rows in set (0.00 sec)

mysql> USE example;
※データベースを選択

Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> SHOW TABLES;
※テーブルを確認

+-------------------+
| Tables_in_example |
+-------------------+
| sample1           |
+-------------------+
1 row in set (0.01 sec)

mysql> SELECT * FROM sample1;
※データを確認

+----+--------+
| id | str    |
+----+--------+
|  1 | line 1 |
|  2 | line 2 |
|  3 | line 3 |
+----+--------+
3 rows in set (0.00 sec)
```

確認できました。

# おわりに

以前は大掛かりな作業が必要だった非同期レプリケーションクラスタ構築ですが、比較的簡単な操作で ReplicaSet として構築できることがわかりました。

また、今回は省略しましたが、AdminAPI では MySQL Router の初期設定も簡素化することが可能です。

---

 - **[Qiitaに投稿したMySQL 8.0関連記事](https://hmatsu47.hatenablog.com/qiita_mysql80)**
 - **[MySQL 8.0 の薄い本](https://github.com/hmatsu47/mysql80_no_usui_hon)（無料で配布中！）**