Quantcast
Channel: 日々の覚書
Viewing all 581 articles
Browse latest View live

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

$
0
0

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 > さっきの値でバルクロードしたレコード(だけ)をフェッチできると期待しているような場合かな… )

本番データ de CLONEプラグイン、そして思ったこと

$
0
0

TL;DR

  • みんなだいすきCLONEプラグインを使って本番のマスター(稼働中)から本番のスレーブ(諸事情により再構築が必要だった)を作った。
    • 何度かリトライはしたけど、ちゃんと動いているような気がする
  • CLONE INSTANCE FROM ..を実行するターミナルは tmuxなどで切断から保護しましょう…

CLONEプラグインって何的な人は先にこっちを読んでいただくと良いかも知れない。
取り敢えず
何も考えずにクローン用のアカウントをマスターに作成する。
mysql> CREATE USER cloner@xxx.xxx.xxx.xxx IDENTIFIED BY 'xxxx';
Query OK, 0 rows affected (0.00 sec)

mysql> GRANT BACKUP_ADMIN ON *.* TO cloner@xxx.xxx.xxx.xxx;
Query OK, 0 rows affected (0.00 sec)
スレーブ用地から CLONE INSTANCE FROM ..でクローンを開始しようとする。
mysql> SET GLOBAL SEESSION clone_valid_donor_list = 'yyy.yyy.yyy.yyy:3306';
Query OK, 0 rows affected (0.00 sec)

mysql> CLONE INSTANCE FROM cloner@yyy.yyy.yyy.yyy:3306 IDENTIFIED BY 'xxxx';
ERROR 3862 (HY000): Clone Donor Error: 1016 : Can't open file: './undo001' (errno: 2 - No such file or directory).
(つд⊂)ゴシゴシ
ERROR 3862は「ドナーからこんなエラーを受け取った」ためのエラーコードなので、ERROR 1016 Can’t open file .. がドナーから受け取った生のエラー。
クローンに関する進捗とかその他は全てドナーノード側のエラーログに出力される。 Noteレベルなのでドナー側が log_error_verbosity= 3でないと出力されないと思う。
こっちがドナー側のエラーログ。
2019-11-26T10:39:22.974397+09:00 34186 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: Acquired backup lock.'
2019-11-26T10:39:22.974459+09:00 34186 [Note] [MY-013457] [InnoDB] Clone Begin Master Task by cloner@172.21.160.132
2019-11-26T10:39:22.979792+09:00 34186 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: COM_INIT: Storage Initialize.'
2019-11-26T10:39:22.979825+09:00 34186 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: COM_RES_COMPLETE.'
2019-11-26T10:39:51.981458+09:00 34187 [Note] [MY-013458] [InnoDB] Clone set state change ACK: 1
2019-11-26T10:39:51.981516+09:00 34187 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: COM_ACK: Storage Ack.'
2019-11-26T10:39:51.981544+09:00 34187 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: COM_RES_COMPLETE.'
2019-11-26T10:39:52.081398+09:00 34186 [Note] [MY-013458] [InnoDB] Clone Master received state change ACK
2019-11-26T10:39:52.081463+09:00 34186 [Note] [MY-013458] [InnoDB] Clone State Change : Number of tasks = 1
2019-11-26T10:39:52.081483+09:00 34186 [Note] [MY-013458] [InnoDB] Clone State BEGIN FILE COPY
2019-11-26T10:39:52.087133+09:00 34186 [Note] [MY-011845] [InnoDB] Clone Start PAGE ARCH : start LSN : 1927678359668, checkpoint LSN : 1927629671670
2019-11-26T10:39:52.094897+09:00 34186 [Note] [MY-013458] [InnoDB] Clone Set Error code: 1815 Saved Error code: 0
2019-11-26T10:39:52.094936+09:00 34186 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: COM_EXECUTE: Storage Execute: error: 1815: Can't open file: './undo001' (errno: 2 - No such file or directory).'
2019-11-26T10:39:52.094954+09:00 34186 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: Before sending COM_RES_ERROR: : error: 1815: Can't open file: './undo001' (errno: 2 - No such file or directory).'
2019-11-26T10:39:52.095022+09:00 34186 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: After sending COM_RES_ERROR.'
2019-11-26T10:39:52.095295+09:00 34187 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: COM_EXIT: Storage End.'
2019-11-26T10:39:52.095347+09:00 34187 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: COM_RES_COMPLETE.'
2019-11-26T10:39:52.095375+09:00 34187 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: Exiting clone protocol.'
2019-11-26T10:39:52.096028+09:00 34186 [Note] [MY-011846] [InnoDB] Clone Stop PAGE ARCH : end LSN : 1927629671670, log sys LSN : 1927678363098
2019-11-26T10:39:52.096207+09:00 34186 [Note] [MY-013457] [InnoDB] Clone End Master Task ID: 0 Passed, code: 0:
2019-11-26T10:39:52.096234+09:00 34186 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: COM_EXIT: Storage End.'
2019-11-26T10:39:52.096268+09:00 34186 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: COM_RES_COMPLETE.'
2019-11-26T10:39:52.096302+09:00 34186 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: Exiting clone protocol.'
こっちがレシピエント側のエラーログ。
2019-11-26T10:39:22.963763+09:00 139 [Note] [MY-013272] [Clone] Plugin Clone reported: 'Client: Task Connect.'
2019-11-26T10:39:22.974258+09:00 139 [Note] [MY-013272] [Clone] Plugin Clone reported: 'Client: Master ACK Connect.'
2019-11-26T10:39:22.974323+09:00 139 [Note] [MY-013457] [InnoDB] Clone Apply Begin Master Version Check
2019-11-26T10:39:22.975340+09:00 139 [Note] [MY-013457] [InnoDB] Clone Apply Version End Master Task ID: 0 Passed, code: 0:
2019-11-26T10:39:22.975382+09:00 139 [Note] [MY-013457] [InnoDB] Clone Apply Begin Master Task
2019-11-26T10:39:22.976280+09:00 139 [Warning] [MY-013460] [InnoDB] Clone removing all user data for provisioning: Started
2019-11-26T10:39:22.976316+09:00 139 [Note] [MY-011977] [InnoDB] Clone Drop all user data
2019-11-26T10:39:24.122046+09:00 139 [Note] [MY-011977] [InnoDB] Clone: Fix Object count: 2314 task: 0
2019-11-26T10:39:51.598531+09:00 139 [Note] [MY-011977] [InnoDB] Clone Drop User schemas
2019-11-26T10:39:51.598901+09:00 139 [Note] [MY-011977] [InnoDB] Clone: Fix Object count: 14 task: 0
2019-11-26T10:39:51.623511+09:00 139 [Note] [MY-011977] [InnoDB] Clone Drop User tablespaces
2019-11-26T10:39:51.624095+09:00 139 [Note] [MY-011977] [InnoDB] Clone: Fix Object count: 6 task: 0
2019-11-26T10:39:51.625091+09:00 132 [Note] [MY-010596] [Repl] Error reading relay log event for channel '': slave SQL thread was killed
2019-11-26T10:39:51.625136+09:00 132 [Note] [MY-010587] [Repl] Slave SQL thread for channel '' exiting, replication stopped in log 'FIRST' at position 0
2019-11-26T10:39:51.742987+09:00 139 [Note] [MY-011977] [InnoDB] Clone Drop: finished successfully
2019-11-26T10:39:51.743073+09:00 139 [Warning] [MY-013460] [InnoDB] Clone removing all user data for provisioning: Finished
2019-11-26T10:39:51.981055+09:00 139 [Note] [MY-013272] [Clone] Plugin Clone reported: 'Client: Command COM_INIT.'
2019-11-26T10:39:52.095229+09:00 139 [Note] [MY-013458] [InnoDB] Clone Set Error code: 3862 Saved Error code: 0
2019-11-26T10:39:52.095271+09:00 139 [Note] [MY-013458] [InnoDB] Clone Apply set error code: 3862: Clone Donor Error: 1016 : Can't open file: './undo001' (errno: 2 - No such file or directory).
2019-11-26T10:39:52.095290+09:00 139 [Note] [MY-013272] [Clone] Plugin Clone reported: 'Client: Wait for remote after local issue: error: 3862: Clone Donor Error: 1016 : Can't open file: './undo001' (errno: 2 - No such file or directory)..'
2019-11-26T10:39:52.095305+09:00 139 [Note] [MY-013272] [Clone] Plugin Clone reported: 'Client: Command COM_EXECUTE: error: 3862: Clone Donor Error: 1016 : Can't open file: './undo001' (errno: 2 - No such file or directory)..'
2019-11-26T10:39:52.095552+09:00 139 [Note] [MY-013272] [Clone] Plugin Clone reported: 'Client: Master ACK COM_EXIT.'
2019-11-26T10:39:52.095941+09:00 139 [Note] [MY-013272] [Clone] Plugin Clone reported: 'Client: Master ACK Disconnect : abort: false.'
2019-11-26T10:39:52.096479+09:00 139 [Note] [MY-013272] [Clone] Plugin Clone reported: 'Client: Task COM_EXIT.'
2019-11-26T10:39:52.096798+09:00 139 [Note] [MY-013272] [Clone] Plugin Clone reported: 'Client: Task Disconnect : abort: false.'
2019-11-26T10:39:52.096858+09:00 139 [Note] [MY-013458] [InnoDB] Clone Set Error code: 3862 Saved Error code: 3862
2019-11-26T10:39:52.096890+09:00 139 [Note] [MY-013458] [InnoDB] Clone Apply set error code: 3862: Clone Donor Error: 1016 : Can't open file: './undo001' (errno: 2 - No such file or directory).
2019-11-26T10:39:52.096944+09:00 139 [Note] [MY-013458] [InnoDB] Clone Set Error code: 3862 Saved Error code: 3862
2019-11-26T10:39:52.097189+09:00 139 [Note] [MY-013457] [InnoDB] Clone Apply End Master Task ID: 0 Failed, code: 3862: Clone Donor Error: 1016 : Can't open file: './undo001' (errno: 2 - No such file or directory).
目をこらして良く見ていただくとわかるのだが、 [Warning] [MY-013460] [InnoDB] Clone removing all user data for provisioning: Finishedなので、クローンが始まった時点でレシピエントのdatadirからは諸々が消える。

ちなみにドナー側の undo001、ちゃんとあるんですけどねってすったもんだした後、レシピエントを再起動したりドナーの innodb_undo_log_truncate = ONにしてみたり色々したけれど、結局ドナー(= 稼働中のマスター)を再起動するまでクローンは同じエラーで転け続けた。
  • ひょっとしたら、ドナーを5.7から8.0にバージョンアップした時に、「自動で更新かかるから良いっしょー」ってアップグレード後に再起動してなかったのがいけないんじゃないかなと思っている( mysql_upgradeの時は自分で再起動してたけど、すっかり忘れていたんじゃないかなと)
mysql> CLONE INSTANCE FROM cloner@yyy.yyy.yyy.yyy:3306 IDENTIFIED BY 'xxxx';
Query OK, 0 rows affected (37 min 23.83 sec)
再起動後はすんなり通った。
レシピエントの方のエラーログには
2019-11-26T11:08:19.464385+09:00 7 [Note] [MY-011978] [InnoDB] Clone estimated size: 188.15 GiB Available space: 359.75 GiB
と書いてあったので、「何も設定しない(= ほぼ無制限にCLONEがリソースを欲しがる)状態」で85MB/sくらい。
m4.xlarge + 900IOPSなgp2のEBSなので「マスターでトラフィックを受けながらにしてはまあまあ?」みたいな感じはする。
ただしまあ dstatを見てたら結構iowait出てるしストレージもかなり読むし出ていくパケットも多いし…で、2台目やる時はちょっと制限しようかなとか思った(↓ はドナー側)
$ dstat -amrl
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system-- ------memory-usage----- --io/total- ---load-avg---
usr sys idl wai hiq siq| read writ| recv send| in out | int csw | used buff cach free| read writ| 1m 5m 15m
53 1 12 33 0 0| 90M 318k| 337k 90M| 0 0 | 20k 5285 |12.0G 0 1602M 1952M| 751 105 |5.57 6.21 5.83
62 1 7 30 0 0| 90M 378k| 220k 90M| 0 0 | 16k 5335 |12.0G 0 1602M 1953M| 752 118 |5.57 6.21 5.83
53 1 17 29 0 1| 89M 321k| 291k 89M| 0 0 | 18k 5428 |12.0G 0 1602M 1950M| 751 103 |5.57 6.21 5.83

2019-11-26T11:08:46.108105+09:00 112 [Note] [MY-013458] [InnoDB] Stage progress: 1% completed.
2019-11-26T11:10:31.546724+09:00 112 [Note] [MY-013458] [InnoDB] Stage progress: 11% completed.
制限するにはこの辺のパラメーターをいじるんだけれど、
複数個所いっぺんに変えるとわからなくなる(というか、なったので一度中断してやり直している)ので今回は clone_enable_compression = ONだけ変更して実行(↓もドナー側)
mysql> SET GLOBAL SEESSION clone_valid_donor_list = 'yyy.yyy.yyy.yyy:3306';
Query OK, 0 rows affected (0.00 sec)

mysql> SET GLOBAL clone_enable_compression = ON;
Query OK, 0 rows affected (0.00 sec)

mysql> CLONE INSTANCE FROM cloner@yyy.yyy.yyy.yyy:3306 IDENTIFIED BY 'xxxx';

----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system-- ------memory-usage----- --io/total-
usr sys idl wai hiq siq| read writ| recv send| in out | int csw | used buff cach free| read writ
50 0 46 4 0 0| 14M 2675k| 47k 4043k| 0 0 |7780 6601 |12.0G 0 2152M 1425M| 143 277
49 1 48 3 0 0| 16M 692k| 29k 4434k| 0 0 |5988 4010 |12.0G 0 2152M 1425M| 171 92.0
50 1 47 3 0 0| 12M 843k| 35k 3330k| 0 0 |7091 5590 |12.0G 0 2152M 1425M| 127 141

2019-11-26T12:17:54.871875+09:00 474 [Note] [MY-013458] [InnoDB] Stage progress: 1% completed.
2019-11-26T12:27:37.153130+09:00 474 [Note] [MY-013458] [InnoDB] Stage progress: 11% completed.
( ゚д゚)
いやー、IOがぐっと下がったのは良いけど、遅い遅い。
読み出し速度が14MB/secしか出てなくて単純計算6倍近くかかるってことは3時間かー…ごはんでも食べてこようそうしよう。
_人人人人人_
> 時は流れた <
 ̄Y^Y^Y^Y^Y^ ̄
さーてクローンどうなったかな。
mysql> CLONE INSTANCE FROM cloner@yyy.yyy.yyy.yyy:3306 IDENTIFIED BY 'xxxx';
Timeout, server 192.168.211.58 not responding.
Σ(゚д゚lll) VPN切れてる!?
2019-11-26T15:31:25.203117+09:00 474 [Note] [MY-013458] [InnoDB] Stage progress: 96% completed.
2019-11-26T15:33:30.443342+09:00 475 [Note] [MY-013458] [InnoDB] Clone set error ACK: 1317
2019-11-26T15:33:30.443418+09:00 475 [Note] [MY-013458] [InnoDB] Clone Set Error code: 1317 Saved Error code: 0
2019-11-26T15:33:30.443440+09:00 475 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: COM_ACK: Storage Ack.'
2019-11-26T15:33:30.443488+09:00 475 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: COM_RES_COMPLETE.'
2019-11-26T15:33:30.443902+09:00 475 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: COM_EXIT: Storage End.'
2019-11-26T15:33:30.443943+09:00 475 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: COM_RES_COMPLETE.'
2019-11-26T15:33:30.443959+09:00 475 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: Exiting clone protocol.'
2019-11-26T15:33:30.464396+09:00 474 [Note] [MY-013458] [InnoDB] Clone Set Error code: 1160 Saved Error code: 1317
2019-11-26T15:33:30.464443+09:00 474 [Note] [MY-013458] [InnoDB] Clone Set Error code: 1160 Saved Error code: 1317
2019-11-26T15:33:30.464465+09:00 474 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: COM_EXECUTE: Storage Execute: error: 1160: Got an error writing communication packets.'
2019-11-26T15:33:30.464491+09:00 474 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: Before sending COM_RES_ERROR: network : error: 1160: Got an error writing communication packets.'
2019-11-26T15:33:30.464566+09:00 474 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: After sending COM_RES_ERROR: error: 1160: Got an error writing communication packets.'
2019-11-26T15:33:30.464646+09:00 474 [Note] [MY-013458] [InnoDB] Clone Set Error code: 1317 Saved Error code: 1317
2019-11-26T15:33:30.464687+09:00 474 [Note] [MY-011846] [InnoDB] Clone Stop PAGE ARCH : end LSN : 1928777840909, log sys LSN : 1928826565652
2019-11-26T15:33:30.464922+09:00 474 [Note] [MY-013457] [InnoDB] Clone End Master Task ID: 0 Failed, code: 1317: Got an error writing communication packets
2019-11-26T15:33:30.464955+09:00 474 [Note] [MY-013273] [Clone] Plugin Clone reported: 'Server: Exiting clone protocol: error: 1160: Got an error writing communication packets.'
Σ(゚д゚lll)
というわけで今、圧縮しない設定でCLONE取り直してます。ちゃんとtmux使って。
機会があれば他のパラメーターも試す。

手探りでマイエスキューエルシェる!

$
0
0

TL;DR

  • ドキュメントを読むのが面倒だから “TAB” 補完でがんばってみた
    • 優秀


この記事は MySQL Advent Calendar 2019の1日目の記事です!

MySQL Shellは遊んでみたいけど、ドキュメントは読みたくない(面倒だから)
タブ補完だけでどんなメソッドがあるか探索しにいく!
$ mysqlsh
MySQL Shell 8.0.18

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.
MySQL JS > a^C
MySQL JS > break
break
MySQL JS > break^C
MySQL JS > c
case catch class const continue
MySQL JS > c^C
MySQL JS >
いかん、タブ補完だけで色々出てくるけど 俺にはそれがJS由来の何かなのか、MySQL Shellのなにかなのかがわからん…。
 MySQL  JS > d
db dba debugger default delete dir() do
MySQL JS > db
db dba
MySQL JS > db.^C
MySQL JS > dba.
checkInstanceConfiguration() deploySandboxInstance() rebootClusterFromCompleteOutage()
configureInstance() dropMetadataSchema() startSandboxInstance()
configureLocalInstance() getCluster() stopSandboxInstance()
createCluster() help() verbose
deleteSandboxInstance() killSandboxInstance()
MySQL JS > dba.^C
さすがに dbaはMySQL Shellだってわかるけどこれはしんどいかも知れない。
 MySQL  JS > db.help()
TypeError: Cannot read property 'help' of null

MySQL JS > dba.help()
NAME
dba - Global variable for InnoDB cluster management.

DESCRIPTION
The global variable dba is used to access the AdminAPI functionality and
perform DBA operations. It is used for managing MySQL InnoDB clusters.

PROPERTIES
verbose
Enables verbose mode on the dba operations.

FUNCTIONS
checkInstanceConfiguration(instance[, options])
Validates an instance for MySQL InnoDB Cluster usage.
..
見分け方としてはこの、 help()メソッドが生えてればMySQL Shell側、生えてなければJS側だと思えばいいのかな…
 MySQL  JS > dba.checkInstanceConfiguration()
Dba.checkInstanceConfiguration: An open session is required to perform this operation. (RuntimeError)

MySQL JS > dba.help('checkInstanceConfiguration')
NAME
checkInstanceConfiguration - Validates an instance for MySQL InnoDB
Cluster usage.

SYNTAX
dba.checkInstanceConfiguration(instance[, options])

WHERE
instance: An instance definition.
options: Data for the operation.
..
MySQL Shell由来のオブジェクトは help()メソッドに関数の名前をクォートして渡してやるとちゃんとドキュメントが生えてくる(?)っぽい。
あとは名前から何となく類推するくらいかなぁ…。
俺はドキュメント読みたくないけどこの手法でInnoDB Clusterサンドボックスの構築くらいはできるようになりました。タブ補完オススメ。

ConoHaの上でひたすらMySQLをビルドする簡単なおしごと in 2019年

$
0
0
この記事は ConoHa Advent Calendar 2019の7日目の記事です。

去年一昨年三年前とひたすらConoHa VPS 1GBプランでMySQLをビルドしています。
今年も変わらずビルドをするわけですが、去年と比べてMySQL 5.5系はMySQL 5.5.62のままだったので飛ばします。
MySQL 5.5は2018年12月でSustaining Support期間に入っているので、もう(Security Fixを含め)新しいリリースはないのでしょう。これがサポートが切れるということだ。
今年もサクッと新しいサーバーを立ち上げ…お、CentOSが8.0のがあるのでこれにしよう。
それではLet’s build.
$ wget https://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-5.6.46.tar.gz
$ tar xf mysql-5.6.46.tar.gz
$ cd mysql-5.6.46/
$ cmake .
-bash: cmake: command not found
ウッ。8系では dnfでインストールするんですよねおれしってる。
$ sudo dnf install cmake
$ cmake .
..
-- Running cmake version 3.11.4
-- Could NOT find Git (missing: GIT_EXECUTABLE)
CMake Error: CMake was unable to find a build program corresponding to "Unix Makefiles". CMAKE_MAKE_PROGRAM is not set. You probably need to select a different build tool.
CMake Error: CMAKE_C_COMPILER not set, after EnableLanguage
CMake Error: CMAKE_CXX_COMPILER not set, after EnableLanguage
-- Configuring incomplete, errors occurred!
See also "/home/yoku0825/mysql-5.6.46/CMakeFiles/CMakeOutput.log".
ウッ、 git, make, gcc, g++あたりがいないせいな気がする。
$ sudo dnf install git make gcc gcc-c++
$ cmake .
..
Cannot find appropriate system libraries for WITH_SSL=system.
Make sure you have specified a supported SSL version.
Valid options are :
system (use the OS openssl library),
yes (synonym for system),
</path/to/custom/openssl/installation>

CMake Error at cmake/ssl.cmake:66 (MESSAGE):
Please install the appropriate openssl developer package.

Call Stack (most recent call first):
cmake/ssl.cmake:260 (FATAL_SSL_NOT_FOUND_ERROR)
CMakeLists.txt:482 (MYSQL_CHECK_SSL)

-- Configuring incomplete, errors occurred!
See also "/home/yoku0825/mysql-5.6.46/CMakeFiles/CMakeOutput.log".
See also "/home/yoku0825/mysql-5.6.46/CMakeFiles/CMakeError.log".
最近のMySQLはYaSSLバンドルをやめてOpenSSL一本になったので、 openssl-develが入ってないとダメ、と。
$ sudo dnf install openssl-devel
$ cmake .
..
CMake Error at cmake/readline.cmake:92 (MESSAGE):
Curses library not found. Please install appropriate package,

remove CMakeCache.txt and rerun cmake.On Debian/Ubuntu, package name is libncurses5-dev, on Redhat and derivates it is ncurses-devel.
Call Stack (most recent call first):
cmake/readline.cmake:135 (FIND_CURSES)
cmake/readline.cmake:225 (MYSQL_USE_BUNDLED_EDITLINE)
CMakeLists.txt:484 (MYSQL_CHECK_EDITLINE)

-- Configuring incomplete, errors occurred!
See also "/home/yoku0825/mysql-5.6.46/CMakeFiles/CMakeOutput.log".
See also "/home/yoku0825/mysql-5.6.46/CMakeFiles/CMakeError.log".
今度は ncurses-devel(毎度転けさせてる気がするこれ)
$ sudo dnf install ncurses-devel
$ cmake .
..
-- Configuring done
-- Generating done
-- Build files have been written to: /home/yoku0825/mysql-5.6.46
よし cmake終わったので make
$ time make
..
real 24m56.940s
user 21m53.985s
sys 2m38.626s
いやあgccのバージョンが新しくなったせいか、ワーニングいっぱい出る…。
とはいえ無事にビルドも終わった。
$ sudo make install
$ du -sh /usr/local/mysql
1.1G /usr/local/mysql
これを5.7.28と8.0.18でもやるだけの簡単なおしごと。
$ wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-boost-5.7.28.tar.gz
$ tar xf mysql-boost-5.7.28.tar.gz
$ cd mysql-5.7.28/
$ cmake -DWITH_BOOST=./boost
..
-- Checking for module 'libtirpc'
-- Package 'libtirpc', required by 'virtual:world', not found
CMake Error at cmake/rpc.cmake:76 (MESSAGE):
Could not find rpc/rpc.h in /usr/include or /usr/include/tirpc
Call Stack (most recent call first):
rapid/plugin/group_replication/configure.cmake:60 (MYSQL_CHECK_RPC)
rapid/plugin/group_replication/CMakeLists.txt:25 (INCLUDE)

-- Configuring incomplete, errors occurred!
See also "/home/yoku0825/mysql-5.7.28/CMakeFiles/CMakeOutput.log".
See also "/home/yoku0825/mysql-5.7.28/CMakeFiles/CMakeError.log".
MySQL 5.7とそれ以降はビルドにBoostライブラリの特定のバージョンを要求するので、面倒がないBoost同梱版のソースをダウンロードしてきて指定してやる。
あれ何だこれ、見たことないやつが出た。ライブラリの方を要求してるっぽく見えて、Ncursesと同じでdevelが必要なやつっぽい。
$ sudo dnf install libtirpc
Package libtirpc-1.1.4-3.el8.x86_64 is already installed.

$ sudo dnf install libtirpc-devel
$ cmake -DWITH_BOOST=./boost
..
CMake Error at rapid/plugin/group_replication/rpcgen.cmake:100 (MESSAGE):
Could not find rpcgen
Call Stack (most recent call first):
rapid/plugin/group_replication/CMakeLists.txt:36 (INCLUDE)

-- Configuring incomplete, errors occurred!
See also "/home/yoku0825/mysql-5.7.28/CMakeFiles/CMakeOutput.log".
See also "/home/yoku0825/mysql-5.7.28/CMakeFiles/CMakeError.log".
ん-。色々新しく必要になるものが増えたのか、CentOS 7と8の違いなのかよくわからん。
$ sudo dnf --enablerepo=PowerTools install rpcgen
$ cmake -DWITH_BOOST=./boost
..
-- Configuring done
-- Generating done
-- Build files have been written to: /home/yoku0825/mysql-5.7.28
cmakeできたのでmake。
$ time make
..
real 67m32.487s
user 50m14.800s
sys 6m39.203s

$ sudo make install
$ du -sh /usr/local/mysql
2.3G /usr/local/mysql
最後はMySQL 8.0.18。
$ wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-boost-8.0.18.tar.gz
$ tar xf mysql-boost-8.0.18.tar.gz
$ cd mysql-8.0.18/
$ cmake -DWITH_BOOST=./boost
..
CMake Error at CMakeLists.txt:341 (MESSAGE):
Please do not build in-source. Out-of source builds are highly
recommended: you can have multiple builds for the same source, and there is
an easy way to do cleanup, simply remove the build directory (note that
'make clean' or 'make distclean' does *not* work)

You *can* force in-source build by invoking cmake with
-DFORCE_INSOURCE_BUILD=1

-- Configuring incomplete, errors occurred!
See also "/home/yoku0825/mysql-8.0.18/CMakeFiles/CMakeOutput.log".
あ、そうか、8.0はソースディレクトリの外側でビルドしないといけないんだった。
$ cd ..
$ mkdir work
$ cd work
$ cmake -DWITH_BOOST=../mysql-8.0.18/boost ../mysql-8.0.18
..
CMake Error at CMakeLists.txt:341 (MESSAGE):
Please do not build in-source. Out-of source builds are highly
recommended: you can have multiple builds for the same source, and there is
an easy way to do cleanup, simply remove the build directory (note that
'make clean' or 'make distclean' does *not* work)

You *can* force in-source build by invoking cmake with
-DFORCE_INSOURCE_BUILD=1
あれ、前と同じエラーが出る時はCMakeCache.txtかな。
$ rm ../mysql-8.0.18/CMakeCache.txt
$ cmake -DWITH_BOOST=../mysql-8.0.18/boost ../mysql-8.0.18
..
-- Configuring done
-- Generating done
-- Build files have been written to: /home/yoku0825/work
大正解。
$ time make
..
real 161m15.531s
user 110m1.582s
sys 15m43.989s

$ sudo make install
$ du -sh /usr/local/mysql
2.2G /usr/local/mysql

8.0.18、更に時間がかかるようになっていた。。
明日は 8mitsuさんです。

MySQL 8.0予約語問題にも使える、performance_schemaからエラーになったクエリーをたたき出す方法

$
0
0

TL;DR

  • events_statements_*テーブルmessage_textカラムにエラーメッセージが入っている
  • MySQL 5.6とそれ以降で使える
    • こんな便利なものに気が付いていなかった…

注意

予約語を踏み抜いたり、 GROUP BY .. ASCみたいなのはパースエラー( mysql_errno = 1064) で出てくるので、それを知ってるとユニットテストとかと組み合わせて予約語対応も捗るかも知れない。
mysql80 48> SELECT * FROM d1.t1 GROUP BY num ASC;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ASC' at line 1

mysql80 48> SELECT num AS rank FROM d1.t1 GROUP BY num ASC;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'rank FROM d1.t1 GROUP BY num ASC' at line 1

mysql80 48> SELECT thread_id, event_name, sql_text, current_schema, mysql_errno, message_text, errors, warnings FROM performance_schema.events_statements_history WHERE errors > 0 OR warnings > 0;
+-----------+---------------------+------------------------------------------------+----------------+-------------+----------------------------------------------------------------------------------------------------------------------------------+--------+----------+
| thread_id | event_name | sql_text | current_schema | mysql_errno | message_text | errors | warnings |
+-----------+---------------------+------------------------------------------------+----------------+-------------+----------------------------------------------------------------------------------------------------------------------------------+--------+----------+
| 111 | statement/sql/error | SELECT * FROM d1.t1 GROUP BY num ASC | NULL | 1064 | You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use | 1 | 0 |
| 111 | statement/sql/error | SELECT num AS rank FROM d1.t1 GROUP BY num ASC | NULL | 1064 | You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use | 1 | 0 |
+-----------+---------------------+------------------------------------------------+----------------+-------------+----------------------------------------------------------------------------------------------------------------------------------+--------+----------+
2 rows in set (0.00 sec)

MySQL 8.0時代のSET GLOBALとかSET PERSISTに必要な権限

$
0
0

TL;DR

  • オンライン変更は SYSTEM_VARIABLES_ADMIN権限。 SUPERは非推奨。
  • SET PERSIST_ONLYSYSTEM_VARIABLES_ADMIN権限 &&PERSIST_RO_VARIABLES_ADMIN権限

ふとコードを読んでいる時に SET PERSIST_ONLYには SUPER権限は絡んでなさそうなことに気が付いたので試してみる。
  /* for dynamic variables user needs SUPER_ACL or SYSTEM_VARIABLES_ADMIN */
if (!static_variable) {
if (!sctx->check_access(SUPER_ACL) &&
!(sctx->has_global_grant(STRING_WITH_LEN("SYSTEM_VARIABLES_ADMIN"))
.first)) {
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0),
"SUPER or SYSTEM_VARIABLES_ADMIN");
return 1;
}
} else {
/*
for static variables user needs both SYSTEM_VARIABLES_ADMIN and
PERSIST_RO_VARIABLES_ADMIN
*/
if (!(sctx->has_global_grant(STRING_WITH_LEN("SYSTEM_VARIABLES_ADMIN"))
.first &&
sctx->has_global_grant(STRING_WITH_LEN("PERSIST_RO_VARIABLES_ADMIN"))
.first)) {
my_error(ER_PERSIST_ONLY_ACCESS_DENIED_ERROR, MYF(0),
"SYSTEM_VARIABLES_ADMIN and PERSIST_RO_VARIABLES_ADMIN");
return 1;
}
}
こういう「なんの権限が必要なの」系は、何も権限のついてないアカウント実際に叩くのが一番検証が楽。
mysql80 100> SHOW GRANTS;
+--------------------------------------+
| Grants for yoku0825@% |
+--------------------------------------+
| GRANT USAGE ON *.* TO `yoku0825`@`%` |
+--------------------------------------+
1 row in set (0.00 sec)

mysql80 100> SET GLOBAL innodb_buffer_pool_size = 128 * 1024 * 1024;ERROR 1227 (42000): Access denied; you need (at least one of) the SUPER or SYSTEM_VARIABLES_ADMIN privilege(s) for this operation

mysql80 100> SET PERSIST innodb_buffer_pool_size = 128 * 1024 * 1024;ERROR 1227 (42000): Access denied; you need (at least one of) the SUPER or SYSTEM_VARIABLES_ADMIN privilege(s) for this operation

mysql80 100> SET PERSIST_ONLY innodb_buffer_pool_size = 128 * 1024 * 1024;
ERROR 3630 (42000): Access denied; you need SYSTEM_VARIABLES_ADMIN and PERSIST_RO_VARIABLES_ADMIN privileges for this operation
8.0導入の新しい権限の方は、権限違反の時にエラー1227じゃなくて3630になるのね。


コードのコメントには "static variables"とは書いてあるけど、ダイナミック変更可能なやつも PERSIST_ONLY ならこっちを通る。
mysql80 102> SHOW GRANTS;
+--------------------------------------+
| Grants for yoku0825@% |
+--------------------------------------+
| GRANT SUPER ON *.* TO `yoku0825`@`%` |
+--------------------------------------+
1 row in set (0.00 sec)

mysql80 102> SET GLOBAL innodb_buffer_pool_size = 128 * 1024 * 1024;
Query OK, 0 rows affected (0.00 sec)

mysql80 102> SET PERSIST innodb_buffer_pool_size = 128 * 1024 * 1024;
Query OK, 0 rows affected (0.02 sec)

mysql80 102> SET PERSIST_ONLY innodb_buffer_pool_size = 128 * 1024 * 1024;
ERROR 3630 (42000): Access denied; you need SYSTEM_VARIABLES_ADMIN and PERSIST_RO_VARIABLES_ADMIN privileges for this operation

mysql80 104> SHOW GRANTS;
+-------------------------------------------------------+
| Grants for yoku0825@% |
+-------------------------------------------------------+
| GRANT USAGE ON *.* TO `yoku0825`@`%` |
| GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO `yoku0825`@`%` |
+-------------------------------------------------------+
2 rows in set (0.00 sec)

mysql80 104> SET GLOBAL innodb_buffer_pool_size = 128 * 1024 * 1024;
Query OK, 0 rows affected (0.00 sec)

mysql80 104> SET PERSIST innodb_buffer_pool_size = 128 * 1024 * 1024;
Query OK, 0 rows affected (0.00 sec)

mysql80 104> SET PERSIST_ONLY innodb_buffer_pool_size = 128 * 1024 * 1024;
ERROR 3630 (42000): Access denied; you need SYSTEM_VARIABLES_ADMIN and PERSIST_RO_VARIABLES_ADMIN privileges for this operation

mysql80 106> SHOW GRANTS;
+-----------------------------------------------------------+
| Grants for yoku0825@% |
+-----------------------------------------------------------+
| GRANT USAGE ON *.* TO `yoku0825`@`%` |
| GRANT PERSIST_RO_VARIABLES_ADMIN ON *.* TO `yoku0825`@`%` |
+-----------------------------------------------------------+
2 rows in set (0.00 sec)

mysql80 106> SET GLOBAL innodb_buffer_pool_size = 128 * 1024 * 1024;
ERROR 1227 (42000): Access denied; you need (at least one of) the SUPER or SYSTEM_VARIABLES_ADMIN privilege(s) for this operation

mysql80 106> SET PERSIST innodb_buffer_pool_size = 128 * 1024 * 1024;
ERROR 1227 (42000): Access denied; you need (at least one of) the SUPER or SYSTEM_VARIABLES_ADMIN privilege(s) for this operation

mysql80 106> SET PERSIST_ONLY innodb_buffer_pool_size = 128 * 1024 * 1024;
ERROR 3630 (42000): Access denied; you need SYSTEM_VARIABLES_ADMIN and PERSIST_RO_VARIABLES_ADMIN privileges for this operation

mysql80 109> SHOW GRANTS;
+----------------------------------------------------------------------------------+
| Grants for yoku0825@% |
+----------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `yoku0825`@`%` |
| GRANT PERSIST_RO_VARIABLES_ADMIN,SYSTEM_VARIABLES_ADMIN ON *.* TO `yoku0825`@`%` |
+----------------------------------------------------------------------------------+
2 rows in set (0.00 sec)

mysql80 109> SET GLOBAL innodb_buffer_pool_size = 128 * 1024 * 1024;
Query OK, 0 rows affected (0.00 sec)

mysql80 109> SET PERSIST innodb_buffer_pool_size = 128 * 1024 * 1024;
Query OK, 0 rows affected (0.00 sec)

mysql80 109> SET PERSIST_ONLY innodb_buffer_pool_size = 128 * 1024 * 1024;
Query OK, 0 rows affected (0.00 sec)

mysql80 111> SHOW GRANTS;
+-----------------------------------------------------------+
| Grants for yoku0825@% |
+-----------------------------------------------------------+
| GRANT SUPER ON *.* TO `yoku0825`@`%` |
| GRANT PERSIST_RO_VARIABLES_ADMIN ON *.* TO `yoku0825`@`%` |
+-----------------------------------------------------------+
2 rows in set (0.00 sec)

mysql80 111> SET GLOBAL innodb_buffer_pool_size = 128 * 1024 * 1024;
Query OK, 0 rows affected (0.00 sec)

mysql80 111> SET PERSIST innodb_buffer_pool_size = 128 * 1024 * 1024;
Query OK, 0 rows affected (0.00 sec)

mysql80 111> SET PERSIST_ONLY innodb_buffer_pool_size = 128 * 1024 * 1024;
ERROR 3630 (42000): Access denied; you need SYSTEM_VARIABLES_ADMIN and PERSIST_RO_VARIABLES_ADMIN privileges for this operation
確かにそうなった。
個人的には SET PERSIST_ONLYだけじゃなくて SET PERSISTSUPERだけでできないようにしてくれると嬉しかったりするけれども。
監視用アカウントに SUPERがついて運用しているとこういう権限分離は嬉しい…。

Twitterの過去のツイート履歴をMySQLに取り込む in 2019

$
0
0

TL;DR

  • 過去のツイート履歴がCSVからJSに変わったっぽい
  • でも大丈夫、俺達には JSON_TABLE関数があるから

かつてはCSVだったツイート履歴、最近ダウンロードしてみたらJSファイルになっていた。しかもでかい。
$ mkdir work
$ cd work
$ unzip ../twitter-2019-12-20-ce0bbf92f327035a47c135f037e0568f6166df65f5f1011bc9d0bc2b6b9b6c3f.zip
..

$ du -sh .
239M .

$ ll -h tweet.js
-rwxrwxrwx 1 yoku0825 yoku0825 47M Dec 20 09:00 tweet.js
しかもこのJS、よしなにpretty printされていてグレッパビリティが悪い。
$ head tweet.js
window.YTD.tweet.part0 = [ {
"retweeted" : false,
"source" : "<a href=\"https://mobile.twitter.com\" rel=\"nofollow\">Twitter Web App</a>",
"entities" : {
"hashtags" : [ ],
"symbols" : [ ],
"user_mentions" : [ {
"name" : "坂井 恵(SAKAI Kei)",
"screen_name" : "sakaik",
"indices" : [ "0", "7" ],
今まで通り俺はテキトーにgrepしてtweet_idを引きたいんだ…!
という訳で、このtweet.jsをMySQLに突っ込むことにした。
先頭の window.YTD.tweet.part0 =はJSONとしてはInvalidになるのが目に見えているので、ここはあらかじめ削り取ってしまう。
$ jq . tweet.js > /dev/null
parse error: Invalid numeric literal at line 1, column 23
$ cp -ip tweet.js{,.orig}
$ sed -i 's/window.YTD.tweet.part0 = //' tweet.js
$ jq . tweet.js > /dev/null
取り敢えずMySQLは使い捨てで良いのでMySQL ShellのdeploySandboxInstanceでいっこ起動する。
$ mysqlsh
MySQL JS > dba.deploySandboxInstance(3306)
..
MySQL JS > \c root@localhost:3306
MySQL localhost:3306 ssl JS > \sql
Switching to SQL mode... Commands end with ;

MySQL localhost:3306 ssl SQL > CREATE DATABASE d1;
Query OK, 1 row affected (0.0291 sec)
取り敢えずガワになる d1スキーマを作ったあと、せっかくなので util.importJSONを使おうかと思ってJSモードに戻ってから叩いてみる。
 MySQL  localhost:3306 ssl  SQL > \js
Switching to JavaScript mode...

MySQL localhost:3306 ssl JS > util.importJson('./tweet.js')
Util.importJson: An X Protocol session is required for JSON import. (RuntimeError)
Xプロトコルじゃないと使えないらしいので、root@localhost:33060に接続しなおしてもう一度。
 MySQL  localhost:3306 ssl  JS > \c root@localhost:33060

MySQL localhost:33060+ ssl JS > util.importJson('./tweet.js')
Util.importJson: There is no active schema on the current session, the target schema for the import operation must be provided in the options. (RuntimeError)
no active schemaってことは USE d1的なものを押し込まないといけなさそうだけど、JSモードでやる方法がわからないのでSQLモードで USE d1してからJSモードに戻る。
わかんなかったSQLでやれるのは良いところなのかも知れない、マイエスキューエルシェル。
 MySQL  localhost:33060+ ssl  JS > \sql use d1
Query OK, 0 rows affected (0.0007 sec)

MySQL localhost:33060+ ssl d1 JS > util.importJson('./tweet.js')
Importing from file "./tweet.js" to collection `d1`.`tweet` in MySQL Server at localhost:33060

Processed 0 bytes in 0 documents in 0.0031 sec (0.00 documents/s)
Total successfully imported documents 0 (0.00 documents/s)
Util.importJson: Input does not start with a JSON object at offset 0 (ArgumentError)
失敗…。
 MySQL  localhost:33060+ ssl  d1  JS > \sql
Switching to SQL mode... Commands end with ;

MySQL localhost:33060+ ssl d1 SQL > SHOW TABLES;
+--------------+
| Tables_in_d1 |
+--------------+
| tweet |
+--------------+
1 row in set (0.0020 sec)

MySQL localhost:33060+ ssl d1 SQL > DESC tweet;
+-------+---------------+------+-----+---------+------------------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------------+------+-----+---------+------------------+
| doc | json | YES | | NULL | |
| _id | varbinary(32) | NO | PRI | NULL | STORED GENERATED |
+-------+---------------+------+-----+---------+------------------+
2 rows in set (0.0021 sec)

MySQL localhost:33060+ ssl d1 SQL > SELECT * FROM tweet;
Empty set (0.0010 sec)
なんかざっと見た感じ、 _idがgenerated columnでPRIMARY KEY(= NOT NULL)なので、そういうカラムが存在しないとダメな気がする。
ドキュメントを流し読んでも、 _idのカラム名を別のもの(tweet_idにできればいいような気がするのよね) に変える方法がパッと見えなかったのでさっさと諦めてSQLでやることにした。
 MySQL  localhost:33060+ ssl  d1  SQL > DROP TABLE tweet;
Query OK, 0 rows affected (0.0746 sec)

MySQL localhost:33060+ ssl d1 SQL > CREATE TABLE t1_tmp (val LONGTEXT);
Query OK, 0 rows affected (0.1394 sec)

MySQL localhost:33060+ ssl d1 SQL > INSERT INTO t1_tmp SELECT LOAD_FILE('/home/yoku0825/down/work/tweet.js');
Query OK, 1 row affected (0.0223 sec)

Records: 1 Duplicates: 0 Warnings: 0

MySQL localhost:33060+ ssl d1 SQL > SELECT * FROM t1_tmp LIMIT 1\G
*************************** 1. row ***************************
val: NULL
1 row in set (0.0005 sec)
ん、しまった、 secure_file_privがあるから LOAD_FILEがNULLになってる。
 MySQL  localhost:33060+ ssl  d1  SQL > SET GLOBAL secure_file_priv= '';
ERROR: 1238: Variable 'secure_file_priv' is a read only variable

MySQL localhost:33060+ ssl d1 SQL > SET PERSIST_ONLY secure_file_priv= '';
ERROR: 1238: Variable 'secure_file_priv' is a non persistent read only variable
できないのかYO…。
おとなしく my.cnf をいじって secure_file_priv = ""を押し込んで再起動。
 MySQL  localhost:33060+ ssl  d1  SQL > TRUNCATE t1_tmp;
Query OK, 0 rows affected (0.1484 sec)

MySQL localhost:33060+ ssl d1 SQL > INSERT INTO t1_tmp SELECT LOAD_FILE('/home/yoku0825/down/work/tweet.js');
Query OK, 1 row affected (4.8416 sec)

Records: 1 Duplicates: 0 Warnings: 0

MySQL localhost:33060+ ssl d1 SQL > \pager head -1000c
Pager has been set to 'head -1000c'.

MySQL localhost:33060+ ssl d1 SQL > SELECT * FROM t1_tmp\G
*************************** 1. row ***************************
val: [ {
"retweeted" : false,
"source" : "<a href=\"https://mobile.twitter.com\" rel=\"nofollow\">Twitter Web App</a>",
"entities" : {
"hashtags" : [ ],
"symbols" : [ ],
"user_mentions" : [ {
"name" : "坂井 恵(SAKAI Kei)",
"screen_name" : "sakaik",
"indices" : [ "0", "7" ],
"id_str" : "3677561",
"id" : "3677561"
} ],
"urls" : [ ]
},
"display_text_range" : [ "0", "27" ],
"favorite_count" : "0",
"in_reply_to_status_id_str" : "1207929574333403136",
"id_str" : "1207929799047385088",
"in_reply_to_user_id" : "3677561",
"truncated" : false,
"retweet_count" : "0",
"id" : "1207929799047385088",
"in_reply_to_status_id" : "1207929574333403136",
"created_at" : "Fri Dec 20 07:44:41 +0000 2019",
"favorited" : false,
"full_text" : "@sakaik なるほどミニ丸の方ですか、見てみます!",
"lang" : "ja",
"in_reply_to_screen_name" : "sakaik",
OK、入った。
ここからが本丸、 JSON_TABLE使う。
JSON_TABLE関数に、既にテーブルに入った値を使いたい時は、 FROM t1_tmp, JSON_TABLE(..)の形式でJOINっぽく書いてやる。
PATHの書き方がつらい感じだけどマニュアル首っ引きでやると
 MySQL  localhost:33060+ ssl  d1  SQL > SELECT tweet_id FROM t1_tmp, JSON_TABLE(val, '$[*]' COLUMNS (tweet_id BIGINT UNSIGNED PATH '$.id')) AS json LIMIT 10;
+---------------------+
| tweet_id |
+---------------------+
| 1207929799047385088 |
| 1207929689987153920 |
| 1207928245233971200 |
| 1207927537533251584 |
| 1207926165190504448 |
| 1207897765130326016 |
| 1207897362191941632 |
| 1207897143224098819 |
| 1207895949948768257 |
| 1207853754973749249 |
+---------------------+
10 rows in set (1.4292 sec)
よし、あたかもテーブル状に、1ツイートが1行の状態で手に入れられそう。
generated columnでパーツ分けはするとして、生JSONの状態で行に分けられればいいのでJSON_TABLEはこんな感じか。
 MySQL  localhost:33060+ ssl  d1  SQL > SELECT tweet_json FROM t1_tmp, JSON_TABLE(val, '$[*]' COLUMNS (tweet_json JSON PATH '$')) AS json LIMIT 1\G
*************************** 1. row ***************************
tweet_json: {"id": "1207929799047385088", "lang": "ja", "id_str": "1207929799047385088", "source": "<a href=\"https://mobile.twitter.com\" rel=\"nofollow\">Twitter Web App</a>", "entities": {"urls": [], "symbols": [], "hashtags": [], "user_mentions": [{"id": "3677561", "name": "坂井 恵(SAKAI Kei)", "id_str": "3677561", "indices": ["0", "7"], "screen_name": "sakaik"}]}, "favorited": false, "full_text": "@sakaik なるほどミニ丸の方ですか、見てみます!", "retweeted": false, "truncated": false, "created_at": "Fri Dec 20 07:44:41 +0000 2019", "retweet_count": "0", "favorite_count": "0", "display_text_range": ["0", "27"], "in_reply_to_user_id": "3677561", "in_reply_to_status_id": "1207929574333403136", "in_reply_to_screen_name": "sakaik", "in_reply_to_user_id_str": "3677561", "in_reply_to_status_id_str": "1207929574333403136"}
1 row in set (1.8848 sec)
よし、じゃあテーブルに突っ込もう。
 MySQL  localhost:33060+ ssl  d1  SQL > CREATE TABLE t1 (tweet_json JSON);
Query OK, 0 rows affected (0.0843 sec)
MySQL localhost:33060+ ssl d1 SQL > INSERT INTO t1 SELECT tweet_json FROM t1_tmp, JSON_TABLE(val, '$[*]' COLUMNS (tweet_json JSON PATH '$')) AS json;
Query OK, 55925 rows affected (4.3389 sec)

Records: 55925 Duplicates: 0 Warnings: 0
generated columnでテキトーに切り分けていくますよ。
 MySQL  localhost:33060+ ssl  d1  SQL > ALTER TABLE t1 ADD tweet_id BIGINT UNSIGNED AS (tweet_json->>'$.id'),                ADD full_text TEXT AS (tweet_json->>'$.full_text'),                ADD created_at VARCHAR(50) AS (tweet_json->>'$.created_at');
Query OK, 0 rows affected (0.0508 sec)

Records: 0 Duplicates: 0 Warnings: 0
MySQL localhost:33060+ ssl d1 SQL >
MySQL localhost:33060+ ssl d1 SQL >
MySQL localhost:33060+ ssl d1 SQL > SELECT tweet_id, full_text, created_at FROM t1 LIMIT 3;
+---------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------+
| tweet_id | full_text | created_at |
+---------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------+
| 1207929799047385088 | @sakaik なるほどミニ丸の方ですか、見てみます! | Fri Dec 20 07:44:41 +0000 2019 |
| 1207929689987153920 | @sakaik 違うOS向けのバイナリを動かそうとよく出るやつっぽいので、インストールするパッケージが間違っていないか気になります

RHEL 7.x用のパッケージを無理矢理RHEL 6.xにインストールするとかそんなかんじです! | Fri Dec 20 07:44:14 +0000 2019 |
| 1207928245233971200 | @sakaik パッケージものだとすると、サポート対象外になってglibcのバージョンが引きあがったとかありそうですが

https://t.co/MipGicihkr | Fri Dec 20 07:38:30 +0000 2019 |
+---------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------
created_atはDATETIME型にキャストしようとしたらできなかったので取り敢えずVARCHARで我慢するとして、これで取り敢えず今まで通り1行1ツイートでゴニョゴニョできるようになった。
in_reply_to_status_idとか切り出せば、再帰CTEでそのスレッドを辿れるな、とか思いつつ取り敢えずここまで。

CentOS 8.0にしたらいくつかのURLに error:141A318A:SSL routines:tls_process_ske_dhe:dh key too small でアクセスできなくなった

$
0
0

TL;DR

$ sudo update-crypto-policies --set LEGACY

CentOS 7.xのVMを壊してしまったのでCentOS 8.xのものにお引越ししていたら
$ curl -I https://www8.cao.go.jp
curl: (35) error:141A318A:SSL routines:tls_process_ske_dhe:dh key too small
こんな風になっていくつかアクセスできないURLが。
こちらをがたぶんそのまんまで、CentOS 8.0で入ってくるのが openssl 1.1.1だから鍵長が短いのをリジェクトしているっぽい。内閣府さん…(www.cao.go.jpの方はいけるんだけどwww8.cao.go.jpの方がダメだった…)
$ rpm -q openssl
openssl-1.1.1-8.el8.x86_64
どうやってさまよってたどり着いたのか全く覚えていないけど、
を眺めていたら crypt-policiesというのが設定できるらしく、これをLEGACYにすれば1024ビットのDHパラメーターでも許してくれるらしい。
$ sudo update-crypto-policies --set LEGACY
Setting system policy to LEGACY
Note: System-wide crypto policies are applied on application start-up.
It is recommended to restart the system for the change of policies
to fully take place.

$ curl -I https://www8.cao.go.jp
HTTP/1.1 200 OK
Date: Tue, 24 Dec 2019 06:45:17 GMT
Server: Apache
X-Frame-Options: SAMEORIGIN
Last-Modified: Tue, 20 Nov 2018 08:06:15 GMT
ETag: "1f10-57b141b87cbc0"
Accept-Ranges: bytes
Content-Length: 7952
Cache-Control: no-store
Expires: Tue, 24 Dec 2019 06:45:17 GMT
Pragma: no-cache
Content-Type: text/html
いったー。
自己責任でどうぞ。

explicit_defaults_for_timstampのONとOFFでエラーになるケースならないケース

$
0
0

TL;DR

explicit_defaults_fot_timestampカラムのデフォルト値INSERTでカラム未指定INSERTでNULLを指定
ONなしエラーエラー
ONありOKエラー
OFFなしエラーOK
OFFありOKOK

explicit_defaults_for_timestampというパラメーターがあって、これは「昔の古いTIMESTAMP型の挙動、SQL標準じゃないからちゃんとエラーにするようにする」というパラメーター。
これ自体は5.6で導入されていて、 “explicit_defaults_for_timestamp” でググるとワーニングの消し方(OFFだとエラーログにワーニングが出るので、ONにすれば消えるよ、というネタ)はいっぱい出てくるんだけど、結局どんなクエリーが影響を受けるのかイマイチまとまってるのを見つけられなかったのでメモ。
MySQL 8.0からはデフォルトでONになったことだし。

デフォルト値なしのカラム定義

mysql57 5> SHOW CREATE TABLE t1\G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`num` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`ts` timestamp NOT NULL,
UNIQUE KEY `num` (`num`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

explicit_defaults_for_timestamp = OFF

mysql57 5> SELECT @@session.explicit_defaults_for_timestamp;
+-------------------------------------------+
| @@session.explicit_defaults_for_timestamp |
+-------------------------------------------+
| 0 |
+-------------------------------------------+
1 row in set (0.00 sec)

mysql57 5> INSERT INTO t1 VALUES (1, NULL);
Query OK, 1 row affected (0.00 sec)

mysql57 5> INSERT INTO t1 (num) VALUES (2);
ERROR 1364 (HY000): Field 'ts' doesn't have a default value

mysql57 5> SELECT * FROM t1;
+-----+---------------------+
| num | ts |
+-----+---------------------+
| 1 | 2020-01-10 19:13:54 |
+-----+---------------------+
1 row in set (0.00 sec)

explicit_defaults_for_timestamp = ON

mysql57 5> SELECT @@session.explicit_defaults_for_timestamp;
+-------------------------------------------+
| @@session.explicit_defaults_for_timestamp |
+-------------------------------------------+
| 1 |
+-------------------------------------------+
1 row in set (0.00 sec)

mysql57 5> INSERT INTO t1 VALUES (1, NULL);
ERROR 1048 (23000): Column 'ts' cannot be null

mysql57 5> INSERT INTO t1 (num) VALUES (2);
ERROR 1364 (HY000): Field 'ts' doesn't have a default value

デフォルト値ありのカラム定義

mysql57 5> SHOW CREATE TABLE t1\G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`num` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY `num` (`num`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

explicit_defaults_for_timestamp = OFF

mysql57 5> SELECT @@session.explicit_defaults_for_timestamp;
+-------------------------------------------+
| @@session.explicit_defaults_for_timestamp |
+-------------------------------------------+
| 0 |
+-------------------------------------------+
1 row in set (0.00 sec)

mysql57 5> INSERT INTO t1 VALUES (1, NULL);
Query OK, 1 row affected (0.00 sec)

mysql57 5> INSERT INTO t1 (num) VALUES (2);
Query OK, 1 row affected (0.00 sec)

mysql57 5> SELECT * FROM t1;
+-----+---------------------+
| num | ts |
+-----+---------------------+
| 1 | 2020-01-10 19:18:24 |
| 2 | 2020-01-10 19:18:27 |
+-----+---------------------+
2 rows in set (0.00 sec)

explicit_defaults_for_timestamp = ON

mysql57 5> SELECT @@session.explicit_defaults_for_timestamp;
+-------------------------------------------+
| @@session.explicit_defaults_for_timestamp |
+-------------------------------------------+
| 1 |
+-------------------------------------------+
1 row in set (0.00 sec)

mysql57 5> INSERT INTO t1 VALUES (1, NULL);
ERROR 1048 (23000): Column 'ts' cannot be null

mysql57 5> INSERT INTO t1 (num) VALUES (2);
Query OK, 1 row affected (0.00 sec)

mysql57 5> SELECT * FROM t1;
+-----+---------------------+
| num | ts |
+-----+---------------------+
| 2 | 2020-01-10 19:18:08 |
+-----+---------------------+
1 row in set (0.00 sec)
というわけで最初のまとめどおり、
explicit_defaults_fot_timestampカラムのデフォルト値INSERTでカラム未指定INSERTでNULLを指定
ONなしエラーエラー
ONありOKエラー
OFFなしエラーOK
OFFありOKOK

MySQL WorkbenchからのLOAD DATA LOCAL INFILEが失敗する場合

$
0
0

TL;DR

“Edit Connection” -> “Connection” -> “Advanced” -> “Others” のところに OPT_LOCAL_INFILE=1


日本MySQLユーザ会のメーリングリストに寄せられたお便り(?) mysql:16565の件なんですが、添付ファイルがML Archiveから上手く見えていなかったのでブログ版です。
“Error Code: 3948. Loading local data is disabled; this must be enabled on both the client and server sides” または “Error Code: 1148. The used command is not allowed with this MySQL version” で失敗する場合のはなし。

カラムの数が違うとか値が不正だとけ権限が足りない系のエラーの対処ではないので注意。
というか TL;DRに書いたことが全てなので特に書くことなかった。。

InnoDB ClusterはPKがないテーブルに対する更新を "ERROR 3098 (HY000): The table does not comply with the requirements by an external plugin."のエラーにする

$
0
0

TL;DR

  • InnoDB Cluster(というかGroup Replication)環境で ERROR 3098 (HY000): The table does not comply with the requirements by an external plugin.と言われたら、テーブルにPRIMARY KEYがあるかどうかを疑ってみても良いかも
  • InnoDB Cluster環境に mysqlslap --auto-generate-sqlを叩き込もうとしたら気が付いた
  • このエラーメッセージを使っているのはMySQL 8.0.19現在ではGroup Replicationくらいだけれど、このエラーを返すインターフェイス自体は汎用的に作られているので将来は変わる鴨

InnoDB Clusterで遊ぼうと テキトーにMySQL ShellでInnoDB Clusterを作ってmysqlslapを叩いたらこれ。
$ mysqlslap80 -P3306 -h127.0.0.1 -uroot --auto-generate-sql --auto-generate-sql-execute-number=10000 --auto-generate-sql-load-type=l
/usr/mysql/8.0.19/bin/mysqlslap: Cannot run query INSERT INTO t1 VALUES (1804289383,'mxvtvmC9127qJNm06sGB8R92q2j7vTiiITRDGXM9ZLzkdekbWtmXKwZ2qG1llkRw5m9DHOFilEREk3q7oce8O3BEJC0woJsm6uzFAEynLH2xCsw1KQ1lT4zg9rdxBL') ERROR : The table does not comply with the requirements by an external plugin.
前にも同じエラーに当たったことがあるような気がしつつ、取り敢えず手で叩いても同じエラーになるかどうかをかくにn
$ mysql80 -P3306 -h127.0.0.1 -uroot

mysql80 52> use mysqlslap
Database changed

mysql80 52> show tables;
+---------------------+
| Tables_in_mysqlslap |
+---------------------+
| t1 |
+---------------------+
1 row in set (0.03 sec)

mysql80 52> SHOW CREATE TABLE t1\G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`intcol1` int DEFAULT NULL,
`charcol1` varchar(128) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
この時点でオチに気が付いてしまった。PRIMARY KEYがない。
mysql80 52> INSERT INTO t1 VALUES (1804289383,'mxvtvmC9127qJNm06sGB8R92q2j7vTiiITRDGXM9ZLzkdekbWtmXKwZ2qG1llkRw5m9DHOFilEREk3q7oce8O3BEJC0woJsm6uzFAEynLH2xCsw1KQ1lT4zg9rdxBL');
ERROR 3098 (HY000): The table does not comply with the requirements by an external plugin.

$ perror 3098
MySQL error code MY-003098 (ER_BEFORE_DML_VALIDATION_ERROR): The table does not comply with the requirements by an external plugin.
エラーコード的には Before DML Vlidation Errorらしいので、Group Replicationに限らず返す ここのフックを通れば同じエラーが返る可能性はある。
というか、テストスイートの中に replication_observers_example_before_dmlとかいうのがあってその中でこのエラーを期待しているらしいので、レプリケーションプラグイン全般で使うことを想定しているのであろう、たぶん。
$ grep -r ER_BEFORE_DML_VALIDATION_ERROR
sql/sql_base.cc: ER_BEFORE_DML_VALIDATION_ERROR
sql/sql_base.cc: if (out_value) my_error(ER_BEFORE_DML_VALIDATION_ERROR, MYF(0));
Binary file GRTAGS matches
mysql-test/suite/rpl/t/rpl_replication_observers_example_before_dml.test:--error ER_BEFORE_DML_VALIDATION_ERROR
mysql-test/suite/rpl/t/rpl_replication_observers_example_before_dml.test:--error ER_BEFORE_DML_VALIDATION_ERROR
mysql-test/suite/rpl/t/rpl_replication_observers_example_before_dml.test:--error ER_BEFORE_DML_VALIDATION_ERROR
mysql-test/suite/rpl/t/rpl_replication_observers_example_before_dml.test:--error ER_BEFORE_DML_VALIDATION_ERROR
mysql-test/suite/rpl/t/rpl_replication_observers_example_before_dml.test:--error ER_BEFORE_DML_VALIDATION_ERROR
mysql-test/suite/rpl/t/rpl_replication_observers_example_before_dml.test:--error ER_BEFORE_DML_VALIDATION_ERROR
mysql-test/suite/rpl/t/rpl_replication_observers_example_before_dml.test:--error ER_BEFORE_DML_VALIDATION_ERROR
mysql-test/suite/rpl/t/rpl_replication_observers_example_before_dml.test:--error ER_BEFORE_DML_VALIDATION_ERROR
mysql-test/suite/rpl/t/rpl_replication_observers_example_before_dml.test:--error ER_BEFORE_DML_VALIDATION_ERROR
^C
sql_require_primary_keyと違って分かりやすいエラーが返るわけではないことにだけ注意。
mysql80 20> SELECT @@session.sql_require_primary_key;
+-----------------------------------+
| @@session.sql_require_primary_key |
+-----------------------------------+
| 1 |
+-----------------------------------+
1 row in set (0.00 sec)

mysql80 20> CREATE TABLE t3 (num int);
ERROR 3750 (HY000): Unable to create or change a table without a primary key, when the system variable 'sql_require_primary_key' is set. Add a primary key to the table or unset this variable to avoid this message. Note that tables without a primary key can cause performance problems in row-based replication, so please consult your DBA before changing this setting.
いないとは思うけれど、InnoDB Clusterに mysqlslap --auto-generate-sqlを叩き込みたい人は --auto-generate-sql-guid-primaryオプションを足せばPRIMARY KEY用のカラムを作るのでちゃんと通るようになるますよ。
$ mysqlslap80 -P3306 -h127.0.0.1 -uroot --auto-generate-sql --auto-generate-sql-execute-number=10000 --auto-generate-sql-load-type=l --auto-generate-sql-guid-primary
Benchmark
Average number of seconds to run all queries: 19.517 seconds
Minimum number of seconds to run all queries: 19.517 seconds
Maximum number of seconds to run all queries: 19.517 seconds
Number of clients running queries: 1
Average number of queries per client: 10000

CentOS 7のAMIでEC2を起動してGroup Replicationを組むところまでを何も考えずに

$
0
0

TL;DR

  • 毎回ちょこちょこ打ち込むのが面倒になったのでコピペ用に
  • まだGroup Replicationを調べるのがメインなのでMySQL Routerはどうにもしてない

Group Replicationに加わる全てのノードで

sudo -i
setenforce 0
yum install -y https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
yum install -y mysql-community-server mysql-shell
mysqld --initialize-insecure --user=mysql
systemctl start mysqld

cluster_user="gradmin"
clusetr_pass="grtest"
cluster_name="myfabric"

mysqlsh -- shell.options set_persist history.autoSave true
mysqlsh --uri=root:""@localhost -- dba configureInstance '' { --clusterAdmin=$cluster_user --clusterAdminPassword=$cluseter_pass --interactive=false --restart=true }
until mysqladmin ping ; do
sleep 1
done

最初の1台になるノードでだけ

  • 変数は引き継いでるつもりで
mysqlsh --uri=$cluster_user:$cluster_pass@localhost -- dba createCluster $cluster_name

### 追加したいノードぶんこれやる
node=xxx.xxx.xxx.xxx
mysqlsh --uri=$cluster_user:$cluster_pass@localhost --cluster -- cluster addInstance "$cluster_user:$cluster_pass@$node" { --recoveryMethod=clone }

忘れそうなポイントだけ。
  • mysqlsh --cluster
    • 接続先がInnoDB Clusterに属していれば、 cluster = dba.getCluster()相当のことを勝手にやってくれるのでいきなり clusterオブジェクトが使える
  • mysqlsh -- ..
    • 名前空間を区切っているドットは書かない。
    • MySQL Shell内部だと {"param":"value","hoge":"fuga"}みたいな形式で渡すやつは不思議形式で記述しないといけなくなる
      • { --param=value --hoge=fuga }

MultiPrimaryModeのGroup Replication環境を崩壊させるテスト

$
0
0

TL;DR

  • 完全崩壊した時の復旧シナリオを考えたりするには、やっぱり崩壊した状態を再現させられると便利だよね
  • cluster.switchToMultiPrimaryMode()してから2つの別のノードに「1回目は成功するけど2回流すと必ず失敗するALTER TABLE」を投げると崩壊させられる

node1> ALTER TABLE sbtest.sbtest1 ADD KEY idx_pad (pad);
Query OK, 0 rows affected, 1 warning (10.36 sec)
Records: 0 Duplicates: 0 Warnings: 1

-- 5秒くらい待つ

node2> ALTER TABLE sbtest.sbtest1 ADD KEY idx_pad (pad);
Query OK, 0 rows affected, 1 warning (10.60 sec)
Records: 0 Duplicates: 0 Warnings: 1
  1. node1のALTER TABLEが終わり、 node2node3に渡される
  2. node2では手で打ったALTER TABLEがメタデータをロックしているので node1から渡ってきたALTER TABLEは待たされる
  3. node3では node1由来のALTER TABLEが実行される
  4. node2の手で打ったALTER TABLEが終わり、 node1由来のALTER TABLEを適用しようとするが同じ名前のインデックスは作成できないのでエラー
  5. node2由来のALTER TABLEが node1node3に到達して、やっぱり同じ名前のインデックスは作成できないのでエラー
結果として完全崩壊する。
### node1
$ mysql -e "SELECT member_host, member_state, member_role FROM performance_schema.replication_group_members"
+------------------------------+--------------+-------------+
| member_host | member_state | member_role |
+------------------------------+--------------+-------------+
| node1 | ERROR | |
| node2 | ONLINE | PRIMARY |
| node3 | ONLINE | PRIMARY |
+------------------------------+--------------+-------------+

### node2
$ mysql -e "SELECT member_host, member_state, member_role FROM performance_schema.replication_group_members"
+-----------------------------+--------------+-------------+
| member_host | member_state | member_role |
+-----------------------------+--------------+-------------+
| node2 | ONLINE | PRIMARY |
+-----------------------------+--------------+-------------+

### node3
$ mysql -e "SELECT member_host, member_state, member_role FROM performance_schema.replication_group_members"
+------------------------------+--------------+-------------+
| member_host | member_state | member_role |
+------------------------------+--------------+-------------+
| node1 | ONLINE | PRIMARY |
| node2 | ONLINE | PRIMARY |
| node3 | ERROR | |
+------------------------------+--------------+-------------+
エラーログはどのノードも同じようなことを言っていた。
2020-02-21T09:10:32.562676Z 13 [ERROR] [MY-011451] [Repl] Plugin group_replication reported: 'The applier thread execution was aborted. Unable to process more transactions, this member will now leave the group.'
2020-02-21T09:10:32.562690Z 13 [ERROR] [MY-010586] [Repl] Error running query, slave SQL thread aborted. Fix the problem, and restart the slave SQL thread with "SLAVE START". We stopped at log 'FIRST' position 0
2020-02-21T09:10:32.562921Z 9 [ERROR] [MY-011452] [Repl] Plugin group_replication reported: 'Fatal error during execution on the Applier process of Group Replication. The server will now leave the group.'
2020-02-21T09:10:32.562975Z 9 [ERROR] [MY-011712] [Repl] Plugin group_replication reported: 'The server was automatically set into read only mode after an error was detected.'
ここまで来るともうまともにMySQL ShellやGroup Replication関連のステートメントが使えなくなるので、たっぷり復旧方法を考えたり試したりできる。

InnoDB Clusterの全ノードを正常に停止させたあとの復旧方法

$
0
0

TL;DR

  • MySQL Shellで dba.rebootClusterFromCompleteOutage()

深く考えずにGroup Replicationの全ノードを停止すると、いざ次回起動した時に
2020-02-25T09:14:08.497656Z 0 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] Error on opening a connection to xxx.xxx.xxx.xxx:33061 on local port: 33061.'
のようなエラーを吐き続けて最終的に
2020-02-25T09:14:08.497685Z 0 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] Error connecting to all peers. Member join failed. Local port: 33061'
2020-02-25T09:14:09.500209Z 0 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] The member was unable to join the group. Local port: 33061'
2020-02-25T09:14:12.789547Z 2 [ERROR] [MY-011640] [Repl] Plugin group_replication reported: 'Timeout on wait for view after joining group'
2020-02-25T09:14:12.789633Z 2 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] The member is leaving a group without being on one.'
GRの起動に失敗する。
タイムアウトとは言っているけれど、「過半数を満たしたグループに接続しようと思ったけれど、接続先がみんな過半数を満たしたグループにいない」からこうなっているんだとは思う。
### node1 
$ mysql -e "SELECT member_host, member_state, member_role FROM performance_schema.replication_group_members"
+------------------------------+--------------+-------------+
| member_host | member_state | member_role |
+------------------------------+--------------+-------------+
| node1 | OFFLINE | |
+------------------------------+--------------+-------------+

### node2
$ mysql -e "SELECT member_host, member_state, member_role FROM performance_schema.replication_group_members"
+------------------------------+--------------+-------------+
| member_host | member_state | member_role |
+------------------------------+--------------+-------------+
| node2 | OFFLINE | |
+------------------------------+--------------+-------------+

### node3
$ mysql -e "SELECT member_host, member_state, member_role FROM performance_schema.replication_group_members"
+------------------------------+--------------+-------------+
| member_host | member_state | member_role |
+------------------------------+--------------+-------------+
| node3 | OFFLINE | |
+------------------------------+--------------+-------------+
performance_schema.replication_group_membersをのぞき込む限りはみんな自分のことしか見えてない。
 MySQL  localhost:33060+ ssl  JS > dba.getCluster()
Dba.getCluster: This function is not available through a session to a standalone instance (metadata exists, instance belongs to that metadata, but GR is not active) (RuntimeError)
MySQL Shellもこの通りエラるが、どれか1台のノードで dba.rebootClusterFromCompleteOutage()を実行すればOK。
 MySQL  localhost:33060+ ssl  JS > dba.rebootClusterFromCompleteOutage()
Reconfiguring the default cluster from complete outage...

The instance 'node2:3306' was part of the cluster configuration.
Would you like to rejoin it to the cluster? [y/N]: y

The instance 'node3:3306' was part of the cluster configuration.
Would you like to rejoin it to the cluster? [y/N]: y

Disabling super_read_only mode on instance 'node1:3306'.
The cluster was successfully rebooted.

<Cluster:myfabric>
残っているメタデータから「他のノードもこのクラスターにrejoinさせる?」と聞いてくれる充実っぷりなので復旧(というのかこの場合)はらくちん。

壊れはしなかった(オフラインの間にゴニョゴニョしたのが競合してしまった…)

MySQL 8.0.19現在のGroup Replicationで空パスワードのアカウントの認証プラグインだけを変えようとすると変になる

$
0
0

TL;DR

  • epelのsysbenchがcaching_sha2_passwordに対応してないので、root@localhostのパスワードを空のまま認証プラグインだけmysql_native_passwordに変更しようとした
  • プライマリーノード以外ではパスワードがEXPIREされて再変更を促された
  • プライマリーノードで SET PASSWORD = ''を実行したらセカンダリーノードでもEXPIRE状態じゃなくなった

パスワードが空っぽの時だけ再現するので、現用環境で問題になる可能性は低い。
再現手順。
mysql> CREATE USER yoku0825 IDENTIFIED BY '';
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT user, host, plugin, password_expired FROM mysql.user WHERE user = 'yoku0825';
+----------+------+-----------------------+------------------+
| user | host | plugin | password_expired |
+----------+------+-----------------------+------------------+
| yoku0825 | % | caching_sha2_password | N |
+----------+------+-----------------------+------------------+
1 row in set (0.00 sec)

mysql> ALTER USER yoku0825 IDENTIFIED WITH mysql_native_password BY ''; -- IDENTIFIED WITH .. BY .. で認証プラグインと一緒に新パスワードを指定しているので password_expired は Nになるはず
Query OK, 0 rows affected (0.01 sec)

primary> SELECT user, host, plugin, password_expired FROM mysql.user WHERE user = 'yoku0825'; -- PRIMARYノードでは確かにそうなっているが
+----------+------+-----------------------+------------------+
| user | host | plugin | password_expired |
+----------+------+-----------------------+------------------+
| yoku0825 | % | mysql_native_password | N |
+----------+------+-----------------------+------------------+
1 row in set (0.00 sec)

secondary> SELECT user, host, plugin, password_expired FROM mysql.user WHERE user = 'yoku0825'; -- SECONDARYノードでは認証プラグインは更新されているもののpassword_exipred が Y、認証プラグインが変更された後にパスワードが更新されていないと認識されている
+----------+------+-----------------------+------------------+
| user | host | plugin | password_expired |
+----------+------+-----------------------+------------------+
| yoku0825 | % | mysql_native_password | Y |
+----------+------+-----------------------+------------------+
1 row in set (0.00 sec)
group_replication_applierのリレーログを確認すると
# at 13356
#200226 2:48:29 server id 2412749166 end_log_pos 0 GTID last_committed=61 sequence_number=62 rbr_only=no original_committed_timestamp=1582687438763140 immediate_commit_timestamp=0 transaction_length=229
# original_commit_timestamp=1582687438763140 (2020-02-26 03:23:58.763140 UTC)
# immediate_commit_timestamp=0 (1970-01-01 00:00:00.000000 UTC)
/*!80001 SET @@session.original_commit_timestamp=1582687438763140*//*!*/;
/*!80014 SET @@session.original_server_version=80019*//*!*/;
/*!80014 SET @@session.immediate_server_version=80019*//*!*/;
SET @@SESSION.GTID_NEXT= '662c9473-5842-11ea-8c75-12458a6f001d:66'/*!*/;
# at 13436
#200226 3:23:58 server id 2412749166 end_log_pos 0 Query thread_id=65 exec_time=0 error_code=0 Xid = 541
SET TIMESTAMP=1582687438/*!*/;
ALTER USER 'yoku0825'@'%' IDENTIFIED WITH 'mysql_native_password'
/*!*/;
空文字の部分が握りつぶされている。
group_replication_applierはバイナリログを受け取っているわけではない(んですよ実は)ので見ても仕方ないような気がするけれど、プライマリーノードのバイナリログを覗いても同じように握りつぶされているので、リレーログとして空文字パスワードが省略されるのは仕様としては正しそう。
# at 37750
#200226 3:23:58 server id 2412749166 end_log_pos 37830 GTID last_committed=65 sequence_number=66 rbr_only=no original_committed_timestamp=1582687438763140 immediate_commit_timestamp=1582687438765388 transaction_length=229
# original_commit_timestamp=1582687438763140 (2020-02-26 03:23:58.763140 UTC)
# immediate_commit_timestamp=1582687438765388 (2020-02-26 03:23:58.765388 UTC)
/*!80001 SET @@session.original_commit_timestamp=1582687438763140*//*!*/;
/*!80014 SET @@session.original_server_version=80019*//*!*/;
/*!80014 SET @@session.immediate_server_version=80019*//*!*/;
SET @@SESSION.GTID_NEXT= '662c9473-5842-11ea-8c75-12458a6f001d:66'/*!*/;
# at 37830
#200226 3:23:58 server id 2412749166 end_log_pos 37979 Query thread_id=65 exec_time=0 error_code=0 Xid = 541
SET TIMESTAMP=1582687438/*!*/;
ALTER USER 'yoku0825'@'%' IDENTIFIED WITH 'mysql_native_password'
/*!*/;
しかしそうなると、SQLを実行したノードではSQLをパースしている段階で IDENTIFIED BY ''が同時指定されているからパスワードを更新したことになり、それ以外(セカンダリーノードだったり、フツーのレプリケーションのスレーブだったり)は認証プラグインの更新後にパスワードの変更が行われてないように見えるからEXPIREする、と。
「認証プラグインを変更しつつパスワードを空文字にする」なんてことがない限りは着火しないので、検証環境以外でお目にかかることはないはず。たぶん。
( ´-`).oO(アカウントのもとのパスワードがNotからっぽだとマスターとスレーブの authentication_stringがズレるから、 password_require_currentが有効だとレプリケーションが死にそうな予感…

InnoDB ClusterのマルチプライマリーモードはGTIDの払い出し方が雑…

$
0
0

TL;DR

  • Certificationのタイミングで綺麗に連番(GNO)を払い出しているのかとか思ったら全然そんなことはなかった
  • 各サーバー内ではちゃんと直列化して、サーバーまたいだ部分は100万番ずつズラしてユニークになるようにしているらしい。
    • 99万9999まで本物のトランザクションが来たら更にオフセットするのかしらん

取り敢えず構築してマルチマスターモードに変更したところ。
 MySQL  localhost:33060+ ssl  JS > cluster.status()
{
"clusterName": "myfabric",
"defaultReplicaSet": {
"name": "default",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"node1:3306": {
"address": "node1:3306",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.19"
},
"node2:3306": {
"address": "node2:3306",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.19"
},
"node3:3306": {
"address": "node3:3306",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.19"
}
},
"topologyMode": "Multi-Primary"
},
"groupInformationSourceMember": "node2:3306"
}
ここに向かって単にsysbenchのoltp_insertを3台それぞれから叩き込んでみた(prepareはnode1に対してだけ行って、その後MySQL Shellを使ってCloneメソッドでInnoDB Clusterにした)
### node1
$ sysbench --mysql-user=root oltp_insert run

### node2
$ sysbench --mysql-user=root oltp_insert run

### node3
$ sysbench --mysql-user=root oltp_insert run
 そしたらこんなじゃよ。
 MySQL  localhost:33060+ ssl  SQL > SHOW MASTER STATUS;
+---------------+-----------+--------------+------------------+--------------------------------------------------------------------------------------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+-----------+--------------+------------------+--------------------------------------------------------------------------------------------------------------------------+
| binlog.000004 | 841384752 | | | 4fefc956-5c63-11ea-8b67-12ca68111319:1-3744,
b5984af1-5c64-11ea-b314-12ca68111319:1-5153:1000049-1005124:2000049-2005337 |
+---------------+-----------+--------------+------------------+--------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.0003 sec)
4fef..のGTIDがnode1がグループレプリケーションになる前にやった sysbench oltp_common prepareのぶん(node1の server_uuidなので)、 b598..のGTIDの1-5153はnode1、1000049-5124はnode2、2000049-2005337はnode3のものだった。
この「どのGTIDをどのサーバーが吐いたか」はバイナリログに GTID_NEXTserver_idが記録されているところを見れば何となくわかった。
$ mysqlbinlog -vv binlog.000002
..
# at 10860366
#200302 10:30:47 server id 1967205333 end_log_pos 10860448 GTID last_committed=23215 sequence_number=23216 rbr_only=yes
original_committed_timestamp=1583145047671259 immediate_commit_timestamp=1583145053357357 transaction_length=466
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
# original_commit_timestamp=1583145047671259 (2020-03-02 10:30:47.671259 UTC)
# immediate_commit_timestamp=1583145053357357 (2020-03-02 10:30:53.357357 UTC)
/*!80001 SET @@session.original_commit_timestamp=1583145047671259*//*!*/;
/*!80014 SET @@session.original_server_version=80019*//*!*/;
/*!80014 SET @@session.immediate_server_version=80019*//*!*/;
SET @@SESSION.GTID_NEXT= 'b5984af1-5c64-11ea-b314-12ca68111319:2007954'/*!*/;
..
Σ(゚д゚lll) 確かにカブらないだろうけど、雑!!!
次はアレだな、99万9999まで本物のトランザクションが来たらどうなるかだな…(さすがにそんなわかりきってるのは対応してあろうが)

壊したマルチプライマリーモード、その後に

$
0
0

TL;DR

  • グループレプリケーションが他のノードから受け取ったクエリーは hostname-relay-bin-group_replication_applier*というリレーログに保管される
    • グループレプリケーションといえどリレーログなので再起動とかで勝手に消えはしない
      • RESET SLAVEで消せる。
  • グループレプリケーションの復旧はかなり面倒なので、一回クラスター破棄して作り直した方が楽かも

日々の覚書: MultiPrimaryModeのGroup Replication環境を崩壊させるテストで壊したあとの残骸を眺めて色々考えるメモ

### node1 (最初に ALTER TABLE を開始
$ mysql -e "SHOW MASTER STATUS"
+---------------+-----------+--------------+------------------+---------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+-----------+--------------+------------------+---------------------------------------------+
| binlog.000004 | 834138015 | | | 5fa73b33-5d09-11ea-b37d-1272d9b5f033:1-3800 |
+---------------+-----------+--------------+------------------+---------------------------------------------+

### node2 (node1のALTER TABLE中にALTER TABLEを開始
$ mysql -e "SHOW MASTER STATUS"
+---------------+-----------+--------------+------------------+-----------------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+-----------+--------------+------------------+-----------------------------------------------------+
| binlog.000003 | 834129853 | | | 5fa73b33-5d09-11ea-b37d-1272d9b5f033:1-3799:1000049 |
+---------------+-----------+--------------+------------------+-----------------------------------------------------+

### node3 (node1のALTER TABLEをグループレプリケーションで受け取ってからnode2のを受信
$ mysql -e "SHOW MASTER STATUS"
+---------------+-----------+--------------+------------------+---------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+-----------+--------------+------------------+---------------------------------------------+
| binlog.000003 | 834129853 | | | 5fa73b33-5d09-11ea-b37d-1272d9b5f033:1-3800 |
+---------------+-----------+--------------+------------------+---------------------------------------------+
node1由来の 5fa73b33-5d09-11ea-b37d-1272d9b5f033:3800と node2由来の 5fa73b33-5d09-11ea-b37d-1272d9b5f033:1000049がバッティングするので、node1とnode3では :3380の方、 node2では :1000049の方だけがgtid_executedに入っている。
なあんだ、フツーに壊れた非同期レプリケーションと一緒じゃん? (フラグ)
node1とnode3を活かしてnode2を捨てて再構築すればいいんじゃん? (フラグ)
### node2
$ sudo systemctl stop mysqld

### node1
$ mysqlsh gradmin@localhost
MySQL localhost:33060+ ssl JS > dba.rebootClusterFromCompleteOutage()
Dba.rebootClusterFromCompleteOutage: This function is not available through a session to an instance in error state (RuntimeError)
あれ…。 rebootClusterFromCompleteOutageって単に group_replication_bootstrap_group = ONにして START GROUP_REPLICATIONしてるだけかと思ったんだけどそれ以外にも何か見てるのかしらん、とエラーログを見に行く。
### node1
$ less /var/log/mysqld.log
..
2020-03-03T05:04:30.110909Z 16 [ERROR] [MY-010584] [Repl] Slave SQL for channel 'group_replication_applier': Error 'Duplicate key name 'idx_pad'' on query. Default database: 'sbtest'. Query
: 'ALTER TABLE sbtest1 ADD KEY idx_pad(pad)', Error_code: MY-001061
2020-03-03T05:04:30.112022Z 16 [Warning] [MY-010584] [Repl] Slave: Duplicate key name 'idx_pad' Error_code: MY-001061
2020-03-03T05:04:30.116461Z 16 [ERROR] [MY-011451] [Repl] Plugin group_replication reported: 'The applier thread execution was aborted. Unable to process more transactions, this member will now leave the group.'
2020-03-03T05:04:30.116528Z 16 [ERROR] [MY-010586] [Repl] Error running query, slave SQL thread aborted. Fix the problem, and restart the slave SQL thread with "SLAVE START". We stopped at log 'FIRST' position 0
2020-03-03T05:04:30.116710Z 13 [ERROR] [MY-011452] [Repl] Plugin group_replication reported: 'Fatal error during execution on the Applier process of Group Replication. The server will now leave the group.'
2020-03-03T05:04:30.116811Z 13 [ERROR] [MY-011712] [Repl] Plugin group_replication reported: 'The server was automatically set into read only mode after an error was detected.'
エラー自体は非同期レプリケーションが壊れた時でも良く見るやつ。node2由来の :1000049を再実行しようとして転けている…が、このnode2由来のものが「どこに残っていて」「どうやって消すのか」
取り敢えずnode1もmysqldを再起動してみましょうかね。
### node1
$ sudo systemctl restart mysqld

$ sudo tail -f /var/log/mysqld.log
..
2020-03-03T05:40:42.619773Z 2 [ERROR] [MY-011640] [Repl] Plugin group_replication reported: 'Timeout on wait for view after joining group'
2020-03-03T05:40:42.619875Z 2 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] The member is leaving a group without being on one.'
起動後のグループレプリケーション再開が諦めてくれるまで待つ(それか group_replication_start_on_boot = OFFでも良かったか)
### node1
$ mysqlsh gradmin@localhost
MySQL localhost:33060+ ssl JS > dba.rebootClusterFromCompleteOutage()
Reconfiguring the default cluster from complete outage...

Dba.rebootClusterFromCompleteOutage: Target member is in state ERROR (RuntimeError)
エラーメッセージは変わったもののまたエラー。
今度はエラーログ何も吐かずにエラー。
### node1
mysql> SET GLOBAL group_replication_bootstrap_group = 'ON';
Query OK, 0 rows affected (0.00 sec)

mysql> START GROUP_REPLICATION;
ERROR 3092 (HY000): The server is not configured properly to be an active member of the group. Please see more details on error log.
手で START GROUP_REPLICATIONしてもダメ。エラーログ見ろって書いてあるから見てみると、まだ結局node2由来の ALTER TABLEがどこかに残っていて引っ掛かる。
2020-03-03T05:52:07.431805Z 27 [System] [MY-010597] [Repl] 'CHANGE MASTER TO FOR CHANNEL 'group_replication_applier' executed'. Previous state master_host='<NULL>', master_port= 0, master_log_file='', master_log_pos= 4, master_bind=''. New state master_host='<NULL>', master_port= 0, master_log_file='', master_log_pos= 4, master_bind=''.
2020-03-03T05:52:07.443379Z 30 [ERROR] [MY-010584] [Repl] Slave SQL for channel 'group_replication_applier': Error 'Duplicate key name 'idx_pad'' on query. Default database: 'sbtest'. Query: 'ALTER TABLE sbtest1 ADD KEY idx_pad(pad)', Error_code: MY-001061
2020-03-03T05:52:07.443412Z 30 [Warning] [MY-010584] [Repl] Slave: Duplicate key name 'idx_pad' Error_code: MY-001061
2020-03-03T05:52:07.443424Z 30 [ERROR] [MY-011451] [Repl] Plugin group_replication reported: 'The applier thread execution was aborted. Unable to process more transactions, this member will now leave the group.'
2020-03-03T05:52:07.443436Z 30 [ERROR] [MY-010586] [Repl] Error running query, slave SQL thread aborted. Fix the problem, and restart the slave SQL thread with "SLAVE START". We stopped at log 'FIRST' position 0
2020-03-03T05:52:07.445214Z 27 [ERROR] [MY-011452] [Repl] Plugin group_replication reported: 'Fatal error during execution on the Applier process of Group Replication. The server will now leave the group.'
2020-03-03T05:52:07.445261Z 27 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] The member is already leaving or joining a group.'
2020-03-03T05:52:07.445279Z 27 [ERROR] [MY-011644] [Repl] Plugin group_replication reported: 'Unable to confirm whether the server has left the group or not. Check performance_schema.replication_group_members to check group membership information.'
2020-03-03T05:52:07.445297Z 27 [ERROR] [MY-011712] [Repl] Plugin group_replication reported: 'The server was automatically set into read only mode after an error was detected.'
2020-03-03T05:52:09.590386Z 0 [ERROR] [MY-011502] [Repl] Plugin group_replication reported: 'There was a previous plugin error while the member joined the group. The member will now exit the group.'
CHANNEL 'group_replication_applier'的なのが何か所に入っていて、「フツーのマルチソースレプリケーションだったらそんな名前のリレーログが出来上がるよな」と思っていたらビンゴだった。
### node1
$ ll /var/lib/mysql/*applier*
-rw-r-----. 1 mysql mysql 1074111468 Mar 3 04:51 /var/lib/mysql/node1-relay-bin-group_replication_applier.000002
-rw-r-----. 1 mysql mysql 834138348 Mar 3 05:04 /var/lib/mysql/node1-relay-bin-group_replication_applier.000003
-rw-r-----. 1 mysql mysql 122 Mar 3 04:51 /var/lib/mysql/node1-relay-bin-group_replication_applier.index

$ mysqlbinlog -vv /var/lib/mysql/node1-relay-bin-group_replication_applier.000003 | grep -C5 :1000049
# original_commit_timestamp=1583211870082250 (2020-03-03 05:04:30.082250 UTC)
# immediate_commit_timestamp=0 (1970-01-01 00:00:00.000000 UTC)
/*!80001 SET @@session.original_commit_timestamp=1583211870082250*//*!*/;
/*!80014 SET @@session.original_server_version=80019*//*!*/;
/*!80014 SET @@session.immediate_server_version=80019*//*!*/;
SET @@SESSION.GTID_NEXT= '5fa73b33-5d09-11ea-b37d-1272d9b5f033:1000049'/*!*/;
# at 834138215
#200303 5:01:33 server id 163517600 end_log_pos 0 Query thread_id=50 exec_time=177 error_code=0 Xid = 7665
SET TIMESTAMP=1583211693/*!*/;
SET @@session.auto_increment_increment=7, @@session.auto_increment_offset=2/*!*/;
/*!80013 SET @@session.sql_require_primary_key=0*//*!*/;
「フツーにリレーログ消すなら RESET SLAVEだよな?」と思ったら消えた。
### node1
mysql> RESET SLAVE;

$ ll /var/lib/mysql/*applier*
-rw-r-----. 1 mysql mysql 236 Mar 3 06:19 /var/lib/mysql/node1-relay-bin-group_replication_applier.000001
-rw-r-----. 1 mysql mysql 628 Mar 3 06:19 /var/lib/mysql/node1-relay-bin-group_replication_applier.000002
-rw-r-----. 1 mysql mysql 122 Mar 3 06:19 /var/lib/mysql/node1-relay-bin-group_replication_applier.index
ただこれでも dba.rebootClusterFromCompleteOutageでは戻せなかったので、手でグループレプリケーションを再起動。
### node1
mysql> STOP GROUP_REPLICATION;
Query OK, 0 rows affected (4.55 sec)

mysql> SET GLOBAL group_replication_bootstrap_group = 'ON';
Query OK, 0 rows affected (0.00 sec)

mysql> START GROUP_REPLICATION;
Query OK, 0 rows affected (3.13 sec)
これでようやく、 mysqlsh --clusterで認識できる状態になる。
node2は落っことしてるから良いとして、node3がMISSINGになってるな。
### node1
$ mysqlsh gradmin@localhost --cluster
MySQL localhost:33060+ ssl JS > cluster.status()
{
"clusterName": "myfabric",
"defaultReplicaSet": {
"name": "default",
"ssl": "REQUIRED",
"status": "OK_NO_TOLERANCE",
"statusText": "Cluster is NOT tolerant to any failures. 2 members are not active",
"topology": {
"node2:3306": {
"address": "node2:3306",
"mode": "n/a",
"readReplicas": {},
"role": "HA",
"shellConnectError": "MySQL Error 2003 (HY000): Can't connect to MySQL server on 'node2' (111)",
"status": "(MISSING)"
},
"node1:3306": {
"address": "node1:3306",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.19"
},
"node3:3306": {
"address": "node3:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "(MISSING)"
}
},
"topologyMode": "Multi-Primary"
},
"groupInformationSourceMember": "node1:3306"
}
テキトーに cluster.rejoinInstanceでいいのかなと思ったら良いっぽい。
### node1
MySQL localhost:33060+ ssl JS > cluster.rejoinInstance('gradmin@node3')
Rejoining the instance to the InnoDB cluster. Depending on the original
problem that made the instance unavailable, the rejoin operation might not be
successful and further manual steps will be needed to fix the underlying
problem.

Please monitor the output of the rejoin operation and take necessary action if
the instance cannot rejoin.

Rejoining instance to the cluster ...

Please provide the password for 'gradmin@node3': ******
Save password for 'gradmin@node3:3306'? [Y]es/[N]o/Ne[v]er (default No):
The instance 'node3' was successfully rejoined on the cluster.

MySQL localhost:33060+ ssl JS > cluster.status()
{
"clusterName": "myfabric",
"defaultReplicaSet": {
"name": "default",
"ssl": "REQUIRED",
"status": "OK_NO_TOLERANCE",
"statusText": "Cluster is NOT tolerant to any failures. 1 member is not active",
"topology": {
"node2:3306": {
"address": "node2:3306",
"mode": "n/a",
"readReplicas": {},
"role": "HA",
"shellConnectError": "MySQL Error 2003 (HY000): Can't connect to MySQL server on 'node2' (111)",
"status": "(MISSING)"
},
"node1:3306": {
"address": "node1:3306",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.19"
},
"node3:3306": {
"address": "node3:3306",
"mode": "R/W",
"readReplicas": {},
"replicationLag": "00:02:56.564037",
"role": "HA",
"status": "ONLINE",
"version": "8.0.19"
}
},
"topologyMode": "Multi-Primary"
},
"groupInformationSourceMember": "node1:3306"
}
とはいえこのあとnode2をクラスターから追放しようにもrejoinさせようにもいろいろ躓いたので、おとなしく cluster.dissolveして作り直した方が楽なんじゃないかな…

シングルプライマリーとDDLとDMLと

$
0
0

TL;DR

  • シングルプライマリー構成のInnoDB Clusterにおいて
    • プライマリーノードで ALTER TABLE中の後続のDDL, DMLはプライマリーノードでブロックされない限りは ALTER TABLEを追い越してコミットすることができる
      • コミットされた後続のDDL, DMLはセカンダリーのリレーログに渡ってリプレイされるので(セカンダリーでの実行にかかる時間程度で)データが同期される
    • セカンダリーノードで ALTER TABLEをリプレイ中にプライマリーで実行されたDDL, DMLはその対象に関わらず 「セカンダリーでリプレイ中の ALTER TABLEを追い越せない」
      • よって、セカンダリーノード側で最初に受け取った ALTER TABLEが終了するまでどんな操作であり遅延する。通常のレプリケーションと一緒っちゃ一緒

プライマリーノードにてテキトーな ALTER TABLEを流す。
mysql> ALTER TABLE sbtest1 Engine = InnoDB;
プライマリーでALTER TABLEが走ってる間の3台での SHOW PROCESSLIST
node1> SHOW PROCESSLIST;  -- PRIMARYなのでroot@localhostで "altering table"
+-----+-----------------+-----------+--------+---------+------+--------------------------------------------------------+-------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+-----------------+-----------+--------+---------+------+--------------------------------------------------------+-------------------------------------+
| 4 | event_scheduler | localhost | NULL | Daemon | 7625 | Waiting on empty queue | NULL |
| 13 | system user | | NULL | Connect | 7315 | waiting for handler commit | Group replication applier module |
| 16 | system user | | NULL | Query | 7315 | Slave has read all relay log; waiting for more updates | NULL |
| 65 | root | localhost | sbtest | Query | 3 | altering table | ALTER TABLE sbtest1 Engine = InnoDB |
| 107 | root | localhost | NULL | Query | 0 | starting | SHOW PROCESSLIST |
+-----+-----------------+-----------+--------+---------+------+--------------------------------------------------------+-------------------------------------+
5 rows in set (0.00 sec)

node2> SHOW PROCESSLIST;
+----+-----------------+-----------+------+---------+------+--------------------------------------------------------+----------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+-----------+------+---------+------+--------------------------------------------------------+----------------------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 7290 | Waiting on empty queue | NULL |
| 9 | system user | | NULL | Connect | 7290 | waiting for handler commit | Group replication applier module |
| 12 | system user | | NULL | Query | 76 | Slave has read all relay log; waiting for more updates | NULL |
| 80 | root | localhost | NULL | Query | 0 | starting | SHOW PROCESSLIST |
+----+-----------------+-----------+------+---------+------+--------------------------------------------------------+----------------------------------+
4 rows in set (0.00 sec)

node3> SHOW PROCESSLIST;
+----+-----------------+-----------+------+---------+------+--------------------------------------------------------+----------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+-----------+------+---------+------+--------------------------------------------------------+----------------------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 7260 | Waiting on empty queue | NULL |
| 9 | system user | | NULL | Connect | 7260 | waiting for handler commit | Group replication applier module |
| 12 | system user | | NULL | Query | 76 | Slave has read all relay log; waiting for more updates | NULL |
| 65 | root | localhost | NULL | Query | 0 | starting | SHOW PROCESSLIST |
+----+-----------------+-----------+------+---------+------+--------------------------------------------------------+----------------------------------+
4 rows in set (0.00 sec)
この時にプライマリーで CREATE DATABASE, CREATE TABLE, INSERT
node1> CREATE DATABASE d1;
Query OK, 1 row affected (0.19 sec)

node1> CREATE TABLE d1.t1 (num serial, val varchar(32));
Query OK, 0 rows affected (0.40 sec)

node1> INSERT INTO d1.t1 VALUES (1, 'one');
Query OK, 1 row affected (0.19 sec)
こいつらはALTER TABLEに干渉せずにコミットされ、コミットされた時点でセカンダリーのリレーログに積み上げられてすぐにリプレイされるのでそのまま同期される(ように見える)
node1> SELECT * FROM d1.t1;
+-----+------+
| num | val |
+-----+------+
| 1 | one |
+-----+------+
1 row in set (0.00 sec)

node2> SELECT * FROM d1.t1;
+-----+------+
| num | val |
+-----+------+
| 1 | one |
+-----+------+
1 row in set (0.00 sec)

node3> SELECT * FROM d1.t1;
+-----+------+
| num | val |
+-----+------+
| 1 | one |
+-----+------+
1 row in set (0.00 sec)
さて、プライマリーで ALTER TABLEが流れ終わり、セカンダリーに適用されている最中の SHOW PROCESSLIST
node1> SHOW PROCESSLIST; -- プライマリーではもうALTER TABLEは終わってる
+-----+-----------------+-----------+--------+---------+------+--------------------------------------------------------+----------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+-----------------+-----------+--------+---------+------+--------------------------------------------------------+----------------------------------+
| 4 | event_scheduler | localhost | NULL | Daemon | 7916 | Waiting on empty queue | NULL |
| 13 | system user | | NULL | Connect | 7606 | waiting for handler commit | Group replication applier module |
| 16 | system user | | NULL | Query | 7606 | Slave has read all relay log; waiting for more updates | NULL |
| 65 | root | localhost | sbtest | Sleep | 70 | | NULL |
| 107 | root | localhost | NULL | Query | 0 | starting | SHOW PROCESSLIST |
+-----+-----------------+-----------+--------+---------+------+--------------------------------------------------------+----------------------------------+
5 rows in set (0.00 sec)

node2> SHOW PROCESSLIST; --Group replicationのApplierスレッド(12)がaltering table
+----+-----------------+-----------+--------+---------+------+----------------------------+-------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+-----------+--------+---------+------+----------------------------+-------------------------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 7581 | Waiting on empty queue | NULL |
| 9 | system user | | NULL | Connect | 7581 | waiting for handler commit | Group replication applier module |
| 12 | system user | | sbtest | Query | 70 | altering table | ALTER TABLE sbtest1 Engine = InnoDB |
| 80 | root | localhost | NULL | Query | 0 | starting | SHOW PROCESSLIST |
+----+-----------------+-----------+--------+---------+------+----------------------------+-------------------------------------+
4 rows in set (0.00 sec)

node3> SHOW PROCESSLIST; -- 同上
+----+-----------------+-----------+--------+---------+------+----------------------------+-------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+-----------+--------+---------+------+----------------------------+-------------------------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 7551 | Waiting on empty queue | NULL |
| 9 | system user | | NULL | Connect | 7551 | waiting for handler commit | Group replication applier module |
| 12 | system user | | sbtest | Query | 70 | altering table | ALTER TABLE sbtest1 Engine = InnoDB |
| 65 | root | localhost | NULL | Query | 0 | starting | SHOW PROCESSLIST |
+----+-----------------+-----------+--------+---------+------+----------------------------+-------------------------------------+
4 rows in set (0.00 sec)
この状態でプライマリーに対して DELETEを打ち込んでみる。
node1> DELETE FROM d1.t1 WHERE num = 1;
Query OK, 1 row affected (0.01 sec)

node1> SELECT * FROM d1.t1;
Empty set (0.00 sec)
node2, node3では
node2> SELECT * FROM d1.t1;
+-----+------+
| num | val |
+-----+------+
| 1 | one |
+-----+------+
1 row in set (0.00 sec)

node3> SELECT * FROM d1.t1;
+-----+------+
| num | val |
+-----+------+
| 1 | one |
+-----+------+
1 row in set (0.03 sec)
まだ消えてない。
node1> DROP TABLE d1.t1;
Query OK, 0 rows affected (0.02 sec)

node1> SELECT * FROM d1.t1;
ERROR 1146 (42S02): Table 'd1.t1' doesn't exist

node2> SELECT * FROM d1.t1;
+-----+------+
| num | val |
+-----+------+
| 1 | one |
+-----+------+
1 row in set (0.01 sec)

node3> SELECT * FROM d1.t1;
+-----+------+
| num | val |
+-----+------+
| 1 | one |
+-----+------+
1 row in set (0.00 sec)
DDLも追い越せない。
セカンダリー側で ALTER TABLEが終わって初めて、 DELETEDROP TABLEも実行されてデータが追い付く。
node2> SHOW PROCESSLIST;
+----+-----------------+-----------+------+---------+------+--------------------------------------------------------+----------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+-----------+------+---------+------+--------------------------------------------------------+----------------------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 8010 | Waiting on empty queue | NULL |
| 9 | system user | | NULL | Connect | 8010 | waiting for handler commit | Group replication applier module |
| 12 | system user | | NULL | Query | 416 | Slave has read all relay log; waiting for more updates | NULL |
| 80 | root | localhost | NULL | Query | 0 | starting | SHOW PROCESSLIST |
+----+-----------------+-----------+------+---------+------+--------------------------------------------------------+----------------------------------+
4 rows in set (0.00 sec)

node2> SELECT * FROM d1.t1;
ERROR 1146 (42S02): Table 'd1.t1' doesn't exist

node3> SHOW PROCESSLIST;
+----+-----------------+-----------+------+---------+------+--------------------------------------------------------+----------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+-----------+------+---------+------+--------------------------------------------------------+----------------------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 7980 | Waiting on empty queue | NULL |
| 9 | system user | | NULL | Connect | 7980 | waiting for handler commit | Group replication applier module |
| 12 | system user | | NULL | Query | 416 | Slave has read all relay log; waiting for more updates | NULL |
| 65 | root | localhost | NULL | Query | 0 | starting | SHOW PROCESSLIST |
+----+-----------------+-----------+------+---------+------+--------------------------------------------------------+----------------------------------+
4 rows in set (0.00 sec)

node3> SELECT * FROM d1.t1;
ERROR 1146 (42S02): Table 'd1.t1' doesn't exist
いわゆるフツーのレプリケーションでフツーに「ALTER TABLEはスレーブでも同じだけ時間がかかるから遅延するよ」というのと同じなので無停止で色々やろうとする時は注意、と。
いわゆる Seconds_Behind_Masterみたいなのは performance_schema.replication_group_member_stats.count_transactions_remote_in_applier_queueにトランザクションの数として現れるので、これで見るのがいいのかな(プライマリーで実行してもセカンダリーで実行しても、グループ全体の値が見える)
mysql> SELECT COUNT_TRANSACTIONS_IN_QUEUE AS queue, COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE AS apply_queue, COUNT_TRANSACTIONS_ROWS_VALIDATING AS validating, COUNT_TRANSACTIONS_REMOTE_APPLIED, COUNT_TRANSACTIONS_LOCAL_PROPOSED, TRANSACTIONS_COMMITTED_ALL_MEMBERS AS gtid FROM performance_schema.replication_group_member_stats;
+-------+-------------+------------+-----------------------------------+-----------------------------------+---------------------------------------------+
| queue | apply_queue | validating | COUNT_TRANSACTIONS_REMOTE_APPLIED | COUNT_TRANSACTIONS_LOCAL_PROPOSED | gtid |
+-------+-------------+------------+-----------------------------------+-----------------------------------+---------------------------------------------+
| 0 | 1525 | 2180 | 2556 | 0 | 810ac99f-61b5-11ea-b87d-120fb5068a5b:1-1939 |
| 0 | 0 | 2180 | 4 | 4115 | 810ac99f-61b5-11ea-b87d-120fb5068a5b:1-1939 |
| 0 | 1525 | 2180 | 2546 | 0 | 810ac99f-61b5-11ea-b87d-120fb5068a5b:1-1939 |
+-------+-------------+------------+-----------------------------------+-----------------------------------+---------------------------------------------+

グループレプリケーションのメンバーとInnoDB Clusterのメタデータと cluster.rescan()

$
0
0

TL;DR

  • グループレプリケーション(mysqldそのもの)が認識してるノードの情報は performance_schema.replication_group_members
  • InnoDB Clusterの中でMySQL ShellとMySQL Routerが使うノードの情報は mysql_innodb_cluster_metadata.instances
  • 整合性が取れなったら cluster.rescanでグループレプリケーション側の情報を正としてInnoDB Clusterのメタデータを修正できる

以下、 performance_schema.replication_group_membersをメンバー、 mysql_innodb_cluster_metadata.instancesをメタデータと略するます。

メンバーにいるけどメタデータにいないパターン

再現方法

  • 手で(=MySQL Shellを使わずに)グループレプリケーションのノードを追加したり、 メタデータのレコードを直接DELETEしたりするとなる

影響

  • MySQL Routerはメタデータ経由で接続先をバランシングするので、メタデータに載っていないノードにはMySQL Router経由でアクセスできない
    • 戯れにシングルプライマリーのプライマリーノードをメタデータから消したら、R/Wの6446ポートにアクセスできなくなった
  • MySQL Shellの動作もおかしくなる
    • シングルプライマリーのプライマリーノードをメタデータから消したら dba.getClusterできなくなった
      • SQLインターフェースで SELECT group_replication_set_as_primary(..)でスイッチしたらgetClusterはできるようになったけど
    • cluster.setPrimaryInstanceとかも動かない
      • Cluster.setPrimaryInstance: This operation requires all the cluster members to be ONLINE (RuntimeError)って言われた

復旧

  • cluster.rescanはメンバーとメタデータを突き合わせて、メンバー(Group Replication上の状態を正として) に合わせてくれる
 MySQL  localhost:33060+ ssl  JS > cluster.rescan()
Rescanning the cluster...

Result of the rescanning operation for the 'myfabric' cluster:
{
"name": "myfabric",
"newTopologyMode": null,
"newlyDiscoveredInstances": [
{
"host": "node3:3306",
"member_id": "f95ce2c9-62a0-11ea-b1cc-12d40307b547",
"name": null,
"version": "8.0.19"
}
],
"unavailableInstances": []
}

A new instance 'node3:3306' was discovered in the cluster.
Would you like to add it to the cluster metadata? [Y/n]:
Adding instance to the cluster metadata...
The instance 'node3:3306' was successfully added to the cluster metadata.

メタデータにいるけどメンバーにいないパターン

再現方法

  • そのへんで STOP GROUP_REPLICATIONするとあっさりこうなる

影響

  • MySQL Routerはメタデータとメンバーの状態をそれぞれSELECTしてルーティング先を決めるっぽいので特に影響はない

復旧

  • これも cluster.rescan()で何とかなる
 MySQL  localhost:33060+ ssl  JS > cluster.rescan()
Rescanning the cluster...

Result of the rescanning operation for the 'myfabric' cluster:
{
"name": "myfabric",
"newTopologyMode": null,
"newlyDiscoveredInstances": [],
"unavailableInstances": [
{
"host": "node2:3306",
"label": "node2:3306",
"member_id": "f96e7097-62a0-11ea-b1c0-12ae12a2047f"
}
]
}

The instance 'node2:3306' is no longer part of the cluster.
The instance is either offline or left the HA group. You can try to add it to the cluster again with the cluster.rejoinInstance('node2:3306') command or you can remove it from the cluster configuration.
Would you like to remove it from the cluster metadata? [Y/n]: Y
Removing instance from the cluster metadata...
The instance 'node2:3306' was successfully removed from the cluster metadata.
cluster.rescanべんり!!

グループレプリケーションの group_replication_applier と group_replication_recovery のリレーログ

$
0
0

TL;DR

  • グループレプリケーションは(形式上かも知れないけど) group_replication_applier というレプリケーションチャンネルと group_replication_recovery というレプリケーションチャンネルを持っている
  • グループレプリケーションの通信が問題なく行われている間は GCS(Group Communication System) から group_replication_applier のリレーログに書き込まれて group_repliation_applierのSQLスレッドが処理
    • この時、プライマリーノードはGCSから応答が返ってきた後にバイナリログに書く
  • グループレプリケーションが途切れてGCSから直接受け取っていない間に発生した書き込みはバイナリログを経由して group_replication_recovery チャンネルを通じて受け取る
    • こっちは割とフツーのGTIDベースなAsyncレプリケーションっぽく振る舞う
    • group_replication_recoveryチャンネルが動いている間はgroup_replication_applierチャンネルが動かないような制御は入ってるっぽい

実験

  • シングルプライマリーの3台構成

まずは3台ともONLINE

### node1 (PRIMARY)
mysql> CREATE DATABASE d1;
Query OK, 1 row affected (0.01 sec)

mysql> CREATE TABLE d1.t1 (num serial, val varchar(32));
Query OK, 0 rows affected (0.02 sec)

mysql> INSERT INTO d1.t1 VALUES (1, 'one');
Query OK, 1 row affected (0.01 sec)
リレーログとバイナリログと、 INSERTが記録されているファイルを雑に調べる( *.0000*はバイナリログとリレーログの連番部分だけを引っ掛けてるつもり、 binlog_rows_query_log_eventsの部分だけ抜粋)
### node1 (PRIMARY)
$ for f in /var/lib/mysql/*.0000* ; do mysqlbinlog -vv $f | stdbuf -oL awk '{print "'$f'", $0}'; done | grep INSERT | grep t1
/var/lib/mysql/binlog.000004 # INSERT INTO d1.t1 VALUES (1, 'one')

### node2
$ for f in /var/lib/mysql/*.0000* ; do mysqlbinlog -vv $f | stdbuf -oL awk '{print "'$f'", $0}'; done | grep INSERT | grep t1
/var/lib/mysql/binlog.000002 # INSERT INTO d1.t1 VALUES (1, 'one')
/var/lib/mysql/node2-relay-bin-group_replication_applier.000002 # INSERT INTO d1.t1 VALUES (1, 'one')

### node3
$ for f in /var/lib/mysql/*.0000* ; do mysqlbinlog -vv $f | stdbuf -oL awk '{print "'$f'", $0}'; done | grep INSERT | grep t1
/var/lib/mysql/binlog.000002 # INSERT INTO d1.t1 VALUES (1,
/var/lib/mysql/node3-relay-bin-group_replication_applier.000002 # INSERT INTO d1.t1 VALUES (1, 'one')
プライマリーではバイナリログのみ、node2とnode3は group_replication_applierのリレーログとバイナリログに記録。

ノード停止中に発生した更新

### node3
$ sudo systemctl stop mysqld

### node1
mysql> DELETE FROM t1 WHERE num = 1;
Query OK, 1 row affected (0.00 sec)

### node1
$ for f in /var/lib/mysql/*.0000* ; do mysqlbinlog -vv $f | stdbuf -oL awk '{print "'$f'", $0}'; done | grep DELETE | grep t1
/var/lib/mysql/binlog.000004 # DELETE FROM t1 WHERE num = 1

### node2
$ for f in /var/lib/mysql/*.0000* ; do mysqlbinlog -vv $f | stdbuf -oL awk '{print "'$f'", $0}'; done | grep DELETE | grep t1
/var/lib/mysql/binlog.000002 # DELETE FROM t1 WHERE num = 1
/var/lib/mysql/node2-relay-bin-group_replication_applier.000002 # DELETE FROM t1 WHERE num = 1

### node3
$ sudo systemctl start mysqld
$ for f in /var/lib/mysql/*.0000* ; do mysqlbinlog -vv $f | stdbuf -oL awk '{print "'$f'", $0}'; done | grep DELETE | grep t1
/var/lib/mysql/binlog.000003 # DELETE FROM t1 WHERE num = 1
個人的には node3-relay-bin-group_replication_recovery の方でも観測できると良かったんだけど、なんかすぐパージされるのか何なのか観測できなかった。
performance_schema.replication_applier_status_by_workerテーブルからは、node3だけ group_replication_recoveryチャンネルが仕事をしたのがわかる。
### node3
MySQL node3:33060+ ssl SQL > SELECT channel_name, service_state, last_applied_transaction AS last_gtid, last_applied_transaction_original_commit_timestamp AS orig_ts, last_applied_transaction_immediate_commit_timestamp AS my_ts FROM performance_schema.replication_applier_status_by_worker;
+----------------------------+---------------+-----------------------------------------+---------------------+----------------------------+
| channel_name | service_state | last_gtid | orig_ts | my_ts |
+----------------------------+---------------+-----------------------------------------+---------------------+----------------------------+
| group_replication_applier | ON | | 0000-00-00 00:00:00 | 0000-00-00 00:00:00 |
| group_replication_recovery | OFF | 96bb7988-64ec-11ea-b013-122c3190731d:58 | 0000-00-00 00:00:00 | 2020-03-13 05:45:03.879562 |
+----------------------------+---------------+-----------------------------------------+---------------------+----------------------------+
2 rows in set (0.0020 sec)

### node2
MySQL node2:33060+ ssl SQL > SELECT channel_name, service_state, last_applied_transaction AS last_gtid, last_applied_transaction_original_commit_timestamp AS orig_ts, last_applied_transaction_immediate_commit_timestamp AS my_ts FROM performance_schema.replication_applier_status_by_worker;
+----------------------------+---------------+-----------------------------------------+---------------------+--------------------------+
| channel_name | service_state | last_gtid | orig_ts | my_ts |
+----------------------------+---------------+-----------------------------------------+---------------------+--------------------------+
| group_replication_applier | ON | 96bb7988-64ec-11ea-b013-122c3190731d:58 | 0000-00-00 00:00:00 | 0000-00-00 00:00:00 |
| group_replication_recovery | OFF | 96bb7988-64ec-11ea-b013-122c3190731d:39 | 0000-00-00 00:00:00 | 2020-03-13 05:37:00.4302 |
+----------------------------+---------------+-----------------------------------------+---------------------+--------------------------+
2 rows in set (0.0007 sec)

### node1
MySQL localhost:33060+ ssl SQL > SELECT channel_name, service_state, last_applied_transaction AS last_gtid, last_applied_transaction_original_commit_timestamp AS orig_ts, last_applied_transaction_immediate_commit_timestamp AS my_ts FROM performance_schema.replication_applier_status_by_worker;
+---------------------------+---------------+-----------------------------------------+---------------------+---------------------+
| channel_name | service_state | last_gtid | orig_ts | my_ts |
+---------------------------+---------------+-----------------------------------------+---------------------+---------------------+
| group_replication_applier | ON | 96bb7988-64ec-11ea-b013-122c3190731d:58 | 0000-00-00 00:00:00 | 0000-00-00 00:00:00 |
+---------------------------+---------------+-----------------------------------------+---------------------+---------------------+
1 row in set (0.0004 sec)
言われてみれば、 グループレプリケーションを手で組む時って CHANGE MASTER TO .. FOR CHANNEL 'group_replication_recovery'だけ設定するので、コイツらは比較的「いつものレプリケーション」に近い仕組みをしていそう。
折角なのでgdbで止めて、group_replication_recoveryチャンネルのリレーログに入っているところを見てみた(バイナリログ書き出し Binlog_event_writer::writeの手前で止めればまだリレーログからは消えてないだろうという雑なアレ)
### node3
$ sudo gdb --args /usr/sbin/mysqld --user=mysql
(gdb) b Binlog_event_writer::write
Breakpoint 1 at 0x1bc0f80: file /export/home/pb2/build/sb_0-37170812-1575922551.75/rpm/BUILD/mysql-8.0.19/mysql-8.0.19/sql/binlog.cc, line 1260.
(gdb) r
Starting program: /usr/sbin/mysqld --user=mysql
..
Breakpoint 1, Binlog_event_writer::write (this=0x7fffc0095fb0, buffer=0x7fffc0095ce0 "LUk^!Wk\002\235R", length=19)
at /export/home/pb2/build/sb_0-37170812-1575922551.75/rpm/BUILD/mysql-8.0.19/mysql-8.0.19/sql/binlog.cc:1260
1260 /export/home/pb2/build/sb_0-37170812-1575922551.75/rpm/BUILD/mysql-8.0.19/mysql-8.0.19/sql/binlog.cc: No such file or directory.

$ for f in /var/lib/mysql/*.0000* ; do mysqlbinlog -vv $f | stdbuf -oL awk '{print "'$f'", $0}'; done | grep DELETE | grep t1
/var/lib/mysql/node3-relay-bin-group_replication_recovery.000003 # DELETE FROM t1 WHERE num = 1
group_replication_recoveryのリレーログに入ってるところが見えて、そのままgdb側の処理を進めてやるとすぐにrelay-bin-group_replication_recovery.000003ファイルそのものが消えた。たぶんrelay_log_purge (group_replication_applierの方のリレーログが割と残るのは何なんだろう…?
ともあれ、グルーレプリケーションは2つのチャンネルを使っているのが何となくわかった。
Viewing all 581 articles
Browse latest View live