LoginSignup
2
2

More than 5 years have passed since last update.

MYSQL 8.0.16 Multi-primaryモードでメンバー追加(スケールアウト)できない話

Last updated at Posted at 2019-06-05

概要

MYSQL8.0で超便利なmysqlshを使用してクラスタを操作する際に掲題の問題にぶつかったので、一部始終を共有します。

環境

OS:Centos 7.5

pkg:

  • mysql-community-common-8.0.16-2.el7.x86_64
  • mysql-community-client-8.0.16-2.el7.x86_64
  • mysql-router-community-8.0.16-2.el7.x86_64
  • mysql-community-server-8.0.16-2.el7.x86_64
  • mysql-community-libs-8.0.16-2.el7.x86_64
  • mysql-shell-8.0.16-1.el7.x86_64

省略

clusterの作成まで成功したところから説明しますので、省略します。

cluster =  dba.createCluster()
or 
cluster = dba.getCluster()

multi-primaryモードでの前提とします。

cluster.switchToMultiPrimaryMode()

再現

この状態でaddInstanceすると以下のエラーで失敗します。

cluster.addInstance("xxx.xxx.xxx.xxx")

...
Instance configuration is suitable.
Cluster.addInstance: The table does not comply with the requirements by an external plugin. (MySQL Error 3098)

ログ(デフォルト:/var/log/mysqld.log)を見ると:

2019-06-05T08:07:08.430457Z 2205 [ERROR] [MY-011543] [Repl] Plugin group_replication reported: 'Table instances has a foreign key with 'CASCADE' clause. This is not compatible with Group Replication.'

原因

cluster.switchToMultiPrimaryMode()を実行すると group_replication_enforce_update_everywhere_checks=ONにしてくれます。
これはmulti-primaryモードを使うにあたり、競合を検出したり、防ぐためのチェック機能ですが、switchToMultiPrimaryModeがそこで引っかかったわけです。

  • If a transaction is executed under the SERIALIZABLE isolation level, then its commit fails when synchronizing itself with the group.
  • If a transaction executes against a table that has foreign keys with cascading constraints, then the transaction fails to commit when synchronizing itself with the group.

解決案を洗い出すためにもう少し具体的に見ると

  • どのテーブルが引っかかったのか
SELECT CONCAT(t1.table_name, '.', column_name) AS 'foreign key',     
     CONCAT(t1.referenced_table_name, '.', referenced_column_name) AS 'references',     
     t1.constraint_name AS 'constraint name', UPDATE_RULE, DELETE_RULE 
     FROM information_schema.key_column_usage as t1 
     JOIN information_schema.REFERENTIAL_CONSTRAINTS as t2 
     WHERE t2.CONSTRAINT_NAME = t1.constraint_name 
       AND t1.referenced_table_name IS NOT NULL 


+-------------------------------------------------------+---------------------------+--------------------------------------------------------+-------------+-------------+
| foreign key                                           | references                | constraint name                                        | UPDATE_RULE | DELETE_RULE |
+-------------------------------------------------------+---------------------------+--------------------------------------------------------+-------------+-------------+
| replicasets.cluster_id                                | clusters.cluster_id       | replicasets_ibfk_1                                     | NO ACTION   | RESTRICT    |
| clusters.default_replicaset                           | replicasets.replicaset_id | clusters_ibfk_1                                        | NO ACTION   | RESTRICT    |
| instances.host_id                                     | hosts.host_id             | instances_ibfk_1                                       | NO ACTION   | RESTRICT    |
| instances.replicaset_id                               | replicasets.replicaset_id | instances_ibfk_2                                       | NO ACTION   | SET NULL    |
| routers.host_id                                       | hosts.host_id             | routers_ibfk_1                                         | NO ACTION   | RESTRICT    |

mysql_innodb_cluster_metadata.instancesの外部キー(instances.replicaset_id)のdelete制約がSET NULLになっていて、
こういう場合はconflict検知できないからやめてとgroup_replication_enforce_update_everywhere_checksにはじかれましたね。
(CASCADE以外もあったんですね。)
一応5.7.23でバグとしてリポートした方もいましたがまだ対応されてないですね。

解決案

Onlineのままメンバーを追加する前提

案1

mysql_innodb_cluster_metadata.instancesの外部キーの制約を変更
バグを書いてくれた方が改善案も書いてあります。

ALTER TABLE `mysql_innodb_cluster_metadata`.`instances` DROP FOREIGN KEY `instances_ibfk_2`;

ALTER TABLE `mysql_innodb_cluster_metadata`.`instances` ADD CONSTRAINT `instances_ibfk_2`
 FOREIGN KEY (`replicaset_id`) REFERENCES `replicasets` (`replicaset_id`);

※ mysql_innodb_cluster_metadataを弄っているので全メンバーで実施必要かと思ったら、クラスタ内同期されました。
※ 非公式な案なので、どんな問題起きるかわかりません。十分な理解とテストした上で採用してください
ただDELETE_RULEを変更しているので、メンバー削除をしなければ問題ないかも知れません。

案2(中の人曰くこっちがおすすめ)

一時的にSingleprimaryに変更すればよいです。
裏でgroup_replication_enforce_update_everywhere_checks=OFFにしてくれます。

cluster.switchToSinglePrimaryMode()
cluster.addInstance("xxx.xxx.xxx.xxx")
cluster.switchToMultiPrimaryMode()

※SinglePrimaryにSwitchした時点で実質1台のみOnlineのままになりますが。。。it's works!

案3

mysqlshを使わずにやれば出来ますが、せっかく便利なツールがあるので積極的に使いたいですよね。作業効率を上げるためにも。

まとめ

まだmysql初心者なので、素朴な質問ですがそもそもmulti-primaryって何がうれしいんでしょう。。。
そのユースケースが分かる方がいらっしゃいましたら教えていただければと思います。

私的には。。。
競合起きる可能性があるから一貫性はさがるでしょう?
書き込みパフォーマンスを上げたい?→shardingがいいでしょう
耐障害性を上げたい(切り替え時間短縮)?→Group replicationによってSingleも遅くないと感じる

マニュアルにもいろいろ制約書いてあり、multi-primaryを使いたい方は忘れずに見てくださいな。

参考

以下のサイトを参考にしました。ライターの方々にありがたいです。

http://gihyo.jp/dev/serial/01/mysql-road-construction-news
https://lefred.be/content/mysql-group-replication-single-primary-or-multi-primary/

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2