お元気そうで残念です

仕事とか趣味のメモを残します

MySQL 5.7.23 のGroup Replicationでデータに差分がある状態での復旧を確認

前回はデータ更新が無い状態でNodeの復旧について確認しました。 今回はデータ更新がある状態でのNode復旧について確認します。

確認項目は以下です。

  • すべてのNodeがONLINE状態でデータを追加してSecondary Nodeで見れるか
  • Secondary Nodeでデータ追加ができないか
  • Secondary Nodeを止めた状態でデータを更新系query(insert update delete)を走らせて、Nodeを復旧してちゃんと更新されるか
  • Primary Nodeで更新系queryを走らせた後、同期が完了する前にPrimary Nodeを停止させて、Primary Nodeが切り替わった後に旧Primary Nodeを復旧させてみてデータが同期されるか
  • binlogを意図的に欠損させてみる

結論としては、binlogの欠損がなければそのうち同期される。
欠損があれば、Master-Slave Replicationと同様にdumpとって復旧する必要がある。
という感じでした。

すべてのNodeがONLINE状態でデータを追加してSecondary Nodeで見れるか

これは問題ないでしょうが確認しておきましょう。

Primary Nodeであるnode1でデータを追加します。

[node1] $ sudo mysql -u root
mysql> show databases;
+-------------------------------+
| Database                      |
+-------------------------------+
| information_schema            |
| mysql                         |
| mysql_innodb_cluster_metadata |
| performance_schema            |
| sys                           |
+-------------------------------+
5 rows in set (0.00 sec)

mysql> CREATE DATABASE test;
Query OK, 1 row affected (0.00 sec)

mysql> CREATE TABLE test.t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL);
Query OK, 0 rows affected (0.03 sec)

mysql> INSERT INTO test.t1 VALUES (1, 'Yui');
Query OK, 1 row affected (0.01 sec)

Secondary Nodeであるnode2でデータを確認してみましょう。
同期できているようですね。

[node2] $ sudo mysql -u root
mysql> show databases;
+-------------------------------+
| Database                      |
+-------------------------------+
| information_schema            |
| mysql                         |
| mysql_innodb_cluster_metadata |
| performance_schema            |
| sys                           |
| test                          |
+-------------------------------+
6 rows in set (0.00 sec)

mysql> show tables from test;
+----------------+
| Tables_in_test |
+----------------+
| t1             |
+----------------+
1 row in set (0.00 sec)

mysql> select * from test.t1;
+----+------+
| c1 | c2   |
+----+------+
|  1 | Yui |
+----+------+
1 row in set (0.00 sec)

Secondary Nodeでデータ追加ができないか

これもできていて当たり前ですが確認してみましょう。
Secondary Nodeではsuper read only modeがONになっているので実行できませんね。

[node2] $ sudo mysql -u root
mysql> CREATE DATABASE hoge;
ERROR 1290 (HY000): The MySQL server is running with the --super-read-only option so it cannot execute this statement

試しにOFFにしてみましょう。
追加できちゃいましたね?

mysql> set global super_read_only=off;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE DATABASE hoge;
Query OK, 1 row affected (0.01 sec)

他のNodeでも同期されているのでしょうか。
されちゃってますねー。

[node1] $ sudo mysql -u root
mysql> show databases;
+-------------------------------+
| Database                      |
+-------------------------------+
| information_schema            |
| hoge                          |
| mysql                         |
| mysql_innodb_cluster_metadata |
| performance_schema            |
| sys                           |
| test                          |
+-------------------------------+
7 rows in set (0.00 sec)

Group Replicationのstatus的にはどうなんでしょうか。
実質Multi Primary Modeですがstatus的にはnode1のみRead/WriteでSingle Primary Modeのままですね。

[node1] $ mysqlsh --log-level=DEBUG3  --uri repl@node1:3306
 MySQL  node1:3306  JS > var cluster = dba.createCluster('myCluster')
 MySQL  node1:3306  JS > cluster.status()
{
    "clusterName": "myCluster", 
    "defaultReplicaSet": {
        "name": "default", 
        "primary": "node1:3306", 
        "ssl": "DISABLED", 
        "status": "OK_NO_TOLERANCE", 
        "statusText": "Cluster is NOT tolerant to any failures.", 
        "topology": {
            "node1:3306": {
                "address": "node1:3306", 
                "mode": "R/W", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE"
            }, 
            "node2:3306": {
                "address": "node2:3306", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE"
            }, 
            "node3:3306": {
                "address": "node3:3306", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE"
            }
        }, 
        "topologyMode": "Single-Primary"
    }, 
    "groupInformationSourceMember": "node1:3306"
}

どんな問題が起こるか分からないので、super-read-onlyはONのままにしておきましょう。

Secondary Nodeを止めた状態でデータを更新系query(insert update delete)を走らせて、Nodeを復旧してちゃんと更新されるか

これもできてほしいですが試してみましょう。

INSERTのみ

まずは、insertのみを実行します。はい、余裕でできますね。

[node2] $ sudo systemctl stop mysqld

[node1] $ sudo mysql -u root
mysql> CREATE DATABASE poge; CREATE TABLE poge.t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL); INSERT INTO poge.t1 VALUES (1, 'Ogura');
Query OK, 1 row affected (0.01 sec)

Query OK, 0 rows affected (0.03 sec)

Query OK, 1 row affected (0.01 sec)

[node2] $ sudo systemctl start mysqld; sudo mysql -u root
mysql> SHOW DATABASES; show tables from poge; select * from poge.t1;
+-------------------------------+
| Database                      |
+-------------------------------+
| information_schema            |
| hoge                          |
| mysql                         |
| mysql_innodb_cluster_metadata |
| performance_schema            |
| poge                          |
| sys                           |
| test                          |
+-------------------------------+
8 rows in set (0.00 sec)

+----------------+
| Tables_in_poge |
+----------------+
| t1             |
+----------------+
1 row in set (0.00 sec)

+----+-------+
| c1 | c2    |
+----+-------+
|  1 | Ogura |
+----+-------+
1 row in set (0.00 sec)

UPDATEのみ

mysqlプロセス再起動後すぐにselectすると更新されていませんでしたが、再実行すると更新されていました。

[node2] $ sudo systemctl stop mysqld

[node1] $ sudo mysql -u root -e "UPDATE poge.t1 SET c2=Rina WHERE c1=1;"

[node2] $ sudo systemctl start mysqld; sudo mysql -u root -e "select * from poge.t1;"
+----+-------+
| c1 | c2    |
+----+-------+
|  1 | Ogura |
+----+-------+
[node2] $  sudo mysql -u root -e "select * from poge.t1;"
+----+------+
| c1 | c2   |
+----+------+
|  1 | Rina |
+----+------+

DELETEのみ

これも問題ないですね

[node2] $ sudo systemctl stop mysqld

[node1] $ sudo mysql -u root -e "DELETE FROM poge.t1 WHERE c1=1; SELECT * FROM poge.t1;"

[node2] $ sudo systemctl start mysqld; sudo mysql -u root -e "SELECT * FROM poge.t1;"
+----+------+
| c1 | c2   |
+----+------+
|  1 | Rina |
+----+------+
[node2] $  sudo mysql -u root -e "SELECT * FROM poge.t1;"

INSERT, UPDATE, DELETEまとめて

ある程度データを入れた状態で試してみましょう。
多少遅れますがまあ問題無いですね。

[node1] $ sudo mysql -u root
mysql> INSERT INTO poge.t1 VALUES (2, 'Hidaka'), (3, 'Sakura'), (4, 'Ayane'), (5, 'Serizawa'), (6, 'Yu'), (7, 'Uchida'), (8, 'Maya'), (9, 'Minase'), (10, 'Inori');
Query OK, 9 rows affected (0.02 sec)
Records: 9  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM poge.t1;
+----+----------+
| c1 | c2       |
+----+----------+
|  2 | Hidaka   |
|  3 | Sakura   |
|  4 | Ayane    |
|  5 | Serizawa |
|  6 | Yu       |
|  7 | Uchida   |
|  8 | Maya     |
|  9 | Minase   |
| 10 | Inori    |
+----+----------+
9 rows in set (0.00 sec)

[node2] $ sudo mysql -u root -e "SELECT * FROM poge.t1;"
+----+----------+
| c1 | c2       |
+----+----------+
|  2 | Hidaka   |
|  3 | Sakura   |
|  4 | Ayane    |
|  5 | Serizawa |
|  6 | Yu       |
|  7 | Uchida   |
|  8 | Maya     |
|  9 | Minase   |
| 10 | Inori    |
+----+----------+
9 rows in set (0.00 sec)

[node2] $ sudo systemctl stop mysqld

[node1] $ sudo mysql -u root -e "DELETE FROM poge.t1 WHERE c1=7; UPDATE poge.t1 SET c2='Rina' WHERE c1=2; INSERT INTO poge.t1 VALUES (11, 'Ogura'), (12, 'Yui'); SELECT * FROM poge.t1;"
+----+----------+
| c1 | c2       |
+----+----------+
|  2 | Rina     |
|  3 | Sakura   |
|  4 | Ayane    |
|  5 | Serizawa |
|  6 | Yu       |
|  8 | Maya     |
|  9 | Minase   |
| 10 | Inori    |
| 11 | Ogura    |
| 12 | Yui      |
+----+----------+

[node2] $ sudo systemctl start mysqld; sudo mysql -u root -e "SELECT * FROM poge.t1;"
+----+----------+
| c1 | c2       |
+----+----------+
|  2 | Hidaka   |
|  3 | Sakura   |
|  4 | Ayane    |
|  5 | Serizawa |
|  6 | Yu       |
|  7 | Uchida   |
|  8 | Maya     |
|  9 | Minase   |
| 10 | Inori    |
+----+----------+

[node2] $ sudo mysql -u root -e "SELECT * FROM poge.t1;"
+----+----------+
| c1 | c2       |
+----+----------+
|  2 | Hidaka   |
|  3 | Sakura   |
|  4 | Ayane    |
|  5 | Serizawa |
|  6 | Yu       |
|  7 | Uchida   |
|  8 | Maya     |
|  9 | Minase   |
| 10 | Inori    |
+----+----------+

[node2] $ sudo mysql -u root -e "SELECT * FROM poge.t1;"
+----+----------+
| c1 | c2       |
+----+----------+
|  2 | Rina     |
|  3 | Sakura   |
|  4 | Ayane    |
|  5 | Serizawa |
|  6 | Yu       |
|  8 | Maya     |
|  9 | Minase   |
| 10 | Inori    |
| 11 | Ogura    |
| 12 | Yui      |

Primary Nodeで更新系queryを走らせた後、同期が完了する前にPrimary Nodeを停止させて、Primary Nodeが切り替わった後に旧Primary Nodeを復旧させてみてデータが同期されるか

どう試そうか迷いますが以下の流れで実行してみます。

  • 処理1
    • session1でPrimary Nodeにupdate文を流しまくる。
    • session1の処理が完了する前にsession2でPrimary Nodeのmysqldをkill。
  • 処理2
    • 旧Primary Nodeで最後に実行できたqueryが新Primary Nodeで反映されているかどうか確認。
  • 処理3
    • 旧Primary Nodeのmysqldを再起動。
  • 処理4
    • 旧Primary Nodeのデータと新Primary Nodeのデータを確認。

処理1

Primary Nodeでこのscriptを実行しつつkillします。

#! /bin/env bash

i=15
while :
do
    sudo mysql -u root -e "INSERT INTO poge.t1 VALUES ($i, 'Ogura$i');"
    i=`expr $i + 1`
done

session1で実行しつつ、session2でkillします。

[node1] $ sudo ./while.sh
ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 104
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (111)
...
ERROR 1290 (HY000) at line 1: The MySQL server is running with the --super-read-only option so it cannot execute this statement
ERROR 1290 (HY000) at line 1: The MySQL server is running with the --super-read-only option so it cannot execute this statement
...
[node1] $ ps aux | grep mysql
mysql    25083  0.3  9.1 10590220 1058624 ?    Sl   11:28   1:03 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
[node1] $ kill -9 25083

killしたタイミングでコネクションが貼れなくなりエラーが出ていますが、少し経つとsuper-read-onlyなので実行できないよエラーに変わります。
systemdがmysqlプロセスを再起動したみたいですね。

データを確認してみましょう。
旧Primary Node(node1)と新Primary Node(node2)で一致していました。

[node1] $ sudo mysql -u root -e "select * from poge.t1;"
+----+----------+
| c1 | c2       |
+----+----------+
...
| 79 | Ogura79  |
+----+----------+

[node2] $ sudo mysql -u root -e "select * from poge.t1;"
+----+----------+
| c1 | c2       |
+----+----------+
...
| 79 | Ogura79  |
+----+----------+

Group Replicationの状態としては、node1がMissing(本人にとってはOFFLINE)になっていました。

[node2] $ mysqlsh --log-level=DEBUG3  --uri repl@node2:3306
 MySQL  node2:3306  JS > var cluster = dba.getCluster()
 MySQL  node2:3306  JS > cluster.status()
{
    "clusterName": "myCluster", 
    "defaultReplicaSet": {
        "name": "default", 
        "primary": "node1:3306", 
        "ssl": "DISABLED", 
        "status": "OK_NO_TOLERANCE", 
        "statusText": "Cluster is NOT tolerant to any failures. 1 member is not active", 
        "topology": {
            "node1:3306": {
                "address": "node1:3306", 
                "mode": "n/a", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "(MISSING)"
            }, 
            "node2:3306": {
                "address": "node2:3306", 
                "mode": "R/W", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE"
            }, 
            "node3:3306": {
                "address": "node3:3306", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE"
            }
        }, 
        "topologyMode": "Single-Primary"
    }, 
    "groupInformationSourceMember": "node2:3306"
}

[node1] $ sudo mysql -u root -e "SELECT * from performance_schema.replication_group_members ORDER BY MEMBER_HOST;"
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST  | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| group_replication_applier | 440dd2c5-a22b-11e9-b851-fa163e47fb1b | node1        |        3306 | OFFLINE      |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
1 row in set (0.00 sec)

[node2] $ sudo mysql -u root -e "SELECT * from performance_schema.replication_group_members ORDER BY MEMBER_HOST;"
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST  | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| group_replication_applier | 34ce3aed-a249-11e9-b3e8-fa163e4531c6 | node2        |        3306 | ONLINE      |
| group_replication_applier | d46d27c7-a24e-11e9-a4e6-fa163e4b9377 | node3        |        3306 | ONLINE      |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
2 row in set (0.00 sec)

node1でSTART GROUP_REPLICATION;してみます。
ONLINEになりました。 systemdがmysqlを自動復旧したときにSTART GROUP_REPLICATION;が実行されないのかな?

[node1] $ sudo mysql -u root -e "START GROUP_REPLICATION;"
[node2] $ sudo mysql -u root -e "SELECT * from performance_schema.replication_group_members ORDER BY MEMBER_HOST;"
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST  | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| group_replication_applier | 440dd2c5-a22b-11e9-b851-fa163e47fb1b | node1        |        3306 | ONLINE      |
| group_replication_applier | 34ce3aed-a249-11e9-b3e8-fa163e4531c6 | node2        |        3306 | ONLINE      |
| group_replication_applier | d46d27c7-a24e-11e9-a4e6-fa163e4b9377 | node3        |        3306 | ONLINE      |
+---------------------------+--------------------------------------+--------------+-------------+--------------+

[node2] $ mysqlsh --log-level=DEBUG3  --uri repl@node2:3306
 MySQL  node2:3306  JS > var cluster = dba.getCluster()
 MySQL  node2:3306  JS > cluster.status()
{
    "clusterName": "myCluster", 
    "defaultReplicaSet": {
        "name": "default", 
        "primary": "node1:3306", 
        "ssl": "DISABLED", 
        "status": "OK", 
        "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", 
        "topology": {
            "node1:3306": {
                "address": "node1:3306", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE"
            }, 
            "node2:3306": {
                "address": "node2:3306", 
                "mode": "R/W", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE"
            }, 
            "node3:3306": {
                "address": "node3:3306", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE"
            }
        }, 
        "topologyMode": "Single-Primary"
    }, 
    "groupInformationSourceMember": "node2:3306"
}

処理1 systemdでやり直し

mysqlが想定外の挙動になってしまったので、systemdでやり直してみます。

#! /bin/env bash

i=100
while :
do
    sudo mysql -u root -e "INSERT INTO poge.t1 VALUES ($i, 'Ogura$i');"
    i=`expr $i + 1`
done

session1で実行しつつ、session2でkillします。

[node2] $ sudo ./while.sh
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)
...
[node2] $ sudo systemctl stop mysqld

無事停止されました。

処理2

旧Primary Nodeで最後に実行できたqueryを確認できてないのですが、 新Primary Nodeを確認してみましょう。
160まで追加されていますね。

[node1] $ sudo mysql -u root -e "select * from poge.t1;"
+----+----------+
| c1 | c2       |
+----+----------+
...
| 160 | Ogura160 |
+-----+----------+
136 rows in set (0.00 sec)

処理3

旧Primary Nodeのmysqldを再起動してみます。
無事起動してONLINEになりました。

[node2] $ sudo systemctl start mysqld

処理4

データは一致していました。

普通に再起動した場合はときに問題無いようですね。

binlogを意図的に欠損させてみる

MySQLのレプリケーション手法の違い | Yakst
こちらのサイトにも書いてあるようにGroup Replicationもbinlogで同期を取っているのでbinlog欠損が発生すると単純に落ちたmysqlを起動してもpositionが分からなくなり復旧できないはずです。

試そうと思いましたが、やってくれている人がいるのでそのまんまご紹介。
やはりそのままでは復旧できないようですね...。dumpを取って復旧が確実そうです。

blog.dshimizu.jp

まとめ

データに差分がある状態での復旧の流れを確認しました。
binlog欠損がある状態での流れはMaster-Slave Replicationと同じでしたね。
binlogを無限に溜める運用は普通はしないと思うので、binlogをexpireするまでにSecondaryのGroup Replicationを再開できれば楽できますね。

次回は、sidecarパターンでMySQL Routerを利用してOpenshiftにdeployしたコンテナからMySQLにアクセスしてみます。
ようやくInnoDB Clusterの要素が揃う..!

参考にしたサイト