GA

2019/11/25

innodb_autoinc_lock_mode = 1 vs 2 でバルクインサートが競合した時のAUTO_INCREMENTの挙動が違うはなし

TL;DR


(0, 'one') な値を INSERT INTO .. SELECT .. で1000万件突っ込んで、オートインクリメントで払い出させる。
 MySQL  localhost:3306 ssl  SQL > CREATE DATABASE d1;
Query OK, 1 row affected (0.0334 sec)

 MySQL  localhost:3306 ssl  SQL > CREATE TABLE d1.t1 (num int auto_increment PRIMARY KEY, val varchar(32));
Query OK, 0 rows affected (0.1181 sec)

 MySQL  localhost:3306 ssl  SQL > SELECT @@innodb_autoinc_lock_mode;
+----------------------------+
| @@innodb_autoinc_lock_mode |
+----------------------------+
|                          2 |
+----------------------------+
1 row in set (0.0007 sec)

 MySQL  localhost:3306 ssl  SQL > SET SESSION cte_max_recursion_depth = 50000000;
Query OK, 0 rows affected (0.0002 sec)

 MySQL  localhost:3306 ssl  SQL > INSERT INTO d1.t1 WITH RECURSIVE a AS (SELECT 1 AS n UNION ALL SELECT n + 1 FROM a WHERE n < 10000000) SELECT 0, 'one' FROM a;

 MySQL  localhost:3306 ssl  SQL > SELECT * FROM t1 ORDER BY num ASC LIMIT 3;
+-----+-----+
| num | val |
+-----+-----+
|   1 | one |
|   2 | one |
|   3 | one |
+-----+-----+
3 rows in set (0.0183 sec)

 MySQL  localhost:3306 ssl  SQL > SELECT * FROM d1.t1 ORDER BY num DESC LIMIT 3;
+----------+-----+
| num      | val |
+----------+-----+
| 10000000 | one |
|  9999999 | one |
|  9999998 | one |
+----------+-----+
3 rows in set (0.0004 sec)

 MySQL  localhost:3306 ssl  SQL > SELECT COUNT(*) FROM d1.t1;
+----------+
| COUNT(*) |
+----------+
| 10000000 |
+----------+
1 row in set (0.2423 sec)
これは従来( innodb_autoinc_lock_mode= 1 )の動作と変わりはない。
で、これを競合させる。
 MySQL  localhost:3306 ssl  SQL > TRUNCATE d1.t1;
Query OK, 0 rows affected (0.2704 sec)

 MySQL  localhost:3306 ssl  SQL > INSERT INTO d1.t1 WITH RECURSIVE a AS (SELECT 1 AS n UNION ALL SELECT n + 1 FROM a WHERE n < 10000000) SELECT 0, 'one' FROM a;

 MySQL  localhost:3306 ssl  SQL > INSERT INTO d1.t1 WITH RECURSIVE a AS (SELECT 1 AS n UNION ALL SELECT n + 1 FROM a WHERE n < 10000000) SELECT 0, 'two' FROM a; -- こっちは別のターミナル && valカラムに 'two' を割り当てる
とするとこうなる。
 MySQL  localhost:3306 ssl  SQL > SELECT * FROM d1.t1 ORDER BY num ASC LIMIT 3;
+-----+-----+
| num | val |
+-----+-----+
|   1 | one |
|   2 | one |
|   3 | one |
+-----+-----+
3 rows in set (0.0006 sec)

 MySQL  localhost:3306 ssl  SQL > SELECT * FROM d1.t1 ORDER BY num DESC LIMIT 3;
+----------+-----+
| num      | val |
+----------+-----+
| 20026855 | two |
| 20026854 | two |
| 20026853 | two |
+----------+-----+
3 rows in set (0.0018 sec)

 MySQL  localhost:3306 ssl  SQL > SELECT COUNT(*) FROM d1.t1;
+----------+
| COUNT(*) |
+----------+
| 20000000 |
+----------+
1 row in set (1.8136 sec)
多少の歯抜けが存在するのは良いとして(AUTO_INCREMENTはもともとそういうもの)、ちゃんと入っているように見える…が。
 MySQL  localhost:3306 ssl  SQL > WITH a AS (SELECT num, val = LEAD(val) OVER (ORDER BY num) AS same_val_prev_row FROM d1.t1)
                               -> SELECT num FROM a WHERE same_val_prev_row <> 1;
+----------+
| num      |
+----------+
|    16383 |
|    20478 |
|    36862 |
..
| 19791570 |
| 19857105 |
| 19895785 |
+----------+
307 rows in set (1 min 1.3050 sec)
このクエリーが何を意味しているかというと、 (1, 'one'), (2, 'one') のように直後の行と同じvalの値を持つ行は1、 (2, 'one'), (3, 'two') のように違うvalを持つ行は0が返されるような same_val_prev_row カラムを計算してそれを <> 1(つまり、直後の行とvalの値が違う) 行を抽出している。
この抽出結果を鑑みて前後の行を確認してみると
 MySQL  localhost:3306 ssl  SQL > SELECT * FROM d1.t1 WHERE num BETWEEN 16383 - 1 AND 16383 + 1;
+-------+-----+
| num   | val |
+-------+-----+
| 16382 | one |
| 16383 | one |
| 16384 | two |
+-------+-----+
3 rows in set (0.0554 sec)

 MySQL  localhost:3306 ssl  SQL > SELECT * FROM d1.t1 WHERE num BETWEEN 19857105 - 1 AND 19857105 + 1;
+----------+-----+
| num      | val |
+----------+-----+
| 19857104 | two |
| 19857105 | two |
| 19857106 | one |
+----------+-----+
3 rows in set (0.5144 sec)
確かに途中でvalの値が変わるのが発生している。これが innodb_autoinc_lock_mode= 2 の時の動作。
innodb_autoinc_lock_mode はオンラインで変更できないので、 SET PERSIST_ONLY を使って書き込んでから RESTART ステートメントで再起動。
 MySQL  localhost:3306 ssl  SQL > TRUNCATE d1.t1;
SERR ^HQuery OK, 0 rows affected (1.3815 sec)

 MySQL  localhost:3306 ssl  SQL > SET PERSIST_ONLY innodb_autoinc_lock_mode = 1;
Query OK, 0 rows affected (0.0201 sec)

 MySQL  localhost:3306 ssl  SQL > RESTART;
Query OK, 0 rows affected (0.0147 sec)
で、同じことをもう一回。
 MySQL  localhost:3306 ssl  SQL > SELECT @@innodb_autoinc_lock_mode;
+----------------------------+
| @@innodb_autoinc_lock_mode |
+----------------------------+
|                          1 |
+----------------------------+
1 row in set (0.6702 sec)

 MySQL  localhost:3306 ssl  SQL > INSERT INTO d1.t1 WITH RECURSIVE a AS (SELECT 1 AS n UNION ALL SELECT n + 1 FROM a WHERE n < 10000000) SELECT 0, 'one' FROM a;

 MySQL  localhost:3306 ssl  SQL > INSERT INTO d1.t1 WITH RECURSIVE a AS (SELECT 1 AS n UNION ALL SELECT n + 1 FROM a WHERE n < 10000000) SELECT 0, 'two' FROM a;

 MySQL  localhost:3306 ssl  SQL > SELECT COUNT(*) FROM d1.t1;
+----------+
| COUNT(*) |
+----------+
| 20000000 |
+----------+
1 row in set (0.4621 sec)

 MySQL  localhost:3306 ssl  SQL > SELECT * FROM d1.t1 ORDER BY num LIMIT 3;
+-----+-----+
| num | val |
+-----+-----+
|   1 | one |
|   2 | one |
|   3 | one |
+-----+-----+
3 rows in set (0.0006 sec)

 MySQL  localhost:3306 ssl  SQL > SELECT * FROM d1.t1 ORDER BY num DESC LIMIT 3;
+----------+-----+
| num      | val |
+----------+-----+
| 20026855 | two |
| 20026854 | two |
| 20026853 | two |
+----------+-----+
3 rows in set (0.0006 sec)

 MySQL  localhost:3306 ssl  SQL > WITH a AS (SELECT num, val = LEAD(val) OVER (ORDER BY num) AS same_val_prev_row FROM d1.t1 WHERE num) SELECT * FROM a WHERE same_val_prev_row <> 1;
+----------+
| num      |
+----------+
| 10000000 |
+----------+
1 row in set (1 min 8.4337 sec)

 MySQL  localhost:3306 ssl  SQL > SELECT * FROM d1.t1 WHERE num >= 9999999 ORDER BY num LIMIT 3;
+----------+-----+
| num      | val |
+----------+-----+
|  9999999 | one |
| 10000000 | one |
| 10026856 | two |
+----------+-----+
3 rows in set (0.0005 sec)
今度は切り替わりが1回だけになった。
performance_schema.data_locks を見ると、 innodb_autoinc_lock_mode= 1 の時はAUTO_INCロックでぶつかりまくっているのに対して
 MySQL  localhost:3306 ssl  performance_schema  SQL > SELECT @@innodb_autoinc_lock_mode;
+----------------------------+
| @@innodb_autoinc_lock_mode |
+----------------------------+
|                          1 |
+----------------------------+
1 row in set (0.0007 sec)

 MySQL  localhost:3306 ssl  performance_schema  SQL > SELECT * FROM data_locks;
+--------+--------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+
| ENGINE | ENGINE_LOCK_ID                       | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+--------+--------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+
| INNODB | 140684506070480:1065:140684405311704 |                  2183 |        47 |       47 | d1            | t1          | NULL           | NULL              | NULL       |       140684405311704 | TABLE     | AUTO_INC  | WAITING     | NULL      |
| INNODB | 140684506071352:1065:140682660470608 |                  2182 |        57 |        5 | d1            | t1          | NULL           | NULL              | NULL       |       140682660470608 | TABLE     | AUTO_INC  | GRANTED     | NULL      |
| INNODB | 140684506071352:1065:140684405317672 |                  2182 |        57 |        5 | d1            | t1          | NULL           | NULL              | NULL       |       140684405317672 | TABLE     | IX        | GRANTED     | NULL      |
+--------+--------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+
3 rows in set (0.0217 sec)

 MySQL  localhost:3306 ssl  performance_schema  SQL > SELECT SUM(count_star), SUM(sum_timer_wait) FROM events_waits_summary_by_instance WHERE event_name = 'wait/synch/mutex/innodb/autoinc_mutex';
+-----------------+---------------------+
| SUM(count_star) | SUM(sum_timer_wait) |
+-----------------+---------------------+
|             338 |            49899332 |
+-----------------+---------------------+
1 row in set (0.0146 sec)
innodb_autoinc_lock_mode= 2 ではAUTO_INCロックはぶつかってはいない。mutex取ってる回数は変わらなさげだけどAUTO_INCREMENTを払い出す回数は変わらないだろうからそれはそれで合っている気がする。
 MySQL  localhost:3306 ssl  performance_schema  SQL > SELECT @@innodb_autoinc_lock_mode;
+----------------------------+
| @@innodb_autoinc_lock_mode |
+----------------------------+
|                          2 |
+----------------------------+
1 row in set (0.0005 sec)

 MySQL  localhost:3306 ssl  performance_schema  SQL > SELECT * FROM data_locks;
+--------+--------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+
| ENGINE | ENGINE_LOCK_ID                       | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+--------+--------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+
| INNODB | 140413401562576:1065:140413285501144 |                  2570 |        47 |        5 | d1            | t1          | NULL           | NULL              | NULL       |       140413285501144 | TABLE     | IX        | GRANTED     | NULL      |
| INNODB | 140413401561704:1065:140413285495192 |                  2569 |        48 |        7 | d1            | t1          | NULL           | NULL              | NULL       |       140413285495192 | TABLE     | IX        | GRANTED     | NULL      |
+--------+--------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+
2 rows in set (0.0206 sec)

 MySQL  localhost:3306 ssl  performance_schema  SQL > SELECT SUM(count_star), SUM(sum_timer_wait) FROM events_waits_summary_by_instance WHERE event_name = 'wait/synch/mutex/innodb/autoinc_mutex';
+-----------------+---------------------+
| SUM(count_star) | SUM(sum_timer_wait) |
+-----------------+---------------------+
|             349 |            25780608 |
+-----------------+---------------------+
1 row in set (0.0009 sec)
という訳で、「バルクロードの先頭から最後まではAUTO_INCREMENTが一貫していなきゃイヤン!」という場合は innodb_autoinc_lock_mode= 1 の時がいいかもですね。
( SELECT last_insert_id() からの SELECT .. FROM id > さっきの値 でバルクロードしたレコード(だけ)をフェッチできると期待しているような場合かな… )
​

2019/11/19

MyNA 望年LT大会2019に参加してきました

昨年に引き続き SCSK さんのご支援で nomuno さんでの開催。美味しいごはんとお酒をありがとうございましたm(_ _)m

終わってしまってからでなんなんですが「どんな雰囲気なの?」「フツーのLT大会と違うの?」みたいなのを(事前に)何回か聞かれたので紹介します。
  • 飲みながらしゃべりたい人がしゃべりたいことをしゃべります
    • そういう意味では MySQL Casual Talks にも近いですがLT枠onlyでした(ただし厳密に5分きっかりなスタイルではない)
  • 椅子に座って全員同じ方向を向いて始まるというスタイルでは ない です
    • 立食パーティーをしながら、一角ではLTやってるという感じ
    • 興味があれば聞いてもいいし、お酒を嗜みながら他の人と談笑するもいいし
      • これは珍しいスタイルかも知れません
  • 赤坂です
    • だいじなことなのでもう一度言います、赤坂です(少なくとも2018と2019は赤坂でした)

私はここ最近 ~(といってもそろそろ熱が冷め始めてきていて困っている)~ MySQL Shellを使って、5分で(間に合わなかった) InnoDB Clusterのサンドボックスを構築するのにチャレンジしました。
( ´-`).oO(記事を書いた当時はWSL1で、昨日の時点ではWSL2を使っていて、かかる時間の想定に若干のブレが(いいわけ
個人的には keny_lala さんの「MySQLの気持ちになって答えるクイズ」が面白かったです! スライド公開期待 :)
しかし後で聞いてみたら「一応LTは準備してきてた(けど発表{しな|できな}かった)」みたいな人が多くて、そういう話こそ聞きたかった感もあり、そういうイベントしたいなって思った赤坂の夜でした!
あと、作って以来渡そう渡そうと思っていた マイナくんのウインドブレーカー をようやく堤井さんに渡せたので俺の2019年は無事に終わりそうです。
来年もよろしくお願いしますm(_ _)m
​

2019/11/12

InnoDB Clusterの構築だけMySQL Shellでやって、運用はSQLインターフェイスでやるのはアリ?

TL;DR

  • 調べたいこと
    • MySQL InnoDB Clusterの構築(と、その後の運用フェーズでメンバーの追加削除)だけMySQL Shellでやって、フェイルオーバーその他の動作はSQLインターフェイス( mysql コマンドまたはスクリプトなど) からだけできるか?
    • できた
  • …というちょっとした検証をしたい時にMySQL Shellは便利

日々の覚書: MySQL Shellのdba.deploySandboxInstanceでサクッとmysqldを起動する でやったのと同じ手順で、 ubuntu:latest (2019/11/12時点) なDockerコンテナに mysql-community-server, mysql-shell, mysql-router-community をインストールした。
( ´-`).oO( mysql-community-server と mysql-router-community って “community” が付く位置が違って一瞬ハマった…そして mysql-shell は “community” がつかない…
# dpkg -l | grep -i mysql
ii  mysql-apt-config            0.8.14-1                          all          Auto configuration for MySQL APT Repo.
ii  mysql-client                8.0.18-1ubuntu18.04               amd64        MySQL Client meta package depending on latest version
ii  mysql-common                8.0.18-1ubuntu18.04               amd64        Common files shared between packages
ii  mysql-community-client      8.0.18-1ubuntu18.04               amd64        MySQL Client
ii  mysql-community-client-core 8.0.18-1ubuntu18.04               amd64        MySQL Client Core Binaries
ii  mysql-community-server      8.0.18-1ubuntu18.04               amd64        MySQL Server
ii  mysql-community-server-core 8.0.18-1ubuntu18.04               amd64        MySQL Server Core Binaires
ii  mysql-router-community      8.0.18-1ubuntu18.04               amd64        MySQL Router
ii  mysql-shell:amd64           8.0.18-1ubuntu18.04               amd64        MySQL Shell (part of MySQL Server) 8.0
mysqlsh を起動し、 dba.deploySandboxInstance() で2つばかりインスタンスを立ち上げる。
# mysqlsh
 MySQL  JS > dba.deploySandboxInstance(3306)
 MySQL  JS > dba.deploySandboxInstance(3307)
どっちかのインスタンス(最初のマスターにするやつ、どれでもいいと思う)にそのまま接続して、InnoDB Clusterを構築する。
 MySQL  JS > \c root@localhost:3306
reating a session to 'root@localhost:3306'
Please provide the password for 'root@localhost:3306':
Save password for 'root@localhost:3306'? [Y]es/[N]o/Ne[v]er (default No): Y

 MySQL  localhost:3306 ssl  JS > dba.createCluster('sakila_cluster')
クラスターのメンバーを追加するには、一度変数に受けてClusterオブジェクトにしてから addInstance() っぽい。
 MySQL  localhost:3306 ssl  JS > var c = dba.getCluster()
 MySQL  localhost:3306 ssl  JS > c.addInstance('localhost:3307')
Please provide the password for 'root@localhost:3307':
Save password for 'root@localhost:3307'? [Y]es/[N]o/Ne[v]er (default No): Y

NOTE: The target instance 'localhost:3307' has not been pre-provisioned (GTID set is
empty). The Shell is unable to decide whether incremental distributed state
recovery can correctly provision it.
The safest and most convenient way to provision a new instance is through
automatic clone provisioning, which will completely overwrite the state of
'localhost:3307' with a physical snapshot from an existing cluster member. To
use this method by default, set the 'recoveryMethod' option to 'clone'.

The incremental distributed state recovery may be safely used if you are sure
all updates ever executed in the cluster were done with GTIDs enabled, there
are no purged transactions and the new instance contains the same GTID set as
the cluster 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
ここでMySQL 8.0.17とそれ以降ならCLONEプラグインを使って初期データのセットアップを済ませるモードが選べる。らくちん。
Monitoring recovery process of the new cluster member. Press ^C to stop monitoring and let it continue in background.
Clone based state recovery is now in progress.

NOTE: A server restart is expected to happen as part of the clone process. If the
server does not support the RESTART command or does not come back after a
while, you may need to manually start it back.

* Waiting for clone to finish...
NOTE: localhost:3307 is being cloned from 127.0.0.1:3306
** Stage DROP DATA: Completed
** Clone Transfer
    FILE COPY  ############################################################  100%  Completed
    PAGE COPY  ############################################################  100%  Completed
    REDO COPY  ############################################################  100%  Completed

NOTE: localhost:3307 is shutting down...

* Waiting for server restart... ready
* 127.0.0.1:3307 has restarted, waiting for clone to finish...
* Clone process has finished: 59.55 MB transferred in about 1 second (~inf TB/s)

State recovery already finished for 'localhost:3307'

The instance 'localhost:3307' was successfully added to the cluster.
なんか仕事をしている気になれそうな出力があったあと、クラスターのメンバーへの追加が終わっている。
 MySQL  localhost:3306 ssl  JS > c.status()
{
    "clusterName": "sakila_cluster",
    "defaultReplicaSet": {
        "name": "default",
        "primary": "127.0.0.1:3306",
        "ssl": "REQUIRED",
        "status": "OK_NO_TOLERANCE",
        "statusText": "Cluster is NOT tolerant to any failures.",
        "topology": {
            "127.0.0.1:3306": {
                "address": "127.0.0.1:3306",
                "mode": "R/W",
                "readReplicas": {},
                "replicationLag": null,
                "role": "HA",
                "status": "ONLINE",
                "version": "8.0.18"
            },
            "127.0.0.1:3307": {
                "address": "127.0.0.1:3307",
                "mode": "R/O",
                "readReplicas": {},
                "replicationLag": null,
                "role": "HA",
                "status": "ONLINE",
                "version": "8.0.18"
            }
        },
        "topologyMode": "Single-Primary"
    },
    "groupInformationSourceMember": "127.0.0.1:3306"
}
はい。取り敢えず2台構成ではあるけどMySQL InnoDB Clusterの形にはなったのでMySQL Shellはここまで。
次はMySQL Router。
初期設定は --bootstrap でInnoDB Clusterに参加しているmysqldを指定( username@hostname:port 形式) してやるくらい。
# mysqlrouter --bootstrap root@localhost:3306 --user=mysqlrouter
Please enter MySQL password for root:
# Reconfiguring system MySQL Router instance...

- Checking for old Router accounts
 - Found old Router accounts, removing
- Creating mysql account 'mysql_router1_c1ntzh4p43m6'@'%' for cluster management
- Storing account in keyring
- Adjusting permissions of generated files
- Creating configuration /etc/mysqlrouter/mysqlrouter.conf

# MySQL Router configured for the InnoDB cluster 'sakila_cluster'

After this MySQL Router has been started with the generated configuration

    $ /etc/init.d/mysqlrouter restart
or
    $ mysqlrouter -c /etc/mysqlrouter/mysqlrouter.conf

the cluster 'sakila_cluster' can be reached by connecting to:

## MySQL Classic protocol

- Read/Write Connections: localhost:6446
- Read/Only Connections:  localhost:6447

## MySQL X protocol

- Read/Write Connections: localhost:64460
- Read/Only Connections:  localhost:64470
これでコンフィグが書き込まれるので、その後フツーに起動してやれば息をし始める。
Dockerコンテナでsystemdを使えるような設定にはしてないので、 nohup で起動しちゃう。
# nohup  mysqlrouter -c /etc/mysqlrouter/mysqlrouter.conf &
[1] 4673

# mysql -h127.0.0.1 -P6446 -uroot -sse "SELECT @@port" ### mysqlrouterのRead/Writeポート
3306

# mysql -h127.0.0.1 -P6447 -uroot -sse "SELECT @@port" ### mysqlrouterのRead/Onlyポート
3307
ちゃんとつながった。
じゃあコイツを mysql コマンドラインクライアントでSQL使ってスイッチオーバーさせる。
mysql> SELECT * FROM performance_schema.replication_group_members; -- BEFORE
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | 5442bcff-0506-11ea-b701-0242ac110003 | 127.0.0.1   |        3306 | ONLINE       | PRIMARY     | 8.0.18         |
| group_replication_applier | 88295d82-0506-11ea-8a34-0242ac110003 | 127.0.0.1   |        3307 | ONLINE       | SECONDARY   | 8.0.18         |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
2 rows in set (0.05 sec)

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | 5442bcff-0506-11ea-b701-0242ac110003 | 127.0.0.1   |        3306 | ONLINE       | PRIMARY     | 8.0.18         |
| group_replication_applier | 88295d82-0506-11ea-8a34-0242ac110003 | 127.0.0.1   |        3307 | ONLINE       | SECONDARY   | 8.0.18         |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
2 rows in set (0.05 sec)

mysql> SELECT group_replication_set_as_primary('88295d82-0506-11ea-8a34-0242ac110003');
+--------------------------------------------------------------------------+
| group_replication_set_as_primary('88295d82-0506-11ea-8a34-0242ac110003') |
+--------------------------------------------------------------------------+
| Primary server switched to: 88295d82-0506-11ea-8a34-0242ac110003         |
+--------------------------------------------------------------------------+
1 row in set (0.15 sec)

mysql> SELECT * FROM performance_schema.replication_group_members; -- AFTER
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | 5442bcff-0506-11ea-b701-0242ac110003 | 127.0.0.1   |        3306 | ONLINE       | SECONDARY   | 8.0.18         |
| group_replication_applier | 88295d82-0506-11ea-8a34-0242ac110003 | 127.0.0.1   |        3307 | ONLINE       | PRIMARY     | 8.0.18         |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
2 rows in set (0.00 sec)
ルーター側はどうなったかな。
# mysql -h127.0.0.1 -P6446 -uroot -sse "SELECT @@port" ### Read/Writeポート
3307

# mysql -h127.0.0.1 -P6447 -uroot -sse "SELECT @@port" ### Read/Onlyポート
3306
いけてますね。
MySQL Shellが苦手でも、一度構築(これは面倒なのでMySQL Shellに任せてしまいたい、個人的に)してしまえばあとはいつもの使い慣れたものから呼び出せるということでひとまず安心。
​

MySQLにおけるNULLと演算の結果


NULLの四則演算はわかりやすいですよね。
mysql80 21> SELECT 1 + NULL, 1 - NULL, 1 * NULL, 1 / NULL;
+----------+----------+----------+----------+
| 1 + NULL | 1 - NULL | 1 * NULL | 1 / NULL |
+----------+----------+----------+----------+
|     NULL |     NULL |     NULL |     NULL |
+----------+----------+----------+----------+
1 row in set (0.00 sec)
NULLに対する四則演算はNULL。
「謎の数字に1を足したら何になる?」「(´・ω・`)知らんがな」
論理演算は知ってる人と知らない人に分かれそう。
mysql80 21> SELECT TRUE AND NULL, TRUE OR NULL, FALSE AND NULL, FALSE OR NULL;
+---------------+--------------+----------------+---------------+
| TRUE AND NULL | TRUE OR NULL | FALSE AND NULL | FALSE OR NULL |
+---------------+--------------+----------------+---------------+
|          NULL |            1 |              0 |          NULL |
+---------------+--------------+----------------+---------------+
1 row in set (0.00 sec)
MySQLの整数型において0以外は真、0は偽で、 TRUE は1のシノニム、 FALSE は0のシノニムです。
話はちょっと逸れますが、 TRUE は1のシノニムなので↓のようなことも可能(?)です。 = TRUE と等価比較すると値が真なのか偽なのかは判定できません。
mysql80 21> SELECT TRUE + TRUE /* 1 + 1 */, TRUE + TRUE = TRUE /* 1 + 1 = 1 */;
+-------------+--------------------+
| TRUE + TRUE | TRUE + TRUE = TRUE |
+-------------+--------------------+
|           2 |                  0 |
+-------------+--------------------+
1 row in set (0.00 sec)
真偽値を判定するには IS演算子 を使う必要があります。
mysql80 21> SELECT (TRUE + TRUE) IS TRUE, (TRUE + TRUE = TRUE) IS TRUE;
+-----------------------+------------------------------+
| (TRUE + TRUE) IS TRUE | (TRUE + TRUE = TRUE) IS TRUE |
+-----------------------+------------------------------+
|                     1 |                            0 |
+-----------------------+------------------------------+
1 row in set (0.00 sec)
単純な話、0ならFALSE(= 0)、それ以外ならTRUE(= 1)を返します。NULLセーフです。 IS演算子(またはIS NOT演算子)の右辺には TRUE, FALSE, UNKNOWNしか取れません。
IS NULL は別の演算子扱いでマニュアルも別れています。これ豆な。
で、戻ってきますが論理演算はこんな感じです(さっきと同じ出力)
mysql80 21> SELECT TRUE AND NULL, TRUE OR NULL, FALSE AND NULL, FALSE OR NULL;
+---------------+--------------+----------------+---------------+
| TRUE AND NULL | TRUE OR NULL | FALSE AND NULL | FALSE OR NULL |
+---------------+--------------+----------------+---------------+
|          NULL |            1 |              0 |          NULL |
+---------------+--------------+----------------+---------------+
1 row in set (0.00 sec)
AND演算子もOR演算子も左辺と右辺を交換しても結果が変わらない演算子なので、順番関わらずこうなります。
この順番で書くと、「短絡評価っぽいでしょ?」と説明しやすいのでよくそういうのですが、本当は短絡評価しているわけではなく、↓のような感じです。
  • AND の場合、
    • 片方が偽なら式全体として偽が確定するので、偽 AND NULLは偽
    • 片方が真なら式全体の真偽はもう片方の値に依存するので、真 AND NULLはNULL
  • OR の場合、
    • 片方が偽なら式全体の真偽はもう片方の値に依存するので、偽 OR NULLはNULL
    • 片方が真なら式全体としての真が確定するので、真 OR NULLは真
十分短絡評価っぽかった。
プログラミングの短絡評価と違うのは「MySQLは飽くまで「ANDもORも両辺を交換可能」と見た上で」こうなっているということ。
「この論理演算がパッと思いつかないならNOT NULLしといた方が踏むリスクはなくなるよ」というユルいNOT NULL推奨のPoCとしてご利用いただければと思います。

ちなみにPostgreSQL 9.1.10。
(PostgreSQLのTRUE, FALSEはシノニムではなくBool型なので、足したり引いたりはできません)
postgres=# SELECT TRUE AND NULL, TRUE OR NULL, FALSE AND NULL, FALSE OR NULL;
 ?column? | ?column? | ?column? | ?column?
----------+----------+----------+----------
 [NULL]   | t        | f        | [NULL]
(1 row)
Oracle 11.2.0.4.0。
どうやらAND演算子がselect_listの中に入るのを許してくれない模様。
SQL> SELECT TRUE AND NULL, TRUE OR NULL, FALSE AND NULL, FALSE OR NULL FROM dual;
SELECT TRUE AND NULL, TRUE OR NULL, FALSE AND NULL, FALSE OR NULL FROM dual
            *
行1でエラーが発生しました。:
ORA-00923: FROMキーワードが指定の位置にありません。
​