DBA专题
DBA授课
DBA公开课
DBA训练营三天
01.Mysql基础入门-数据库简介
02.Mysql基础入门-部署与管理体系
03.MySQL主流版本版本特性与部署安装
04.Mysql-基础入门-用户与权限
05 MySQL-SQL基础2
06 SQL高级开发-函数
07 MySQL-SQL高级处理
08 SQL练习 作业
09 数据库高级开发2
10 Mysql基础入门-索引
11 Mysql之InnoDB引擎架构与体系结构
12 Mysql之InnoDB存储引擎
13 Mysql之日志管理
14 Mysql备份,恢复与迁移
15 主从复制的作用及重要性
16 Mysql Binlog Event详解
17 Mysql 主从复制
18 MySQL主从复制延时优化及监控故障处理
19 MySQL主从复制企业级场景解析
20 MySql主从复制搭建
21 MySQL高可用-技术方案选型
22 MySQL高可用-MHA(原理篇)
23 MySQL MHA实验
24 MySQL MGR
25 部署MySQL InnoDB Cluster
26 MySQL Cluster(MGR)
27 MySQL ProxySQL中间件
相信可能就有无限可能
-
+
首页
24 MySQL MGR
> 第 20 章 组复制https://dev.mysql.com/doc/refman/8.0/en/group-replication.html # 1 什么是 MGR MGR(MySQL Group Replication)是 MySQL 5.7 引⼊的⼀种⾼可⽤性和复制⽅案。以下是 MGR 的⼀些特点: - 多主复制:MGR 可以将多个 MySQL 实例配置为主节点,⽀持多个实例同时进⾏写操作,实现了多主复制的 机制。 - 分布式事务提交:MGR 使⽤了基于 paxos 算法的分布式⼀致性协议,保证了数据的⼀致性和可靠性。 - ⾃动故障转移:当主节点发⽣故障时,MGR 可以⾃动将从节点中的⼀个节点提升为主节点,继续提供服务, 避免了⼿动⼲预的繁琐过程,提⾼了系统的可⽤性和稳定性。 - 动态成员管理:MGR ⽀持在线成员加⼊和离开,可以⾃动调整群组成员,保证数据⼀致性和⾼可⽤性。 - 读写分离和负载均衡:MGR ⽀持读写分离和负载均衡,可以将读操作分配给从节点,减轻主节点的负载,提 ⾼系统的性能和可扩展性。 # 2 MGR 推出的背景 - 异步复制 ![async-replication-diagram](https://img.sunrisenan.com/img/2024/05/14/141843387.png) - 半同步复制 ![semisync-replication-diagram](https://img.sunrisenan.com/img/2024/05/14/141937576.png) - 组复制 ![gr-replication-diagram](https://img.sunrisenan.com/img/2024/05/14/142126262.png) 相对于传统的主从复制,组复制主要新增了两个模块: Consensus:共识,基于Paxos实现,可保证消息的全局有序和消息会被半数以上节点接受。 certify:认证。要么全部认证通过,要么全部认证失败。 # 3 MGR 部署 ## 3.1 集群拓扑 | 角色 | 主机名 | IP | | --------- | -------------- | ------------ | | Primary | db01.hosts.com | 172.31.7.110 | | Secondary | db02.hosts.com | 172.31.7.111 | | Secondary | db03.hosts.com | 172.31.7.112 | | Router | Router.hosts.com | 172.31.7.66 | ## 3.2 准备⼯作 ```bash # systemctl stop firewalld # systemctl disable firewalld # iptables -F # setenforce 0 # vim /etc/selinux/config SELINUX=disabled ``` ## 3.3 初始化实例 三个节点都要执⾏。 ### 3.3.1 准备安装包 ```bash useradd mysql cd /usr/local/ wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.33-linux-glibc2.12- x86_64.tar.xz tar xvf mysql-8.0.33-linux-glibc2.12-x86_64.tar.xz ln -s mysql-8.0.33-linux-glibc2.12-x86_64 mysql ``` ### 3.3.2 编辑 MySQL 配置⽂件 以下是 node1 的配置⽂件。 ```bash [client] socket = /data/mysql3306/data/mysql.sock [mysqld] # Server user = mysql datadir = /data/mysql3306/data basedir = /usr/local/mysql port = 3306 socket = /data/mysql3306/data/mysql.sock log_timestamps = system log_error = /data/mysql3306/data/mysqld.err skip_name_resolve report_host = "172.31.7.110" disabled_storage_engines = "MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY" sql_require_primary_key = ON # Replication server_id = 1 log_bin = mysql-bin binlog_format = ROW log_slave_updates = ON gtid_mode = ON enforce_gtid_consistency = ON master_info_repository = TABLE relay_log_info_repository = TABLE super_read_only = ON binlog_transaction_dependency_tracking = WRITESET transaction_write_set_extraction = XXHASH64 # Multi-threaded Replication slave_parallel_type = LOGICAL_CLOCK slave_preserve_commit_order = ON slave_parallel_workers = 4 # Group Replication Settings plugin_load_add = "group_replication.so" loose_group_replication_group_name = "22e34e6b-f37e-11ed-a2a8-000c29f6d187" loose_group_replication_start_on_boot = OFF loose_group_replication_local_address = "172.31.7.110:33061" loose_group_replication_group_seeds = "172.31.7.110:33061,172.31.7.111:33061,172.31.7.112:33061" loose_group_replication_bootstrap_group = OFF loose_group_replication_recovery_get_public_key = ON ``` 组复制的相关参数。 - plugin_load_add:加载组复制插件。也可在实例启动后,通过以下命令⼿动加载。 ```sql mysql> install plugin group_replication soname 'group_replication.so'; ``` - group_replication_group_name:集群名。⽤于唯⼀标识⼀个集群,必须是个有效的 UUID 值。 - group_replication_start_on_boot:是否在实例启动时⾃动开启组复制,默认为 ON。 - group_replication_local_address:当前节点的内部通信地址。 - group_replication_group_seeds:种⼦节点地址。当有新的节点加⼊时,它会⾸先与种⼦节点建⽴连接。 - group_replication_bootstrap_group:是否由当前节点初始化集群,默认为 OFF。 - group_replication_recovery_get_public_key:如果复制⽤户使⽤了 caching_sha2_password,在分布式恢 复阶段,如果没有使⽤ SSL(group_replication_recovery_use_ssl=ON),则必须使⽤ RSA 密钥对进⾏密码 交换。将 group_replication_recovery_get_public_key 设置为 ON,则允许该节点直接从源节点获取公钥。 组复制的参数中为什么需要加 loose? ```bash 2023-05-16T09:43:55.202530+08:00 0 [Warning] [MY-013501] [Server] Ignoring --pluginload[_add] list as the server is running with --initialize(-insecure). 2023-05-16T09:43:55.666236+08:00 0 [Warning] [MY-000067] [Server] unknown variable 'loose_group_replication_group_name=22e34e6b-f37e-11ed-a2a8-000c29f6d187'. 2023-05-16T09:43:55.666250+08:00 0 [Warning] [MY-000067] [Server] unknown variable 'loose_group_replication_start_on_boot=OFF'. 2023-05-16T09:43:55.666254+08:00 0 [Warning] [MY-000067] [Server] unknown variable 'loose_group_replication_local_address=192.168.79.10:33061'. 2023-05-16T09:43:55.666258+08:00 0 [Warning] [MY-000067] [Server] unknown variable 'loose_group_replication_group_seeds=192.168.79.10:33061,192.168.79.20:33061,192.168.79 .30:33061'. 2023-05-16T09:43:55.666261+08:00 0 [Warning] [MY-000067] [Server] unknown variable 'loose_group_replication_bootstrap_group=OFF'. 2023-05-16T09:43:55.666264+08:00 0 [Warning] [MY-000067] [Server] unknown variable 'loose_group_replication_recovery_get_public_key=ON'. ``` node2 的配置⽂件 参数基本相同,只需修改 report_host、server_id 和 loose-group_replication_local_address ```bash report_host = "172.31.7.111" server_id = 2 group_replication_local_address = "172.31.7.111:33061" ``` node3 的配置⽂件 ```bash report_host = "172.31.7.112" server_id = 2 group_replication_local_address = "172.31.7.112:33061" ``` ### 3.3.3 创建数据⽬录、初始化、启动实例 ```bash mkdir -p /data/mysql/3306/data /usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf --initialize-insecure /usr/local/mysql/bin/mysqld_safe --defaults-file=/etc/my.cnf & ``` # 4 启动组复制 ## 4.1 查看插件是否加载成功 ```bash root@db01:/data/mysql3306/data# mysql -e 'show plugins' | grep group_replication group_replication ACTIVE GROUP REPLICATION group_replication.so GPL ``` ## 4.2 初始化组复制 只在 node1 上执⾏。 ```bash set global group_replication_bootstrap_group=on; start group_replication; set global group_replication_bootstrap_group=off; ``` 通过 performance_schema.replication_group_members 查看集群的节点信息。 ```bash mysql> select member_id,member_host,member_port,member_state,member_role from performance_schema.replication_group_members; +--------------------------------------+--------------+-------------+--------------+-------------+ | member_id | member_host | member_port | member_state | member_role | +--------------------------------------+--------------+-------------+--------------+-------------+ | 32049408-02a5-11ef-8b62-000c29f23176 | 172.31.7.110 | 3306 | ONLINE | PRIMARY | +--------------------------------------+--------------+-------------+--------------+-------------+ 1 row in set (0.01 sec) ``` ONLINE 代表节点状态正常。 ## 4.3 创建复制⽤户 ```sql create user 'rpl_user'@'%' identified by 'rpl_password'; grant replication slave on *.* to 'rpl_user'@'%'; grant backup_admin on *.* to 'rpl_user'@'%'; ``` 如果使⽤ MySQL 5.7 搭建组复制,复制⽤户只需授予 REPLICATION SLAVE 权限。 MySQL 8.0.17 引⼊的 Clone Plugin,使⽤克隆插件需要 BACKUP_ADMIN 权限。 从 MySQL 8.0.21 开始,如果通过 group_replication_advertise_recovery_endpoints 指定了 admin_port 来进⾏ 分布式恢复操作,⽤户还需要授予 SERVICE_CONNECTION_ADMIN 权限。 从 MySQL 8.0.27 开始,如果组成员之间的连接要使⽤ MySQL 通信栈( group_replication_communication_stack ),⽤户还需要授予 GROUP_REPLICATION_STREAM 和 CONNECTION_ADMIN 权限。 ## 4.4 配置恢复通道 ```bash change master to master_user='rpl_user', master_password='rpl_password' for channel 'group_replication_recovery'; ``` ## 4.5 构造测试数据 ```sql create database test; create table test.t1(id int primary key,c1 varchar(10)); insert into test.t1 values(1,'abc'); ``` # 5 添加节点 添加节点⽐较简单,只需两步: 1. 配置恢复通道。 2. 启动组复制。 依次在 node2 和 node3 上执⾏以下命令。 ```bash change master to master_user='rpl_user', master_password='rpl_password' for channel 'group_replication_recovery'; start group_replication; ``` ```bash 2024-05-13T13:45:38.427825+08:00 10 [Warning] [MY-010604] [Repl] Neither --relay-log nor --relay-log-index were used; so replication may break when this MySQL server acts as a replica and has his hostname changed!! Please use '--relay-log=db03-relay-bin' to avoid this problem. 2024-05-13T13:45:38.435302+08:00 10 [System] [MY-010597] [Repl] 'CHANGE REPLICATION SOURCE TO FOR CHANNEL 'group_replication_recovery' executed'. Previous state source_host='', source_port= 3306, source_log_file='', source_log_pos= 4, source_bind=''. New state source_host='', source_port= 3306, source_log_file='', source_log_pos= 4, source_bind=''. 2024-05-13T13:45:45.064040+08:00 10 [System] [MY-013587] [Repl] Plugin group_replication reported: 'Plugin 'group_replication' is starting.' 2024-05-13T13:45:45.067204+08:00 10 [System] [MY-011565] [Repl] Plugin group_replication reported: 'Setting super_read_only=ON.' 2024-05-13T13:45:45.090278+08:00 12 [System] [MY-010597] [Repl] 'CHANGE REPLICATION SOURCE TO FOR CHANNEL 'group_replication_applier' executed'. Previous state source_host='', source_port= 3306, source_log_file='', source_log_pos= 4, source_bind=''. New state source_host='<NULL>', source_port= 0, source_log_file='', source_log_pos= 4, source_bind=''. 2024-05-13T13:45:46.532863+08:00 10 [System] [MY-011511] [Repl] Plugin group_replication reported: 'This server is working as secondary member with primary member address 172.31.7.110:3306.' 2024-05-13T13:45:46.533088+08:00 0 [System] [MY-011565] [Repl] Plugin group_replication reported: 'Setting super_read_only=ON.' 2024-05-13T13:45:46.533274+08:00 0 [System] [MY-013471] [Repl] Plugin group_replication reported: 'Distributed recovery will transfer data using: Incremental recovery from a group donor' 2024-05-13T13:45:46.534116+08:00 0 [System] [MY-011503] [Repl] Plugin group_replication reported: 'Group membership changed to 172.31.7.111:3306, 172.31.7.110:3306, 172.31.7.112:3306 on view 17155785251699230:3.' 2024-05-13T13:45:46.578597+08:00 24 [System] [MY-010597] [Repl] 'CHANGE REPLICATION SOURCE TO FOR CHANNEL 'group_replication_recovery' executed'. Previous state source_host='', source_port= 3306, source_log_file='', source_log_pos= 4, source_bind=''. New state source_host='172.31.7.110', source_port= 3306, source_log_file='', source_log_pos= 4, source_bind=''. 2024-05-13T13:45:46.591623+08:00 25 [Warning] [MY-010897] [Repl] Storing MySQL user name or password information in the connection metadata repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START REPLICA; see the 'START REPLICA Syntax' in the MySQL Manual for more information. 2024-05-13T13:45:46.980845+08:00 25 [System] [MY-014002] [Repl] Replica receiver thread for channel 'group_replication_recovery': connected to source 'rpl_user@172.31.7.110:3306' with server_uuid=32049408-02a5-11ef-8b62-000c29f23176, server_id=1. Starting GTID-based replication. 2024-05-13T13:45:47.183859+08:00 24 [System] [MY-010597] [Repl] 'CHANGE REPLICATION SOURCE TO FOR CHANNEL 'group_replication_recovery' executed'. Previous state source_host='172.31.7.110', source_port= 3306, source_log_file='', source_log_pos= 4, source_bind=''. New state source_host='<NULL>', source_port= 0, source_log_file='', source_log_pos= 4, source_bind=''. 2024-05-13T13:45:47.273497+08:00 0 [System] [MY-011490] [Repl] Plugin group_replication reported: 'This server was declared online within the replication group.' 2024-05-13T13:45:47.533311+08:00 10 [System] [MY-014010] [Repl] Plugin group_replication reported: 'Plugin 'group_replication' has been started.' ``` 如果组复制启动失败,可通过错误⽇志查看具体的报错原因。 ```sql 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. ``` 通过 performance_schema.replication_group_members 查看集群的节点信息。 ```bash mysql> select member_id,member_host,member_port,member_state,member_role from performance_schema.replication_group_members; +--------------------------------------+--------------+-------------+--------------+-------------+ | member_id | member_host | member_port | member_state | member_role | +--------------------------------------+--------------+-------------+--------------+-------------+ | 31b3d46c-02a5-11ef-a0ab-000c297308a4 | 172.31.7.111 | 3306 | ONLINE | SECONDARY | | 32049408-02a5-11ef-8b62-000c29f23176 | 172.31.7.110 | 3306 | ONLINE | PRIMARY | | 320aac89-02a5-11ef-9f18-000c29d13bb5 | 172.31.7.112 | 3306 | ONLINE | SECONDARY | +--------------------------------------+--------------+-------------+--------------+-------------+ 3 rows in set (0.00 sec) ``` 分别在 node2 和 node3 上执⾏以下操作 ```bash mysql> select * from test.t1; +----+------+ | id | c1 | +----+------+ | 1 | abc | +----+------+ 1 row in set (0.01 sec) ``` # 6 部署过程中的常⻅问题 1. 防⽕墙或 SELINUX 开启导致节点之间的⽹络不通。 2. 如果没有绑定主机名或者使⽤ DNS,建议设置 report_host。 # 7 单主模式和多主模式的区别 ## 7.1 部署⽅式 两者部署⽅式基本相同,多主模式需额外设置以下两个参数: ```bash group_replication_single_primary_mode = OFF group_replication_enforce_update_everywhere_checks = ON ``` ## 7.2 read_only 对于单主模式,Group Replication 会⾃动将 Secondary 节点的 super_read_only 和 read_only 设置为 ON。 ## 7.3 ⾃增主键 在单主模式下,auto_increment_offset 和 auto_increment_increment 默认为 1。 多主模式下,auto_increment_offset 和 auto_increment_increment 则分别取⾃ server_id 和 group_replication_auto_increment_increment。 ## 7.4 Group Replication 的限制 很多限制是其实针对多主模式。 ## 7.5 单主模式和多主模式的在线切换 在组复制中,单主模式和多主模式不能混合部署。 ```bash [ERROR] [MY-011529] [Repl] Plugin group_replication reported: 'The member configuration is not compatible with the group configuration. Variables such as group_replication_single_primary_mode or group_replication_enforce_update_everywhere_checks must have the same value on every server in the group. (member configuration option: [group_replication_single_primary_mode], group configuration option: [group_replication_enforce_update_everywhere_checks]).' ``` 在 MySQL 8.0.13 之前,不⽀持在线调整集群模式。如果要调整,只能重启整个组复制。 从 MySQL 8.0.13 开始,可通过以下命令在线调整集群模式。 ```bash # 单主模式切换为多主模式 select group_replication_switch_to_multi_primary_mode(); # 多主模式切换为单主模式 select group_replication_switch_to_single_primary_mode(member_uuid); # 切换单主模式下的 Primary 节点 select group_replication_set_as_primary(member_uuid); ``` ```bash mysql> select member_id,member_host,member_port,member_state,member_role from performance_schema.replication_group_members; +--------------------------------------+--------------+-------------+--------------+-------------+ | member_id | member_host | member_port | member_state | member_role | +--------------------------------------+--------------+-------------+--------------+-------------+ | 31b3d46c-02a5-11ef-a0ab-000c297308a4 | 172.31.7.111 | 3306 | ONLINE | SECONDARY | | 32049408-02a5-11ef-8b62-000c29f23176 | 172.31.7.110 | 3306 | ONLINE | PRIMARY | | 320aac89-02a5-11ef-9f18-000c29d13bb5 | 172.31.7.112 | 3306 | ONLINE | SECONDARY | +--------------------------------------+--------------+-------------+--------------+-------------+ 3 rows in set (0.04 sec) # 单主模式切换为多主模式 mysql> select group_replication_switch_to_multi_primary_mode(); +--------------------------------------------------+ | group_replication_switch_to_multi_primary_mode() | +--------------------------------------------------+ | Mode switched to multi-primary successfully. | +--------------------------------------------------+ 1 row in set (0.02 sec) mysql> select member_id,member_host,member_port,member_state,member_role from performance_schema.replication_group_members; +--------------------------------------+--------------+-------------+--------------+-------------+ | member_id | member_host | member_port | member_state | member_role | +--------------------------------------+--------------+-------------+--------------+-------------+ | 31b3d46c-02a5-11ef-a0ab-000c297308a4 | 172.31.7.111 | 3306 | ONLINE | PRIMARY | | 32049408-02a5-11ef-8b62-000c29f23176 | 172.31.7.110 | 3306 | ONLINE | PRIMARY | | 320aac89-02a5-11ef-9f18-000c29d13bb5 | 172.31.7.112 | 3306 | ONLINE | PRIMARY | +--------------------------------------+--------------+-------------+--------------+-------------+ 3 rows in set (0.00 sec) # 多主模式切换为单主模式 mysql> select group_replication_switch_to_single_primary_mode('31b3d46c-02a5-11ef-a0ab-000c297308a4'); +-----------------------------------------------------------------------------------------+ | group_replication_switch_to_single_primary_mode('31b3d46c-02a5-11ef-a0ab-000c297308a4') | +-----------------------------------------------------------------------------------------+ | Mode switched to single-primary successfully. | +-----------------------------------------------------------------------------------------+ 1 row in set (0.04 sec) mysql> select member_id,member_host,member_port,member_state,member_role from performance_schema.replication_group_members; +--------------------------------------+--------------+-------------+--------------+-------------+ | member_id | member_host | member_port | member_state | member_role | +--------------------------------------+--------------+-------------+--------------+-------------+ | 31b3d46c-02a5-11ef-a0ab-000c297308a4 | 172.31.7.111 | 3306 | ONLINE | PRIMARY | | 32049408-02a5-11ef-8b62-000c29f23176 | 172.31.7.110 | 3306 | ONLINE | SECONDARY | | 320aac89-02a5-11ef-9f18-000c29d13bb5 | 172.31.7.112 | 3306 | ONLINE | SECONDARY | +--------------------------------------+--------------+-------------+--------------+-------------+ 3 rows in set (0.01 sec) # 切换单主模式下的 Primary 节点 mysql> select group_replication_set_as_primary('32049408-02a5-11ef-8b62-000c29f23176'); +--------------------------------------------------------------------------+ | group_replication_set_as_primary('32049408-02a5-11ef-8b62-000c29f23176') | +--------------------------------------------------------------------------+ | Primary server switched to: 32049408-02a5-11ef-8b62-000c29f23176 | +--------------------------------------------------------------------------+ 1 row in set (0.02 sec) mysql> select member_id,member_host,member_port,member_state,member_role from performance_schema.replication_group_members; +--------------------------------------+--------------+-------------+--------------+-------------+ | member_id | member_host | member_port | member_state | member_role | +--------------------------------------+--------------+-------------+--------------+-------------+ | 31b3d46c-02a5-11ef-a0ab-000c297308a4 | 172.31.7.111 | 3306 | ONLINE | SECONDARY | | 32049408-02a5-11ef-8b62-000c29f23176 | 172.31.7.110 | 3306 | ONLINE | PRIMARY | | 320aac89-02a5-11ef-9f18-000c29d13bb5 | 172.31.7.112 | 3306 | ONLINE | SECONDARY | +--------------------------------------+--------------+-------------+--------------+-------------+ 3 rows in set (0.00 sec) ``` # 8 MGR的限制和监控 ## 8.1 Group Replication 的要求 - 只⽀持 InnoDB 存储引擎。 - 表上必须存在主键或唯⼀⾮空索引。 - MySQL 8.0.14 之前,只⽀持 IPv4,不⽀持 IPv6。 - MySQL 8.0.21 之前,不⽀持带有 checksum 的⼆进制⽇志事件。 所以在 MySQL 8.0.21 之前,binlog_checksum 只能设置为 NONE。 - 组复制可允许的最⼤节点数是 9。 - 不允许对 group_replication_applier 或 group_replication_recovery 通道设置过滤规则 - MySQL 8.0.20 之前,Group Replication 在运⾏过程中不允许⼿动执⾏克隆操作。 ## 8.2 多主模式下,Group Replication 的限制 - 验证阶段不会考虑间隙锁(Gap Locks),表锁(Lock Tables)和 GET_LOCK 操作 对于多主模式,官⽅建议将事务隔离级别设置为 RC。 - 不能通过 SELECT .. FOR UPDATE,SELECT .. FOR SHARE 锁定数据 - DDL 与 DML 的并发执⾏问题。 组复制中没有对 DDL 进⾏冲突检测。当针对同⼀对象的 DDL 和 DML 操作在不同节点并发执⾏时,有可能会 导致节点数据不⼀致。 - 不允许将事务隔离级别设置为 SERIALIZABLE。 - 不允许外键的级联删除。 最后两个限制是由 group_replication_enforce_update_everywhere_checks 参数控制的。多主模式下,⼀般会设 置为 ON。 ## 8.3 Group Replication 的监控 ```sql mysql> select * from performance_schema.replication_group_members\G *************************** 1. row *************************** CHANNEL_NAME: group_replication_applier MEMBER_ID: 31b3d46c-02a5-11ef-a0ab-000c297308a4 MEMBER_HOST: 172.31.7.111 MEMBER_PORT: 3306 MEMBER_STATE: ONLINE MEMBER_ROLE: SECONDARY MEMBER_VERSION: 8.0.36 MEMBER_COMMUNICATION_STACK: XCom *************************** 2. row *************************** CHANNEL_NAME: group_replication_applier MEMBER_ID: 32049408-02a5-11ef-8b62-000c29f23176 MEMBER_HOST: 172.31.7.110 MEMBER_PORT: 3306 MEMBER_STATE: ONLINE MEMBER_ROLE: PRIMARY MEMBER_VERSION: 8.0.36 MEMBER_COMMUNICATION_STACK: XCom *************************** 3. row *************************** CHANNEL_NAME: group_replication_applier MEMBER_ID: 320aac89-02a5-11ef-9f18-000c29d13bb5 MEMBER_HOST: 172.31.7.112 MEMBER_PORT: 3306 MEMBER_STATE: ONLINE MEMBER_ROLE: SECONDARY MEMBER_VERSION: 8.0.36 MEMBER_COMMUNICATION_STACK: XCom 3 rows in set (0.01 sec) ``` 各字段的含义如下: - CHANNEL_NAME:CHANNEL 名。组复制引⼊了两个 CHANNEL:group_replication_recovery 和 group_replication_applier。 - MEMBER_ID:节点的 UUID。 - MEMBER_HOST:节点的主机名。默认由 @@hostname 决定。 - MEMBER_PORT:节点的对外服务端⼝,由 @@port 决定。 - MEMBER_STATE:节点的状态:ONLINE(节点状态正常),RECOVERING(节点处于 Distributed Recovery 阶段),OFFLINE(已加载组复制插件,但还未开启组复制),ERROR(错误状态), UNREACHABLE(发送给⽬标节点的组消息超时)。 - MEMBER_ROLE:节点⻆⾊,PRIMARY 或 SECONDARY。 - MEMBER_VERSION:实例版本。 - MEMBER_COMMUNICATION_STACK:节点之间使⽤的通信栈。 ```sql mysql> select * from performance_schema.replication_group_member_stats\G *************************** 1. row *************************** CHANNEL_NAME: group_replication_applier VIEW_ID: 17155785251699230:3 MEMBER_ID: 31b3d46c-02a5-11ef-a0ab-000c297308a4 COUNT_TRANSACTIONS_IN_QUEUE: 0 COUNT_TRANSACTIONS_CHECKED: 3 COUNT_CONFLICTS_DETECTED: 0 COUNT_TRANSACTIONS_ROWS_VALIDATING: 0 TRANSACTIONS_COMMITTED_ALL_MEMBERS: 22e34e6b-f37e-11ed-a2a8-000c29f6d187:1-9, 32049408-02a5-11ef-8b62-000c29f23176:1-361 LAST_CONFLICT_FREE_TRANSACTION: 22e34e6b-f37e-11ed-a2a8-000c29f6d187:8 COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE: 0 COUNT_TRANSACTIONS_REMOTE_APPLIED: 4 COUNT_TRANSACTIONS_LOCAL_PROPOSED: 0 COUNT_TRANSACTIONS_LOCAL_ROLLBACK: 0 *************************** 2. row *************************** CHANNEL_NAME: group_replication_applier VIEW_ID: 17155785251699230:3 MEMBER_ID: 32049408-02a5-11ef-8b62-000c29f23176 COUNT_TRANSACTIONS_IN_QUEUE: 0 COUNT_TRANSACTIONS_CHECKED: 6 COUNT_CONFLICTS_DETECTED: 0 COUNT_TRANSACTIONS_ROWS_VALIDATING: 0 TRANSACTIONS_COMMITTED_ALL_MEMBERS: 22e34e6b-f37e-11ed-a2a8-000c29f6d187:1-9, 32049408-02a5-11ef-8b62-000c29f23176:1-361 LAST_CONFLICT_FREE_TRANSACTION: 22e34e6b-f37e-11ed-a2a8-000c29f6d187:8 COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE: 0 COUNT_TRANSACTIONS_REMOTE_APPLIED: 3 COUNT_TRANSACTIONS_LOCAL_PROPOSED: 6 COUNT_TRANSACTIONS_LOCAL_ROLLBACK: 0 *************************** 3. row *************************** CHANNEL_NAME: group_replication_applier VIEW_ID: 17155785251699230:3 MEMBER_ID: 320aac89-02a5-11ef-9f18-000c29d13bb5 COUNT_TRANSACTIONS_IN_QUEUE: 0 COUNT_TRANSACTIONS_CHECKED: 0 COUNT_CONFLICTS_DETECTED: 0 COUNT_TRANSACTIONS_ROWS_VALIDATING: 0 TRANSACTIONS_COMMITTED_ALL_MEMBERS: 22e34e6b-f37e-11ed-a2a8-000c29f6d187:1-9, 32049408-02a5-11ef-8b62-000c29f23176:1-361 LAST_CONFLICT_FREE_TRANSACTION: COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE: 0 COUNT_TRANSACTIONS_REMOTE_APPLIED: 0 COUNT_TRANSACTIONS_LOCAL_PROPOSED: 0 COUNT_TRANSACTIONS_LOCAL_ROLLBACK: 0 3 rows in set (0.01 sec) ``` 各字段的含义如下: - CHANNEL_NAME:CHANNEL 名。 - VIEW_ID:组视图 ID。 - MEMBER_ID:节点的 UUID。 - COUNT_TRANSACTIONS_IN_QUEUE:队列中等待冲突检测的事务数。 - COUNT_TRANSACTIONS_CHECKED:已经进⾏过冲突检测的事务数,包括通过和没有通过的。 - COUNT_CONFLICTS_DETECTED:冲突检测失败的事务数 - COUNT_TRANSACTIONS_ROWS_VALIDATING:冲突检测数据库当前的记录数。 - TRANSACTIONS_COMMITTED_ALL_MEMBERS:所有成员都执⾏过的事务的 GTID 集合,60s 更新⼀次。 - LAST_CONFLICT_FREE_TRANSACTION:最近⼀个通过冲突检测的事务的 GTID。 - COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE:从组接受,等待应⽤的事务数。 - COUNT_TRANSACTIONS_REMOTE_APPLIED:从组接受,已经应⽤的事务数。 - COUNT_TRANSACTIONS_LOCAL_PROPOSED:由当前节点发起,发送给组的事务数。 - COUNT_TRANSACTIONS_LOCAL_ROLLBACK:由当前节点发起,但被组回滚的事务数。 **成员状态** ```bash #MGR node状态 offline-->online-->re-->error #MGR加入节点状态 re-->online re-->un ``` ![image](https://img.sunrisenan.com/img/2024/05/13/141859354.png) ```sql mysql> select * from performance_schema.replication_group_members; +---------------------------+--------------------------------------+--------------+-------------+--------------+-------------+----------------+----------------------------+ | CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | MEMBER_COMMUNICATION_STACK | +---------------------------+--------------------------------------+--------------+-------------+--------------+-------------+----------------+----------------------------+ | group_replication_applier | 31b3d46c-02a5-11ef-a0ab-000c297308a4 | 172.31.7.111 | 3306 | ONLINE | SECONDARY | 8.0.36 | XCom | | group_replication_applier | 32049408-02a5-11ef-8b62-000c29f23176 | 172.31.7.110 | 3306 | ONLINE | PRIMARY | 8.0.36 | XCom | | group_replication_applier | 320aac89-02a5-11ef-9f18-000c29d13bb5 | 172.31.7.112 | 3306 | ONLINE | SECONDARY | 8.0.36 | XCom | +---------------------------+--------------------------------------+--------------+-------------+--------------+-------------+----------------+----------------------------+ 3 rows in set (0.01 sec) ``` 输出结果中主要几个列的解读如下: - MEMBER_ID 列值就是各节点的 server_uuid,用于唯一标识每个节点,在命令行模式下,调用 udf 时传入 MEMBER_ID 以指定各节点。 - MEMBER_ROLE 表示各节点的角色,如果是 PRIMARY 则表示该节点可接受读写事务,如果是 SECONDARY 则表示该节点只能接受只读事务。如果只有一个节点是 PRIMARY,其余都是 SECONDARY,则表示当前处于 单主模式;如果所有节点都是 PRIMARY,则表示当前处于 多主模式。 - MEMBER_STATE 表示各节点的状态,共有几种状态:ONLINE、RECOVERING、OFFLINE、ERROR、UNREACHABLE 等,下面分别介绍几种状态。 - ONLINE,表示节点处于正常状态,可提供服务。 - RECOVERING,表示节点正在进行分布式恢复,等待加入集群,这时候有可能正在从donor节点利用clone复制数据,或者传输binlog中。 - OFFLINE,表示该节点当前处于离线状态。提醒,在正要加入或重加入集群时,可能也会有很短瞬间的状态显示为 OFFLINE。 - ERROR,表示该节点当前处于错误状态,无法成为集群的一员。当节点正在进行分布式恢复或应用事务时,也是有可能处于这个状态的。当节点处于ERROR状态时,是无法参与集群事务裁决的。节点正在加入或重加入集群时,在完成兼容性检查成为正式MGR节点前,可能也会显示为ERROR状态。 - UNREACHABLE,当组通信消息收发超时时,故障检测机制会将本节点标记为怀疑状态,怀疑其可能无法和其他节点连接,例如当某个节点意外断开连接时。当在某个节点上看到其他节点处于 UNREACHABLE 状态时,有可能意味着此时部分节点发生了网络分区,也就是多个节点分裂成两个或多个子集,子集内的节点可以互通,但子集间无法互通。 当节点的状态不是ONLINE 时,就应当立即发出告警并检查发生了什么。 在节点状态发生变化时,或者有节点加入、退出时,表 performance_schema.replication_group_members 的数据都会更新,各节点间会交换和共享这些状态信息,因此可以在任意节点查看。 **事务和队列状态监控** ```sql mysql> select MEMBER_ID as id,COUNT_TRANSACTIONS_IN_QUEUE as trx_qu from performance_schema.replication_group_member_stats; +--------------------------------------+--------+ | id | trx_qu | +--------------------------------------+--------+ | 31b3d46c-02a5-11ef-a0ab-000c297308a4 | 0 | | 32049408-02a5-11ef-8b62-000c29f23176 | 0 | | 320aac89-02a5-11ef-9f18-000c29d13bb5 | 0 | +--------------------------------------+--------+ 3 rows in set (0.01 sec) 其中,relaylog_tobe_applied 的值表示远程事务写到relay log后,等待回放的事务队列,trx_tobe_certified 表示等待被认证的事务队列大小,这二者任何一个值大于0,都表示当前有一定程度的延迟。 还可以通过关注上述两个数值的变化,看看两个队列是在逐步加大还是缩小,据此判断Primary节点是否"跑得太快"了,或者Secondary节点是否"跑得太慢"。 多提一下,在启用流控(flow control)时,上述两个值超过相应的阈值时(group_replication_flow_control_applier_threshold 和 group_replication_flow_control_certifier_threshold 默认阈值都是 25000),就会触发流控机制。 #查看工作线程状态 mysql> select * from performance_schema.replication_applier_status; +---------------------------+---------------+-----------------+----------------------------+ | CHANNEL_NAME | SERVICE_STATE | REMAINING_DELAY | COUNT_TRANSACTIONS_RETRIES | +---------------------------+---------------+-----------------+----------------------------+ | group_replication_applier | ON | NULL | 0 | +---------------------------+---------------+-----------------+----------------------------+ 1 row in set (0.01 sec) #其他监控 另外,也可以查看接收到的事务和已执行完的事务之间的差距来判断: mysql> SELECT RECEIVED_TRANSACTION_SET FROM performance_schema.replication_connection_status WHERE channel_name = 'group_replication_applier' UNION ALL SELECT variable_value FROM performance_schema.global_variables WHERE variable_name = 'gtid_executed'\G *************************** 1. row *************************** RECEIVED_TRANSACTION_SET: 6cfb873b-573f-11ec-814a-d08e7908bcb1:1-3124520 *************************** 2. row *************************** RECEIVED_TRANSACTION_SET: 6cfb873b-573f-11ec-814a-d08e7908bcb1:1-3078139 可以看到,接收到的事务 GTID 已经到了 3124520,而本地只执行到 3078139,二者的差距是 46381。 可以顺便持续关注这个差值的变化情况,估算出本地节点是否能及时追平延迟,还是会加大延迟。 另外,当原来的主节点发生故障,想要手动选择某个节点做为新的主节点时,也应该先判断哪个节点已执行的事务GTID值更大,应优先选择该节点。 #per库相关MGR表 show tables like '%replication%'; ``` # 9 MGR 的新主选举算法 MGR 的新主选举算法,在节点版本⼀致的情况下, ⾸先⽐较权重(节点的权重由 group_replication_member_weight 决定,该参数是 MySQL 5.7.20 引⼊的,可设 置 0 到 100 之间的任意整数值,默认是 50),权重越⾼,选为新主的优先级越⾼。 如果权重⼀致,则会进⼀步⽐较节点的 server_uuid。server_uuid 越⼩,选为新主的优先级越⾼。 所以,在节点版本⼀致的情况下,会选择权重最⾼,server_uuid 最⼩的节点作为新的主节点。 但如果集群节点版本不⼀致,实际的选举算法就⽐较复杂了。 1. 如果集群中存在 MySQL 5.7 的节点,则会将 MySQL 5.7 的节点作为候选节点。 2. 如果集群节点的版本都是 MySQL 8.0,这⾥需要区分两种情况: - 如果最⼩版本⼩于 MySQL 8.0.17,则所有的节点都可作为候选节点。 - 如果最⼩版本⼤于等于 MySQL 8.0.17,则只有最⼩版本的节点会作为候选节点。 3. 在候选节点的基础上,会进⼀步根据候选节点的权重和 server_uuid 选择 Primary 节点。具体来说, - 如果候选节点中存在 MySQL 5.7.20 之前版本的节点,则会选择 server_uuid 最⼩的节点作为 Primary 节 点。 - 如果候选节点都⼤于等于 MySQL 5.7.20,则会选择权重最⾼,server_uuid 最⼩的节点作为 Primary 节点。 源码分析流程:https://mp.weixin.qq.com/s/ocKIsWqIVSfe79VQBDNOpg **⼿动选主** 从 MySQL 8.0.13 开始,可以通过下⾯两个函数⼿动选择新的主节点: - group_replication_set_as_primary(server_uuid) :切换单主模式下的 Primary 节点。 - group_replication_switch_to_single_primary_mode([server_uuid]) :将多主模式切换为单主模式。可通过 server_uuid 指定单主模式下的 Primary 节点。 在使⽤这两个参数时,注意,指定的 server_uuid 必须属于候选节点。 另外,这两个函数是 MySQL 8.0.13 引⼊的,所以,如果集群中存在 MySQL 8.0.13 之前的节点,执⾏时会报错。 # 10 MGR流控机制 ## 10.1 流控出现的背景 Group Replication 是⼀种 Shared-Nothing 的架构,每个节点都会保留⼀份数据。虽然⽀持多点写⼊,但实际上 系统的吞吐量是由处理能⼒最弱的那个节点决定的。如果各个节点的处理能⼒参差不⻬,那处理能⼒慢的节点就会 出现事务堆积。 在事务堆积的时候,如果处理能⼒快的节点出现了故障,这个时候能否让处理能⼒慢的节点(存在事务堆积)接受 业务流量呢? 1. 如果不等待堆积事务应⽤完,直接接受业务流量。 ⼀⽅⾯会读到旧数据,另⼀⽅⾯也容易出现写冲突。 2. 如果等待堆积事务应⽤完才接受业务流量,⼜会影响数据库服务的可⽤性。 为了避免出现上述两难场景,Group Replication 引⼊了流控机制。 在实现上,Group Replication 的流控模块会定期检查各个节点的事务堆积情况,如果超过⼀定值,则会触发流 控。 流控会基于上⼀周期各个节点的事务认证情况和事务应⽤情况,决定当前节点(注意是当前节点,不是其它节点) 下个周期的写⼊配额。 超过写⼊配额的事务操作会被阻塞,等到下个周期才能执⾏。 ## 10.2 触发流控的条件 ```python bool Pipeline_member_stats::is_flow_control_needed() { return (m_flow_control_mode == FCM_QUOTA) && (m_transactions_waiting_certification > get_flow_control_certifier_threshold_var() || m_transactions_waiting_apply > get_flow_control_applier_threshold_var()); } ``` 触发流控需满⾜以下条件: 1. group_replication_flow_control_mode 设置为 QUOTA。 2. 当前等待认证的事务数⼤于 group_replication_flow_control_certifier_threshold。 当前等待认证的事务数可通过 performance_schema.replication_group_member_stats 中的 COUNT_TRANSACTIONS_IN_QUEUE 查看。 3. 当前等待应⽤的事务数⼤于 group_replication_flow_control_applier_threshold。 当前等待应⽤的事务数可通过 performance_schema.replication_group_member_stats 中的 COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE 查看。 除了条件 1,条件 2,3 满⾜其⼀即可。 **流控的相关参数** - group_replication_flow_control_mode 是否开启流控。默认是 QUOTA,基于配额进⾏流控。如果设置为 DISABLED ,则关闭流控。 - group_replication_flow_control_period 流控周期。有效值 1 - 60,单位秒。默认是 1。 注意,各个节点的流控周期应保持⼀致,否则的话,就会将周期较短的节点配额作为集群配额。 - group_replication_flow_control_applier_threshold 待应⽤的事务数如果超过 group_replication_flow_control_applier_threshold 的设置,则会触发流控,该参数默 认是 25000。 - group_replication_flow_control_certifier_threshold 待认证的事务数如果超过 group_replication_flow_control_certifier_threshold 的设置,则会触发流控,该参数默 认是 25000。 - group_replication_flow_control_min_quota - group_replication_flow_control_min_recovery_quota 两个参数都会决定当前节点下个周期的最⼩配额。 只不过 group_replication_flow_control_min_recovery_quota 适⽤于新节点加⼊时的分布式恢复阶段。 group_replication_flow_control_min_quota 则适⽤于所有场景。 如果两者同时设置了, group_replication_flow_control_min_quota 的优先级更⾼。两者默认都为 0,即不限制。 - group_replication_flow_control_max_quota 当前节点下个周期的最⼤配额。默认是 0,即不限制。 - group_replication_flow_control_member_quota_percent 分配给当前成员的配额⽐例。有效值 0 - 100。默认为 0,此时,节点配额 = 集群配额 / 上个周期写节点的数量。 注意,这⾥的写节点指的是有实际写操作的节点,不是仅指 PRIMARY 节点。毕竟不是所有的 PRIMARY 节点都会 有写操作。 另外,设置配额⽐例时,不要求所有节点的配额⽐例加起来等于 100。 - group_replication_flow_control_hold_percent 预留配额的⽐例。有效值 0 - 100,默认是 10。预留的配额可⽤来处理落后节点积压的事务。 - group_replication_flow_control_release_percent 当流控结束后,会逐渐增加吞吐量以避免出现突刺。 下⼀周期的 quota_size = 上⼀周期的 quota_size * (1 + group_replication_flow_control_release_percent / 100)。有效值 0 - 1000,默认是 50。 ## 10.3 流控⼩结 1. 从可⽤性的⻆度出发,不建议线上关闭流控。虽然主节点出现故障的概率很⼩,但墨菲定律告诉我们,任何 有可能发⽣的事情最后⼀定会发⽣。在线上还是不要⼼存侥幸。 2. 流控限制的是当前节点的流量,不是其它节点的。 3. 流控参数在各节点应保持⼀致,尤其是 group_replication_flow_control_period。 ## 10.4 从源码分析 MGR 的流控机制 https://mp.weixin.qq.com/s/lMdOqsvu5DqIGKyZahw4Sw # 11 MGR故障检测流程 故障检测(Failure Detection)是 Group Replication 的⼀个核⼼功能模块,通过它可以及时识别集群中的故障节 点,并将故障节点从集群中剔除掉。如果不将故障节点及时剔除的话,⼀⽅⾯会影响集群的性能,另⼀⽅⾯还会 阻⽌集群拓扑的变更。 Group Repliction 的故障检测流程。 1. 集群中每个节点都会定期(每秒 1 次)向其它节点发送⼼跳信息。如果在 5s 内(固定值,⽆参数调整)没 有收到其它节点的⼼跳信息,则会将该节点标记为可疑节点,同时会将该节点的状态设置为 UNREACHABLE 。如果集群中有等于或超过 1/2 的节点显示为 UNREACHABLE ,则该集群不能对外提供写服务。 2. 如果在group_replication_member_expel_timeout(从 MySQL 8.0.21 开始,该参数的默认值为 5,单位 s,最⼤可设置值为3600,即 1 ⼩时)时间内,可疑节点恢复正常,则会直接应⽤ XCom Cache 中的消息。 XCom Cache 的⼤⼩由group_replication_message_cache_size 决定,默认是 1G。 3. 如果在 group_replication_member_expel_timeout 时间内,可疑节点没有恢复正常,则会被驱逐出集群。 4. ⽽少数派节点呢,不会⾃动离开集群,它会⼀直维持当前的状态,直到: - ⽹络恢复正常。 - 达到 group_replication_unreachable_majority_timeout 的限制。注意,该参数的起始计算时间是连 接断开 5s 之后,不是可疑节点被驱逐出集群的时间。该参数默认为 0。 5. ⽆论哪种情况,都会触发: - 节点状态从 ONLINE 切换到 ERROR 。 - 回滚当前被阻塞的写操作。 ```bash mysql> delete from slowtech.t1 where id=1; ERROR 3100 (HY000): Error on observer while running replication hook 'before_commit'. ``` 6. ERROR 状态的节点会⾃动设置为只读。 7. 如果 group_replication_autorejoin_tries 不为 0,对于 ERROR 状态的节点,会⾃动重试,重新加⼊集群 (auto-rejoin)。 8. 如果 group_replication_autorejoin_tries 为 0 或重试失败,则会执⾏ group_replication_exit_state_action 指定的操作。 可选的操作有: - READ_ONLY:只读模式。在这种模式下,会将 super_read_only 设置为 ON。默认值。 - OFFLINE_MODE:离线模式。在这种模式下,会将 offline_mode 和 super_read_only 设置为 ON,此 时,只有CONNECTION_ADMIN(SUPER)权限的⽤户才能登陆,普通⽤户不能登录。 - ABORT_SERVER:关闭实例。 如果集群中存在 UNREACHABLE 的节点,会有以下限制和不⾜: 1. 不能调整集群的拓扑,包括添加和删除节点。 2. 在单主模式下,如果 Primary 节点出现故障了,⽆法选择新主。 3. 如果 Group Replication 的⼀致性级别等于 AFTER 或 BEFORE_AND_AFTER,则写操作会⼀直等待,直到 UNREACHABLE 节点 ONLINE 并应⽤该操作。 4. 集群吞吐量会下降。如果是单主模式,可将 group_replication_paxos_single_leader (MySQL 8.0.27 引⼊ 的)设置为 ON 解决这个问题。 所以,在线上 group_replication_member_expel_timeout 不宜设置过⼤。 基于案例分析 MGR 的故障检测流程:https://mp.weixin.qq.com/s/POmy-dtufJtzGWnu24zgmg # 12 MGR事务一致性保障 Group Replication 的实现原理。 1. 事务 T1 在 M1 上发起。 2. 提交时,事务信息会通过 GCS 模块发送给集群所有节点。 3. 当集群⼤多数节点确认接受后,每个成员开始独⽴进⾏冲突检测。 - 如果存在冲突,则回滚事务。 - 如果没有冲突,M1 会直接提交事务。对于 M2 和 M3,则会先写⼊到 Relay Log 中,然后再重放。 既然事务在 M2 和 M3 中是异步处理的,所以就有可能出现下⾯这种情况:事务 T2 在 M3 上发起,虽然是在 T1 之后执⾏的,但由于它执⾏时,T1 对应的操作还没在 M3 上重放,所以 T2 读取的数据有可能不是最新的。 ```bash - 通过消息传播和施放进行数据同步 - 8.0.14后通过变量 group_replication_consistency 精确地控制每个节点上数据的一致性。 - 一个事务广播到其他节点,达成共识(一致性冲突监测),本地提交,异地异步回方,最终一致性 - 五种模式(8.0.14后) - EVENTUAL(默认) - BEFORE - AFTER - BEFORE_AND_AFTER #特点 优点:MGR+DAL,在单主模式下,可以根据业务场景进行读写分离,不用担心会产生延迟,充分利用了MGR主节点以外的节点。 缺点:使用读写一致性会对性能有极大影响,尤其是网络环境不稳定的场景下。 ``` ![Untitled](https://img.sunrisenan.com/img/2024/05/13/160754143.png) - EVENTUAL 最终⼀致性。 EVENTUAL 默认值,开启该级别的事务(T2),事务执行前不会等待先序事务(T1)的回放完成,也不会影响后序事务等待该事务回放完成。 ![image (1)](https://img.sunrisenan.com/img/2024/05/13/161019389.png) - BEFORE_ON_PRIMARY_FAILOVER 当发⽣故障切换时,必须等待新主应⽤完积压队列中的事务,才开始响应业务的读写请求,这样能避免故障切换时 业务读到旧数据。 开启该级别等事务(T2),需要等待前序事务的回放完成(T1);同时后序事务(T3)等待该事务的回放完成; ![Untitled (1)](https://img.sunrisenan.com/img/2024/05/13/161145248.png) - BEFORE 事务执⾏时,会等待它之前的事务执⾏完才开始执⾏。这样,能确保读到的数据⼀定是最新的。 ```bash BEFORE 开启了该级别的事务(T2),在开始前首先要等待先序事务(T1)的回放完成,确保此事务将在最新的数据上执行。 使用场景 - 应用大量写入数据,偶尔进行读取一致性数据,应当选择BEFORE。 - 有特定事务需要读写一致性,以便对敏感数据操作时,始终读取最新的数据;应当选择BEFORE。 ``` ![Untitled (2)](https://img.sunrisenan.com/img/2024/05/13/161323143.png) - AFTER 事务执⾏时,会等到它在所有的节点上都应⽤完才返回给客户端。这样就能确保 AFTER 事务⼀旦执⾏完,后续其 它节点的事务都能读到它的最新值。 ```bash AFTER,开启该级别的事务(T1),只有等该事务回放完成。其他后序事务(T2)才开始执行,这样所有后序事务都会读取包含其更改的数据库状态,而不管它们在哪个成员上执行。 适用场景: - 写少读多的场景进行读写分离,担心读取到过期事务,可选择AFTER。 - 只读为主的集群,有RW的事务需要保证提交的事务能被其他后序事务读到最新读数据,可选择AFTER 特点: - 任何单点的故障都会导致集群不可用,到节点提出集群(5-10s) - 可以满足部分业务对严格数据一致性的需求,但对一般的业务却是极度不友好的 - 不推荐使用 ``` ![Untitled (3)](https://img.sunrisenan.com/img/2024/05/13/161434722.png) - BEFORE_AND_AFTER BEFORE_AND_AFTER 实际上是 BEFORE 和 AFTER 两种模式的结合,即事务在执⾏时, 1. 会等待它之前的事务执⾏完才开始执⾏。 2. 会等到它在所有节点上都应⽤完才返回给客户端。 ```bash 开启该级别等事务(T2),需要等待前序事务的回放完成(T1);同时后序事务(T3)等待该事务的回放完成; 场景: 有一个读为主的集群,有RW的事务既要保证读到最新的数据,又要保证这个事务提交后,被其他后序事务读到;在这种情况下可选择BEFORE_AND_AFTER。 ``` ![Untitled (4)](https://img.sunrisenan.com/img/2024/05/13/161620970.png) - 主节点故障转移,从节点切换成主节点前,处理积压事务 ```bash #方案1 不管积压事务,处理堆积事务+旧事物,可能读取旧版本数据 #方案2 访问限制,积压事务处理完成处理新事务 ``` - 一致性级别选择建议 一致性级别选择建议 对于绝大多数场景,使用默认的 EVENTUAL 等级就足够了;尤其是在使用单主模式时,如果需要实时读取事务数据,只需向Primary节点发起请求即可。 进一步,如果担心Primary节点切换时会读取到旧事务数据,可以提高到 BEFORE_ON_PRIMARY_FAILOVER 级别。 更进一步,如果希望在Secondary节点也能及时读取到最新事务数据,以此提高读扩展能力,可以提高到 BEFORE 级别。 更高的一致性级别就不再建议使用了,潜在的风险以及bug比较多。 P.S,各个节点的一致性级别最好都设置成一样,并且在运行过程中也不要修改其session级选项值,避免造成不可预料的影响。 **总结** 1. group_replication_consistency 即可在全局级别设置,也可在会话级别设置。级别越⾼,对性能的影响也越 ⼤。 2. ⽆论是 BEFORE 还是 AFTER,设置的初衷都是为了能读到最新的数据。只不过两者的作⽤对象不同, BEFORE 作⽤在读操作上⾯,只会影响当前会话的事务。⽽ AFTER 则作⽤在写操作上⾯,⼀旦事务提交,后 续其它节点都能读到该事务的最新数据,但它会影响其它节点事务的提交。 # 13 InnoDB Cluster > MySQL Shell 8.0 https://dev.mysql.com/doc/mysql-shell/8.0/en/ 第 7 章 MySQL InnoDB 集群 https://dev.mysql.com/doc/mysql-shell/8.0/en/mysql-innodb-cluster.html ## 13.1 InnoDB Cluster 架构图 MySQL InnoDB Cluster(MIC) 想必大家已经非常熟悉,由三个组件组成,分别为:MySQL Shell、MySQL Router 、MySQL MGR 。 MySQL Shell 用来进行MGR的日常运维,MySQL Router 对上层应用提供一个简单的读写分离入口,MySQL MGR 则是用来存放真实数据的多个 MySQL 实例。对应的架构如下: ![img](https://img.sunrisenan.com/img/2024/05/13/163529131.png) 结构图介绍 1. MySQL Servers - MySQL Group Replication,简称MGR, 是 MySQL 的主从同步高可用方案,包括数据同步及角色选举 - 里面有三个服务节点 (官网指定服务的节点数最少 3 个最多 9 个),如果过多或造成性能下降 - 1.1 其中有一个主节点,两个从节点 - 1.2 主节点可以进行读写操作,从节点只能进行读取操作 - 1.3 集群设置默认为单主模式,当然还可以设置成多主模式 (下面会介绍) 2. MySQL Router - 是业务流量入口,支持对MGR的主从角色判断,可以配置不同的端口分别对外提供读写服务,实现读写分离(重要) 3. MySQL Shell(Cluster Admin) - 是 InnoDB Cluster 的管理工具,用于管理和配置集群 (重要) 4. Client App(MYSQL Connector) - 这个节点部署的是项目 (非重点,和本章课程无关,可以看成是操作数据库的Java项目) 那如果想针对 MIC 做一个容灾功能,该怎么做?如果你一直使用 MySQL 8.0,并且保持 MySQL 版本一直为最新,那答案是肯定的(最新的 MySQL 8.0 小版本为 8.0.28),新名字为 MySQL InnoDB Cluster Set(MICS)。 这个新特性其实就是基于纯粹的 MIC 做容灾。 比如北京上地一套 MIC 对外提供服务,东直门另外一套 MIC 用来做灾备,两套 MIC 通过专用复制通道clusterset_replication来同步数据。 截取官网的架构如下: ![img](https://img.sunrisenan.com/img/2024/05/13/180201893.png) MICS 虽然看起来挺好,但是限制很多,几个主要限制如下: MICS 的最大特性是高可用,而不是一致性。由于数据传输依赖传统的 MySQL 异步复制(不能使用半同步),无法避免异步复制的缺陷:数据延迟、数据一致性、需要手动故障转移等等。 从库不能是已有的 MIC ,必须新建。所以在搭建 MICS 前,得想办法解决已有数据的保存问题。 MICS 内部 MIC 限制为单主模式,不能多主。 MICS 内部只有一套 MIC 对外提供服务,其他只能作为备库。 只支持 MySQL 8.0 。 ## 13.2 下载 MySQL Shell ```bash cd /usr/local/ wget https://dev.mysql.com/get/Downloads/MySQL-Shell/mysql-shell-8.0.36-linux-glibc2.12-x86-64bit.tar.gz tar xvf mysql-shell-8.0.36-linux-glibc2.12-x86-64bit.tar.gz ln -s mysql-shell-8.0.36-linux-glibc2.12-x86-64bit mysql-shell export PATH=$PATH:/usr/local/mysql-shell/bin ``` ## 13.3 部署 InnoDB Cluster ### 13.3.1 初始化实例 ```bash root@db01:~# cat /etc/my.cnf [client] socket = /data/mysql3306/data/mysql.sock [mysqld] # Server user = mysql datadir = /data/mysql3306/data #basedir = /usr/local/mysql basedir = /opt/mysql-8.0.36 port = 3306 socket = /data/mysql3306/data/mysql.sock log_timestamps = system log_error = /data/mysql3306/data/mysqld.err skip_name_resolve report_host = "172.31.7.110" disabled_storage_engines = "MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY" sql_require_primary_key = ON # Replication server_id = 1 log_bin = mysql-bin binlog_format = ROW log_slave_updates = ON gtid_mode = ON enforce_gtid_consistency = ON master_info_repository = TABLE relay_log_info_repository = TABLE #super_read_only = ON binlog_transaction_dependency_tracking = WRITESET transaction_write_set_extraction = XXHASH64 # Multi-threaded Replication slave_parallel_type = LOGICAL_CLOCK slave_preserve_commit_order = ON slave_parallel_workers = 4 # Group Replication Settings #plugin_load_add = "group_replication.so" #loose_group_replication_group_name = "22e34e6b-f37e-11ed-a2a8-000c29f6d187" #loose_group_replication_start_on_boot = OFF #loose_group_replication_local_address = "172.31.7.110:33061" #loose_group_replication_group_seeds = "172.31.7.110:33061,172.31.7.111:33061,172.31.7.112:33061" #loose_group_replication_bootstrap_group = OFF #loose_group_replication_recovery_get_public_key = ON root@db02:~# cat /etc/my.cnf [client] socket = /data/mysql3306/data/mysql.sock [mysqld] # Server user = mysql datadir = /data/mysql3306/data #basedir = /usr/local/mysql basedir = /opt/mysql-8.0.36 port = 3306 socket = /data/mysql3306/data/mysql.sock log_timestamps = system log_error = /data/mysql3306/data/mysqld.err skip_name_resolve report_host = "172.31.7.111" disabled_storage_engines = "MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY" sql_require_primary_key = ON # Replication server_id = 2 log_bin = mysql-bin binlog_format = ROW log_slave_updates = ON gtid_mode = ON enforce_gtid_consistency = ON master_info_repository = TABLE relay_log_info_repository = TABLE #super_read_only = ON binlog_transaction_dependency_tracking = WRITESET transaction_write_set_extraction = XXHASH64 # Multi-threaded Replication slave_parallel_type = LOGICAL_CLOCK slave_preserve_commit_order = ON slave_parallel_workers = 4 # Group Replication Settings #plugin_load_add = "group_replication.so" #loose_group_replication_group_name = "22e34e6b-f37e-11ed-a2a8-000c29f6d187" #loose_group_replication_start_on_boot = OFF #loose_group_replication_local_address = "172.31.7.111:33061" #loose_group_replication_group_seeds = "172.31.7.110:33061,172.31.7.111:33061,172.31.7.112:33061" #loose_group_replication_bootstrap_group = OFF #loose_group_replication_recovery_get_public_key = ON root@db03:~# cat /etc/my.cnf [client] socket = /data/mysql3306/data/mysql.sock [mysqld] # Server user = mysql datadir = /data/mysql3306/data #basedir = /usr/local/mysql basedir = /opt/mysql-8.0.36 port = 3306 socket = /data/mysql3306/data/mysql.sock log_timestamps = system log_error = /data/mysql3306/data/mysqld.err skip_name_resolve report_host = "172.31.7.112" disabled_storage_engines = "MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY" sql_require_primary_key = ON # Replication server_id = 3 log_bin = mysql-bin binlog_format = ROW log_slave_updates = ON gtid_mode = ON enforce_gtid_consistency = ON master_info_repository = TABLE relay_log_info_repository = TABLE #super_read_only = ON binlog_transaction_dependency_tracking = WRITESET transaction_write_set_extraction = XXHASH64 # Multi-threaded Replication slave_parallel_type = LOGICAL_CLOCK slave_preserve_commit_order = ON slave_parallel_workers = 4 # Group Replication Settings #plugin_load_add = "group_replication.so" #loose_group_replication_group_name = "22e34e6b-f37e-11ed-a2a8-000c29f6d187" #loose_group_replication_start_on_boot = OFF #loose_group_replication_local_address = "172.31.7.112:33061" #loose_group_replication_group_seeds = "172.31.7.110:33061,172.31.7.111:33061,172.31.7.112:33061" #loose_group_replication_bootstrap_group = OFF #loose_group_replication_recovery_get_public_key = ON ``` ### 13.3.2 创建超级管理员账号 ⽅便起⻅,这⾥直接创建可远程登录的 root 账号,三个节点都要创建。 ```bash set session sql_log_bin=0; create user root@'%' identified by '123456'; grant all on *.* to root@'%' with grant option; set session sql_log_bin=1; ``` ### 13.3.3 配置实例 三个节点均要执⾏,⾸先配置 node1 ```sql root@db01:~# mysqlsh Please provide the password for 'root@/data%2Fmysql3306%2Fdata%2Fmysql.sock': Save password for 'root@/data%2Fmysql3306%2Fdata%2Fmysql.sock'? [Y]es/[N]o/Ne[v]er (default No): MySQL Shell 8.0.36 Copyright (c) 2016, 2023, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type '\help' or '\?' for help; '\quit' to exit. Creating a Classic session to 'root@/data%2Fmysql3306%2Fdata%2Fmysql.sock' Fetching schema names for auto-completion... Press ^C to stop. Your MySQL connection id is 9 Server version: 8.0.36 MySQL Community Server - GPL No default schema selected; type \use <schema> to set one. MySQL localhost JS > dba.configureInstance('root:123456@172.31.7.110:3306', { clusterAdmin:'cluster_admin', clusterAdminPassword: 'cluster_pass' }) Configuring local MySQL instance listening at port 3306 for use in an InnoDB cluster... This instance reports its own address as 172.31.7.110:3306 Assuming full account name 'cluster_admin'@'%' for cluster_admin applierWorkerThreads will be set to the default value of 4. The instance '172.31.7.110:3306' is valid to be used in an InnoDB cluster. Creating user cluster_admin@%. Account cluster_admin@% was successfully created. The instance '172.31.7.110:3306' is already ready to be used in an InnoDB cluster. Successfully enabled parallel appliers. ## 快捷命令 ################################################################################## dba.configureInstance('root:123456@172.31.7.110:3306', { clusterAdmin:'cluster_admin', clusterAdminPassword: 'cluster_pass' }) ``` 命令中的 clusterAdmin 和 clusterAdminPassword ⽤来指定集群的管理账号及密码。 ```sql MySQL localhost JS > dba.configureInstance('root:123456@172.31.7.111:3306', { clusterAdmin:'cluster_admin', clusterAdminPassword: 'cluster_pass' }) Configuring MySQL instance at 172.31.7.111:3306 for use in an InnoDB cluster... This instance reports its own address as 172.31.7.111:3306 Assuming full account name 'cluster_admin'@'%' for cluster_admin applierWorkerThreads will be set to the default value of 4. The instance '172.31.7.111:3306' is valid to be used in an InnoDB cluster. Creating user cluster_admin@%. Account cluster_admin@% was successfully created. The instance '172.31.7.111:3306' is already ready to be used in an InnoDB cluster. Successfully enabled parallel appliers. MySQL localhost JS > dba.configureInstance('root:123456@172.31.7.112:3306', { clusterAdmin:'cluster_admin', clusterAdminPassword: 'cluster_pass' }) Configuring MySQL instance at 172.31.7.112:3306 for use in an InnoDB cluster... This instance reports its own address as 172.31.7.112:3306 Assuming full account name 'cluster_admin'@'%' for cluster_admin applierWorkerThreads will be set to the default value of 4. The instance '172.31.7.112:3306' is valid to be used in an InnoDB cluster. Creating user cluster_admin@%. Account cluster_admin@% was successfully created. The instance '172.31.7.112:3306' is already ready to be used in an InnoDB cluster. Successfully enabled parallel appliers. ## 快捷命令 ################################################################################## dba.configureInstance('root:123456@172.31.7.111:3306', { clusterAdmin:'cluster_admin', clusterAdminPassword: 'cluster_pass' }) dba.configureInstance('root:123456@172.31.7.112:3306', { clusterAdmin:'cluster_admin', clusterAdminPassword: 'cluster_pass' }) ``` ### 13.3.4 创建 InnoDB Cluster 因为 node1 是 Primary 节点,这⾥登录 node1 执⾏创建操作。 ```bash MySQL localhost JS > shell.connect('cluster_admin:cluster_pass@172.31.7.110:3306') Creating a session to 'cluster_admin@172.31.7.110:3306' Fetching schema names for auto-completion... Press ^C to stop. Closing old connection... Your MySQL connection id is 12 Server version: 8.0.36 MySQL Community Server - GPL No default schema selected; type \use <schema> to set one. <ClassicSession:cluster_admin@172.31.7.110:3306> MySQL 172.31.7.110:3306 ssl JS > dba.createCluster('myCluster', { disableClone:false }) A new InnoDB Cluster will be created on instance '172.31.7.110:3306'. Validating instance configuration at 172.31.7.110:3306... This instance reports its own address as 172.31.7.110:3306 Instance configuration is suitable. NOTE: Group Replication will communicate with other members using '172.31.7.110:3306'. Use the localAddress option to override. * Checking connectivity and SSL configuration... Creating InnoDB Cluster 'myCluster' on '172.31.7.110:3306'... Adding Seed Instance... Cluster successfully created. Use Cluster.addInstance() to add MySQL instances. At least 3 instances are needed for the cluster to be able to withstand up to one server failure. <Cluster:myCluster> ## 快捷命令 ################################################################################## shell.connect('cluster_admin:cluster_pass@172.31.7.110:3306') dba.createCluster('myCluster', { disableClone:false }) ``` ### 13.3.5 添加节点 添加节点依赖于 Cluster 对象,在执⾏添加操作之前,先通过 dba.getCluster 命令获取⼀个 Cluster 对象。 ```bash MySQL 172.31.7.110:3306 ssl JS > cluster=dba.getCluster('myCluster') <Cluster:myCluster> ``` 接下来添加 node2。 ```bash MySQL 172.31.7.110:3306 ssl JS > cluster.addInstance('cluster_admin:cluster_pass@172.31.7.111:3306') NOTE: The target instance '172.31.7.111:3306' has not been pre-provisioned (GTID set is empty). The Shell is unable to decide whether incremental state recovery can correctly provision it. The safest and most convenient way to provision a new instance is through automatic clone provisioning, which will completely overwrite the state of '172.31.7.111:3306' with a physical snapshot from an existing cluster member. To use this method by default, set the 'recoveryMethod' option to 'clone'. The incremental state recovery may be safely used if you are sure all updates ever executed in the cluster were done with GTIDs enabled, there are no purged transactions and the new instance contains the same GTID set as the cluster or a subset of it. To use this method by default, set the 'recoveryMethod' option to 'incremental'. Please select a recovery method [C]lone/[I]ncremental recovery/[A]bort (default Clone): Validating instance configuration at 172.31.7.111:3306... This instance reports its own address as 172.31.7.111:3306 Instance configuration is suitable. NOTE: Group Replication will communicate with other members using '172.31.7.111:3306'. Use the localAddress option to override. * Checking connectivity and SSL configuration... A new instance will be added to the InnoDB Cluster. Depending on the amount of data on the cluster this might take from a few seconds to several hours. Adding instance to the cluster... Monitoring recovery process of the new cluster member. Press ^C to stop monitoring and let it continue in background. Clone based state recovery is now in progress. NOTE: A server restart is expected to happen as part of the clone process. If the server does not support the RESTART command or does not come back after a while, you may need to manually start it back. * Waiting for clone to finish... NOTE: 172.31.7.111:3306 is being cloned from 172.31.7.110:3306 ** Stage DROP DATA: Completed ** Clone Transfer FILE COPY ############################################################ 100% Completed PAGE COPY ############################################################ 100% Completed REDO COPY ############################################################ 100% Completed NOTE: 172.31.7.111:3306 is shutting down... * Waiting for server restart... ready * 172.31.7.111:3306 has restarted, waiting for clone to finish... ** Stage RESTART: Completed * Clone process has finished: 74.70 MB transferred in about 1 second (~74.70 MB/s) State recovery already finished for '172.31.7.111:3306' The instance '172.31.7.111:3306' was successfully added to the cluster. ## 快捷命令 ################################################################################## cluster.addInstance('cluster_admin:cluster_pass@172.31.7.111:3306') ``` 接下来添加 node3 。 ```bash MySQL 172.31.7.110:3306 ssl JS > cluster.addInstance('cluster_admin:cluster_pass@172.31.7.112:3306') ## 快捷命令 ################################################################################## cluster.addInstance('cluster_admin:cluster_pass@172.31.7.112:3306') ``` 移除节点案例: ```bash MySQL 172.31.7.110:3306 ssl JS > cluster.removeInstance('cluster_admin:cluster_pass@172.31.7.111:3306') The instance will be removed from the InnoDB Cluster. * Waiting for instance '172.31.7.111:3306' to synchronize with the primary... ** Transactions replicated ############################################################ 100% * Instance '172.31.7.111:3306' is attempting to leave the cluster... The instance '172.31.7.111:3306' was successfully removed from the cluster. ``` ### 13.3.6 查看Cluster的状态 ```bash MySQL 172.31.7.110:3306 ssl JS > cluster.status() { "clusterName": "myCluster", "defaultReplicaSet": { "name": "default", "primary": "172.31.7.110:3306", "ssl": "REQUIRED", "status": "OK", "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", "topology": { "172.31.7.110:3306": { "address": "172.31.7.110:3306", "memberRole": "PRIMARY", "mode": "R/W", "readReplicas": {}, "replicationLag": "applier_queue_applied", "role": "HA", "status": "ONLINE", "version": "8.0.36" }, "172.31.7.111:3306": { "address": "172.31.7.111:3306", "memberRole": "SECONDARY", "mode": "R/O", "readReplicas": {}, "replicationLag": "applier_queue_applied", "role": "HA", "status": "ONLINE", "version": "8.0.36" }, "172.31.7.112:3306": { "address": "172.31.7.112:3306", "memberRole": "SECONDARY", "mode": "R/O", "readReplicas": {}, "replicationLag": "applier_queue_applied", "role": "HA", "status": "ONLINE", "version": "8.0.36" } }, "topologyMode": "Single-Primary" }, "groupInformationSourceMember": "172.31.7.110:3306" } MySQL 172.31.7.110:3306 ssl JS > ``` ### 13.3.7 部署 MySQL Router 1. 应⽤向 MySQL Router 发起连接。 2. MySQL Router 检查并选择⼀个可⽤的后端节点(MySQL Server)。 3. MySQL Router 与这个后端节点建⽴连接。 4. MySQL Router 来回转发应⽤与后端节点之间的数据包。 5. 如果后端节点出现问题,MySQL Router 会断开应⽤端的连接。应⽤端重试后,MySQL Router 会选择另外 ⼀个可⽤节点重复上述流程。 **下载 MySQL Router** ```bash cd /usr/local/ wget https://dev.mysql.com/get/Downloads/MySQL-Router/mysql-router-8.0.36-linux-glibc2.12-x86_64.tar.xz tar xvf mysql-router-8.0.36-linux-glibc2.12-x86_64.tar.xz ln -s mysql-router-8.0.36-linux-glibc2.12-x86_64 mysql-router export PATH=$PATH:/usr/local/mysql-router/bin ``` 基于刚刚创建的 Cluster 初始化 MySQL Router 。 ```bash root@Router:/usr/local# mkdir -p /data/myrouter/6446 root@Router:/usr/local# mysqlrouter --bootstrap cluster_admin@172.31.7.110:3306 --user=mysql --directory /data/myrouter/6446 --conf-use-sockets --report-host='172.31.7.66' Please enter MySQL password for cluster_admin: # Bootstrapping MySQL Router 8.0.36 (MySQL Community - GPL) instance at '/data/myrouter/6446'... - Creating account(s) (only those that are needed, if any) - Verifying account (using it to run SQL queries that would be run by Router) - Storing account in keyring - Adjusting permissions of generated files - Creating configuration /data/myrouter/6446/mysqlrouter.conf # MySQL Router configured for the InnoDB Cluster 'myCluster' After this MySQL Router has been started with the generated configuration $ mysqlrouter -c /data/myrouter/6446/mysqlrouter.conf InnoDB Cluster 'myCluster' can be reached by connecting to: ## MySQL Classic protocol - Read/Write Connections: 172.31.7.66:6446, /data/myrouter/6446/mysql.sock - Read/Only Connections: 172.31.7.66:6447, /data/myrouter/6446/mysqlro.sock ## MySQL X protocol - Read/Write Connections: 172.31.7.66:6448, /data/myrouter/6446/mysqlx.sock - Read/Only Connections: 172.31.7.66:6449, /data/myrouter/6446/mysqlxro.sock ``` 接下来,启动 MySQL Router。 ```bash root@Router:/usr/local# sh /data/myrouter/6446/start.sh ``` 除了脚本,也可通过 mysqlrouter 命令直接启动。 ```bash # mysqlrouter -c /data/myrouter/6446/mysqlrouter.conf --user=mysql & ``` 默认会开启五个监听端⼝: ```bash root@Router:/usr/local# netstat -lntup | grep mysqlrouter tcp 0 0 0.0.0.0:8443 0.0.0.0:* LISTEN 2166/mysqlrouter tcp 0 0 0.0.0.0:6446 0.0.0.0:* LISTEN 2166/mysqlrouter tcp 0 0 0.0.0.0:6447 0.0.0.0:* LISTEN 2166/mysqlrouter tcp 0 0 0.0.0.0:6448 0.0.0.0:* LISTEN 2166/mysqlrouter tcp 0 0 0.0.0.0:6449 0.0.0.0:* LISTEN 2166/mysqlrouter ``` 其中, - 6446:对应 Classic 协议的读写操作。 - 6447 :对应 Classic 协议的只读操作。 - 6448 :对应 X 协议的读写操作。 - 6449: 对应 X 协议的只读操作。 - 8443 :提供 REST API 的 HTTP 端⼝。 在初始化的过程中,MySQL Router 的元数据信息会更新到 InnoDB Cluster 中。 Cluster 相关的 Router 信息可通过 cluster.listRouters() 命令查看。 ```bash MySQL 172.31.7.110:3306 ssl JS > cluster.listRouters() { "clusterName": "myCluster", "routers": { "172.31.7.66::": { "hostname": "172.31.7.66", "lastCheckIn": "2024-05-13 21:27:58", "roPort": "6447", "roXPort": "6449", "rwPort": "6446", "rwXPort": "6448", "version": "8.0.36" } } } ``` ⾄此,InnoDB Cluster 搭建完毕。 ### 13.3.8 ⾼可⽤测试 测试脚本 ```python # vim check_mysql_connection.py import pymysql import time import sys hostname = '172.31.7.66' username = 'root' password = '123456' database = 'information_schema' def check_mysql(hostname, port, username, password, database, mode): try: conn = pymysql.connect( host=hostname, port=port, user=username, password=password, database=database ) cursor = conn.cursor() cursor.execute("SELECT @@hostname") result = cursor.fetchone() print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 当前节点: {result[0]}") cursor.close() conn.close() return True except pymysql.Error as e: print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 操作失败:", e) return False if len(sys.argv) > 1: mode = sys.argv[1] if mode == 'rw': port = 6446 else: port = 6447 else: print("请提供命令⾏参数 mode (rw/ro)") sys.exit(1) while True: check_mysql(hostname, port, username, password, database, mode) time.sleep(1) ``` - 测试 ```bash root@Router:~# python3 check_mysql_connection.py rw [2024-05-13 21:56:10] 当前节点: db01.hosts.com [2024-05-13 21:56:11] 当前节点: db01.hosts.com [2024-05-13 21:56:13] 当前节点: db01.hosts.com [2024-05-13 21:56:14] 当前节点: db01.hosts.com ^CTraceback (most recent call last): File "/root/check_mysql_connection.py", line 46, in <module> time.sleep(1) KeyboardInterrupt root@Router:~# python3 check_mysql_connection.py ro [2024-05-13 21:56:17] 当前节点: db02.hosts.com [2024-05-13 21:56:18] 当前节点: db03.hosts.com [2024-05-13 21:56:19] 当前节点: db02.hosts.com [2024-05-13 21:56:20] 当前节点: db03.hosts.com [2024-05-13 21:56:21] 当前节点: db02.hosts.com [2024-05-13 21:56:22] 当前节点: db03.hosts.com # 关闭node01(db01) root@Router:~# python3 check_mysql_connection.py rw [2024-05-13 21:57:42] 当前节点: db01.hosts.com [2024-05-13 21:57:43] 当前节点: db01.hosts.com [2024-05-13 21:57:44] 当前节点: db01.hosts.com [2024-05-13 21:57:45] 当前节点: db01.hosts.com [2024-05-13 21:57:46] 当前节点: db01.hosts.com [2024-05-13 21:57:47] 当前节点: db01.hosts.com [2024-05-13 21:57:51] 操作失败: (2013, 'Lost connection to MySQL server during query') [2024-05-13 21:57:52] 当前节点: db02.hosts.com [2024-05-13 21:57:53] 当前节点: db02.hosts.com [2024-05-13 21:57:54] 当前节点: db02.hosts.com [2024-05-13 21:57:55] 当前节点: db02.hosts.com [2024-05-13 21:57:56] 当前节点: db02.hosts.com [2024-05-13 21:57:57] 当前节点: db02.hosts.com [2024-05-13 21:57:58] 当前节点: db02.hosts.com ``` - 查看集群状态 ```bash MySQL 172.31.7.112:3306 ssl JS > shell.connect('cluster_admin:cluster_pass@172.31.7.110:3306') Creating a session to 'cluster_admin@172.31.7.110:3306' Fetching schema names for auto-completion... Press ^C to stop. Closing old connection... Your MySQL connection id is 103 Server version: 8.0.36 MySQL Community Server - GPL No default schema selected; type \use <schema> to set one. <ClassicSession:cluster_admin@172.31.7.110:3306> MySQL 172.31.7.110:3306 ssl JS > cluster=dba.getCluster('myCluster') <Cluster:myCluster> MySQL 172.31.7.110:3306 ssl JS > cluster.status() { "clusterName": "myCluster", "defaultReplicaSet": { "name": "default", "primary": "172.31.7.111:3306", "ssl": "REQUIRED", "status": "OK", "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", "topology": { "172.31.7.110:3306": { "address": "172.31.7.110:3306", "memberRole": "SECONDARY", "mode": "R/O", "readReplicas": {}, "replicationLag": "applier_queue_applied", "role": "HA", "status": "ONLINE", "version": "8.0.36" }, "172.31.7.111:3306": { "address": "172.31.7.111:3306", "memberRole": "PRIMARY", "mode": "R/W", "readReplicas": {}, "replicationLag": "applier_queue_applied", "role": "HA", "status": "ONLINE", "version": "8.0.36" }, "172.31.7.112:3306": { "address": "172.31.7.112:3306", "memberRole": "SECONDARY", "mode": "R/O", "readReplicas": {}, "replicationLag": "applier_queue_applied", "role": "HA", "status": "ONLINE", "version": "8.0.36" } }, "topologyMode": "Single-Primary" }, "groupInformationSourceMember": "172.31.7.111:3306" } MySQL 172.31.7.110:3306 ssl JS > ######################################################## shell.connect('cluster_admin:cluster_pass@172.31.7.110:3306') # 链接 cluster=dba.getCluster('myCluster') #获取集群 cluster.status() # 查看集群状态 ```
李延召
2024年5月14日 14:43
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码