MySQL Group Replication

Stone大约 64 分钟

MySQL Group Replication

注意:

此文档对应的 MySQL 版本为 8.0.32 社区版。

MySQL Replicationopen in new window 中,介绍了创建复制环境实现数据和服务的高可用。本章节介绍 MySQL 组复制(Group Replication)以及如何安装,配置和监控组复制,以实现弹性的,高可用的,容错的复制环境。

Group Replication Background

Replication Technologies

本节介绍组复制的基本概念和工作原理,以及异步复制open in new window和组复制之间的区别。

Source to Replica Replication

传统的 MySQL 异步复制提供了一种简单的源库到副本库的复制方法,源库异步将二进制日志发送到副本库,不会等待副本库的接收确认。

MySQL Asynchronous Replication

半同步复制open in new window则是添加了一个同步步骤,源库需要等待任一副本库确认收到事务后才会恢复提交操作。

MySQL Semisynchronous Replication

Group Replication

MySQL 组复制是一种基于 Paxos 算法的复制技术,可以实现多个 MySQL 实例之间的数据同步和高可用性。

组复制的核心原理是使用 Paxos 协议来协调不同 MySQL 实例之间的数据同步和决策制定。Paxos 协议是一种分布式算法,它能够确保不同节点之间达成共识,并保证了数据一致性和可靠性。

组复制由多个 MySQL Server 组成,组中的每个成员可以在任何时候独立执行事务。但是,所有读写事务(不包含只读事务)只有在得到组的批准之后才能提交(通过冲突认证检测之后才能提交)。换句话说,对于任何读写事务,都是由组来决定它是否可以提交,而不是由发起事务提交的原始组成员单方面决定。如果是只读事务,则不需要在组内进行协调,可以立即提交(即,发起事务的组成员可以单方面决定)。

当读写事务准备在原始组成员上提交时,该成员将自动广播写入值(数据变更的行)和相应的 Write Set(发生数据变更的行的惟一标识符)。因为事务是通过原子广播发送的,所以组中的所有成员要么都接收事务,要么都不接收事务。如果组中的所有成员都收到了该广播消息(事务),那么它们都会按照与之前发送事务的相同顺序收到该广播消息。因此,所有组成员都以相同的顺序接收相同的事务集,并为这些事务建立全局顺序。

但是在不同组成员上并发执行的事务可能存在冲突。这种冲突是通过检查和比较两个并发事务的 Write Set 来验证的,这个过程称为认证(Certification)。在认证期间,冲突检测在行级别执行:如果在不同组成员上执行的两个并发事务更新了同一行数据,则存在冲突。根据冲突认证检测机制判断,按照顺序,第一次提交到所有组成员上的事务继续执行提交(成功),第二次提交到所有组成员上的事务被中止(失败),因此第二个事务在事务发起的原始组成员上执行回滚,组中的其他成员对该事务执行删除。

对于一些事务,如果不破坏数据的一致性和有效性,则组复制允许组成员偏离商定的事务顺序,组复制是一个最终一致性的系统,这意味着只要通过了冲突认证检测的事务,不要求在所有组成员的事务提交顺序都完全一致,只需要保证组复制内的所有成员最终具有相同的数据内容即可。例如:在多主模式下,本地事务可能会在冲突认证通过之后立即提交(即使全局序列中存在着更早的还未应用的远程事务也是如此 ,但是要注意,只允许通过了冲突认证检测的事务先提交,冲突认证不通过的本地事务会被回滚),在单主模式下,在写节点上,并发的且通过了事务冲突认证的事务可能以与组同意的且与全局顺序不相同的顺序提交(但在读节点上,事务总是以全局顺序提交事务)。

MySQL Group Replication Protocol

Group Replication Use Cases

MySQL 组复制提供了高可用、高弹性及可靠的 MySQL 服务。

可以用于:

  • 弹性复制
  • 高可用分片
  • 替代异步复制
  • 自动化部署

Multi-Primary and Single-Primary Modes

组复制可以运行在单主模式或者多主模式,使用参数 group_replication_single_primary_mode 指定,所有组成员需要保持一致。默认为 ON,表示使用单主模式,设置为 OFF,表示使用多主模式。

在 MySQL 8.0.13 之前,不能在组复制运行时修改参数 group_replication_single_primary_mode 值。从 MySQL 8.0.13 开始,可以在组复制运行时,使用 group_replication_switch_to_single_primary_mode()group_replication_switch_to_multi_primary_mode() 函数修改组复制运行模式。

Single-Primary Mode

单主模式(group_replication_single_primary_mode=ON)时,整个组只有一个读写模式的主库,其他组成员为只读模式(super_read_only=ON)。

单主模式需要设置参数 group_replication_enforce_update_everywhere_checksOFF 禁用组的一致性检查。

更改主库的情况有:

  • 现有主库失联,自动选择新主库。
  • 使用 group_replication_set_as_primary() 函数指定组成员为新主库。
  • 使用 group_replication_switch_to_single_primary_mode() 函数从多主模式修改为单主模式时,可以指定新主库,也可以自动选择新主库。

New Primary Election

Primary Election Algorithm

自动选择主库时考虑的因素依次如下:

  1. 版本,优先选择最低版本为主库,先比较大版本,再比较小版本。
  2. 权重,版本相同时,参数 group_replication_member_weight 指定的权重值最大为主库,默认为 50,范围为 0 到 100。值越大权重越高。
  3. UUID,版本和权重相同时,参数 server_uuid 指定的 UUID 最小为主库。
Finding the Primary

查询 performance_schema.replication_group_members 表,获取组成员及角色。

mysql> SELECT MEMBER_HOST, MEMBER_ROLE FROM performance_schema.replication_group_members;
+-------------------------+-------------+
| MEMBER_HOST             | MEMBER_ROLE |
+-------------------------+-------------+
| remote1.example.com     | PRIMARY     |
| remote2.example.com     | SECONDARY   |
| remote3.example.com     | SECONDARY   |
+-------------------------+-------------+

Multi-Primary Mode

多主模式(group_replication_single_primary_mode=OFF)时,所有组成员角色相同,都为读写模式。

Client Failover

组复制是一个最终一致性系统,也就是说虽然在某个时刻组成员之间有可能数据不一致,但随着时间的推移,各个组成员之间的数据会最终一致的。

从 MySQL 8.0.14 开始,可以使用参数 group_replication_consistency 为组中的每个事务提供事务一致性保证。

Transaction Checks

多主模式时,将执行以下一致性检查:

  • 如果事务在 SERIALIZABLE 隔离级别下执行,则提交失败。
  • 如果事务针对级联外键约束的表,则提交失败。

使用参数 group_replication_enforce_update_everywhere_checks 指定是否进行检查。在多主模式中,通常应设置为 ON,但可以将其设置为 OFF 来选择性地停用检查。在单主模式中,必须设置为 OFF

Data Definition Statements

多主模式时,如果对同一对象进行 DDL 和 DML 操作,则应在同一主机上处理,避免数据不一致或者操作失败。

Version Compatibility

所有组复制成员的 MySQL 版本应一致。

Group Replication Services

本节介绍组复制相关服务。

Group Membership

MySQL 组复制包含一组服务器,组成一个复制组,其名称采用 UUID 的形式。可以随时加入或者离开(自愿或非自愿)复制组,如果加入组,则会从现有成员中自动获取缺失的数据状态以便和组保持数据同步,如果离开(自愿或非自愿)组,例如例行维护时关闭了某个组成员,其余组成员会发现该成员离开组,并自动重新配置组。

组复制使用组成员身份服务(Group Membership Service)定义哪些服务器处于联机状态并参与组。联机服务器列表被称为组视图(View)。组成员不仅必须就事务提交达成一致,而且还必须就当前组视图达成一致。如果现有成员同意新服务器成为组的一部分,则会重新配置组以将该服务器加入到组中,从而触发组视图更改。如果服务器离开(自愿或非自愿)组,该组将动态重新配置并触发组视图更改。

成员自愿离开组时,首先启动动态组重新配置,在此期间,所有剩余组成员必须就新的组视图达成一致。成员非自愿离开组时(例如,由于意外宕机或网络连接中断),那么离开组的成员就不能启动动态重新配置。在这种情况下,组复制的故障检测机制会在短时间内识别出该成员已经离开组,并建议将已离开组成员排除在外并重新配置组。与自愿离开的成员一样,重新配置需要组中大多数服务器同意。但是如果此时组内不能达成一致,例如由于组内活跃节点数量少于节点总数量的半数时,系统就不能动态重新配置组,以防止出现裂脑情况。

在故障检测机制检测到其故障之前,或在重新配置组以删除该故障成员之前,允许组成员短暂离开组,然后尝试重新加入组。在这种情况下,重新加入的成员可能会丢失它以前的状态,如果此时其他成员向它发送了包含该成员崩溃前的状态消息,则有可能会导致数据不一致。为了应对这种情况,从 MySQL 5.7.22 和 MySQL 8.0 开始,组复制会检查同一服务器的旧化身(具有相同的地址和端口号)仍列为组成员时,其新化身尝试加入组的情况,直到可以通过重新配置删除旧化身才会将新化身加入组。如果通过参数 group_replication_member_expel_timeout 增加了成员在被驱逐出组之前允许返回组的等待时间,则成员在超时前都可以以当前化身重新加入组。如果组成员超时被驱逐或使用 STOP GROUP_REPLICATION 语句停止了组复制,或者服务器宕机, 则必须以新化身加入组。

Failure Detection

组复制的故障检测机制是一种分布式服务,能够识别组中无响应的服务器。

如果一个成员在 5 秒内未收到来自另一个成员的消息,则怀疑该成员已失败,并在其 performance_schema.replication_group_members 表中该成员的状态列为 UNREACHABLE

如果怀疑持续超过 10 秒,仅当怀疑成员为通知者时,才会尝试将其可疑成员有错误的组视图传播给组中的其他成员。

如果网络不稳定,成员经常以不同的组合失去和重新获得彼此的联系,理论上一个组有可能最终将其所有成员标记为驱逐,之后该组将不复存在,必须重新建立。为了应对这种可能性,从 MySQL 8.0.20 开始,组复制的组通信系统(Group Communication System,GCS)跟踪已被标记为驱逐的组成员,并决定是否将他们视为可疑成员组。这可确保至少有一个成员保留在组中,并且该组可以继续存在。当被驱逐的成员真正被从组中删除时,GCS 将删除其已将该成员标记为驱逐的记录,以便该成员可以在可能的情况下重新加入该组。

Fault-tolerance

组复制使用 Paxos 分布式算法提供组成员之间的分布式协调。因此需要大多数组成员处于活动状态才能达到仲裁成员数,从而做出决策。该要求会直接影响到系统在不影响自身和整体可用性的情况下能够容忍发生故障的成员数量。可以容忍发生故障的成员数量(假设为 f 个)和组内总成员数量(假设为 n 个,最大为 9)之间的关系为:n = 2 x f + 1

根据上述公式可知,如果要容忍 1 个组成员发生故障,那么组内的总成员数量至少为 3 个。因为当 1 个组成员发生故障时,仍然有 2 个组成员是活跃成员,即活跃成员占多数(三分之二),此时,组内可通过仲裁机制自动驱逐故障成员并允许系统继续对外提供服务。但是如果第 2 个组成员再发生故障(非自愿离开组),则该组(剩下 1 个成员)会发生阻塞,因为缺少多数成员来做出合理的决策,所以无法自动恢复到能对外提供服务的状态,此时需要人工介入处理。

Group SizeMajorityInstant Failures Tolerated
110
220
321
431
532
642
743

Observability

可以查询性能模式(Performance Schema)表获取组复制信息。

Group Replication Plugin Architecture

MySQL 组复制是一个 MySQL 插件,建立在现有的 MySQL 复制基础架构之上,使用二进制日志open in new window基于行的二进制日志格式open in new window全局事务标识符open in new window(GTID)等功能。MySQL 组复制的整体体系结构图如下:

Group Replication Plugin Block Diagram

MySQL 组复制插件最上层包含一组用于捕获(Capture)、应用(Apply)和生命周期(Lifecycle)的 API,用于控制插件与 MySQL 服务器的交互方式。将类似服务器启动、服务器恢复、服务器准备接受连接以及服务器即将提交事务这些事件通知从 MySQL Server 发送给组复制插件;组复制插件通知 MySQL Server 执行类似于提交或中止正在进行的事务,或将事务写入中继日志中排队等候处理。

组复制插件下一层包括:

  • 捕获(Capture)组件:负责跟踪与正在执行的事务相关的上下文。
  • 应用(Apply)组件:负责在数据库上执行远程事务。
  • 恢复(Recovery)组件:管理分布式恢复,

下一层的复制协议模块包含复制协议中的一些特定逻辑。例如:处理冲突检测,并接收和传播事务到组中。

最后两层是组通信系统(Group Communication System GCS)API 和基于 Paxos 的组通信引擎(XCom)的实现。

Getting Started

MySQL 组复制通过插件实现,每个组成功都需要安装和配置插件。本节介绍如何创建至少包含 3 个组成员的复制组。

Deploying Group Replication in Single-Primary Mode

本节介绍如何创建一个复制组,包含 3 个 MySQL Server 实例,每个实例运行在不同的主机上。

Group Architecture

Deploying Instances for Group Replication

部署组复制的第一步是至少创建 3 个 MySQL Server 实例,分别位于 3 台主机上,主机名分别为 s1,s2,s3。

创建 MySQL Server 实例参考:Installing MySQL on Linuxopen in new window

Configuring an Instance for Group Replication

本节介绍组复制需要的 MySQL Server 实例配置。

Storage Engines

组复制必须使用 InnoDB 存储引擎,使用其他存储引擎,包括 MEMORY 存储引擎,都可能会导致错误。故需设置参数 disabled_storage_engines 禁用这些存储引擎:

disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
Replication Framework

组复制需要如下复制配置:

server_id=8135
gtid_mode=ON
enforce_gtid_consistency=ON

其中 3 台主机的 server_id 不同,分别为 8135,8136,8137。

在 MySQL 8.0.20 及之前,还需要以下设置:

binlog_checksum=NONE

此设置禁止事件校验和写入到二进制日志中,默认是 ENABLE。从 MySQL 8.0.21 开始,组复制支持二进制日志中存在校验和,并可以使用其来验证某些通道上事件的完整性,因此可以使用默认设置。

在 MySQL 8.0.3 之前,还需要以下设置:

log_bin=binlog
log_slave_updates=ON
binlog_format=ROW
master_info_repository=TABLE
relay_log_info_repository=TABLE
transaction_write_set_extraction=XXHASH64
Group Replication Settings

组复制配置如下:

plugin_load_add='group_replication.so'
group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
group_replication_start_on_boot=off
group_replication_local_address= "s1:33061"
group_replication_group_seeds= "s1:33061,s2:33061,s3:33061"
group_replication_bootstrap_group=off
group_replication_consistency='BEFORE_ON_PRIMARY_FAILOVER'
  • plugin-load-add:添加组复制插件,以便在 MySQL Server 启动时加载。
  • group_replication_group_name:指定需要创建或加入的组名,值必须是有效的 UUID,可以使用 SELECT UUID() 语句来生成。
  • group_replication_start_on_boot:指定为 OFF 表示在 MySQL Server 启动时不自动启动组复制操作,确保在只有在组复制配置完成后才启动。
  • group_replication_local_address:指定组成员与其他组成员进行内部通信时使用的地址和端口。如果地址使用主机名,则需要确保在所有主机上都可以解析,故建议在 /etc/hosts 中配置所有组成员的地址解析。端口推荐设置为 33061。
  • group_replication_group_seeds:指定种子成员的地址和端口,用于新成员建立到组的连接。通常包含所有组成员的 group_replication_local_address 的值,也可以是其子集。最佳实践是先启动引导成员并让其创建组,然后使其成为要加入的其余组成员的种子成员。
  • group_replication_bootstrap_group:指定插件是否引导组,默认为 OFF。最佳实践是在所有组成员的参数文件中将该参数设置为 OFF,任一成员启动后,调整该参数为 ON,用于第一次引导组或者重启组,然后再调整该参数为 OFF。不要在多个实例上同时配置该参数为 ON
  • group_replication_consistency:指定一致性级别。

最终 3 个 MySQL Server 实例的参数分别如下:

  • s1:
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
log_error=/var/log/mysqld.log
pid_file=/var/run/mysqld/mysqld.pid
default_time_zone='+8:00'

server_id=44135
gtid_mode=on
enforce_gtid_consistency=on
event_scheduler=OFF
binlog_format=row
binlog_cache_size=8M
binlog_expire_logs_seconds=432000
relay_log_recovery=1
relay_log=relay-binlog
relay_log_index=/data/mysql/relay-binlog.index

disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
plugin_load_add='group_replication.so'
group_replication_group_name="0ae16bd1-fd34-11ed-9710-000c29c21d3a"
group_replication_start_on_boot=off
group_replication_local_address="s1:33061"
group_replication_group_seeds="s1:33061,s2:33061,s3:33061"
group_replication_bootstrap_group=off
group_replication_consistency='BEFORE_ON_PRIMARY_FAILOVER'

skip_name_resolve=1
character_set_server=utf8mb4
innodb_file_per_table=1
innodb_redo_log_capacity=1G
innodb_max_undo_log_size=1G
innodb_buffer_pool_size=1G
innodb_buffer_pool_load_at_startup=1
innodb_buffer_pool_dump_at_shutdown=1
innodb_open_files=65535
innodb_temp_data_file_path=ibtmp1:12M:autoextend:max:1G
max_connections=5000
max_connect_errors=100000
thread_cache_size=512
slow_query_log=1
slow_query_log_file=/var/log/mysql.slow
long_query_time=10
log_short_format
explicit_defaults_for_timestamp=1
log_timestamps=SYSTEM
lower_case_table_names=1
local_infile=ON

sort_buffer_size=4M
join_buffer_size=4M
key_buffer_size=32M
read_buffer_size=8M
read_rnd_buffer_size=4M
bulk_insert_buffer_size=64M

[client]
socket=/data/mysql/mysql.sock
user=root
password=MyNewPass4!

[mysql]
prompt=[\\d]> \\
no-auto-rehash
  • s2:
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
log_error=/var/log/mysqld.log
pid_file=/var/run/mysqld/mysqld.pid
default_time_zone='+8:00'

server_id=44136
gtid_mode=on
enforce_gtid_consistency=on
event_scheduler=OFF
binlog_format=row
binlog_cache_size=8M
binlog_expire_logs_seconds=432000
relay_log_recovery=1
relay_log=relay-binlog
relay_log_index=/data/mysql/relay-binlog.index

disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
plugin_load_add='group_replication.so'
group_replication_group_name="0ae16bd1-fd34-11ed-9710-000c29c21d3a"
group_replication_start_on_boot=off
group_replication_local_address="s2:33061"
group_replication_group_seeds="s1:33061,s2:33061,s3:33061"
group_replication_bootstrap_group=off
group_replication_consistency='BEFORE_ON_PRIMARY_FAILOVER'

skip_name_resolve=1
character_set_server=utf8mb4
innodb_file_per_table=1
innodb_redo_log_capacity=1G
innodb_max_undo_log_size=1G
innodb_buffer_pool_size=1G
innodb_buffer_pool_load_at_startup=1
innodb_buffer_pool_dump_at_shutdown=1
innodb_open_files=65535
innodb_temp_data_file_path=ibtmp1:12M:autoextend:max:1G
max_connections=5000
max_connect_errors=100000
thread_cache_size=512
slow_query_log=1
slow_query_log_file=/var/log/mysql.slow
long_query_time=10
log_short_format
explicit_defaults_for_timestamp=1
log_timestamps=SYSTEM
lower_case_table_names=1
local_infile=ON

sort_buffer_size=4M
join_buffer_size=4M
key_buffer_size=32M
read_buffer_size=8M
read_rnd_buffer_size=4M
bulk_insert_buffer_size=64M

[client]
socket=/data/mysql/mysql.sock
user=root
password=MyNewPass4!

[mysql]
prompt=[\\d]> \\
no-auto-rehash

s3:

[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
log_error=/var/log/mysqld.log
pid_file=/var/run/mysqld/mysqld.pid
default_time_zone='+8:00'

server_id=44137
gtid_mode=on
enforce_gtid_consistency=on
event_scheduler=OFF
binlog_format=row
binlog_cache_size=8M
binlog_expire_logs_seconds=432000
relay_log_recovery=1
relay_log=relay-binlog
relay_log_index=/data/mysql/relay-binlog.index

disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
plugin_load_add='group_replication.so'
group_replication_group_name="0ae16bd1-fd34-11ed-9710-000c29c21d3a"
group_replication_start_on_boot=off
group_replication_local_address="s3:33061"
group_replication_group_seeds="s1:33061,s2:33061,s3:33061"
group_replication_bootstrap_group=off
group_replication_consistency='BEFORE_ON_PRIMARY_FAILOVER'

skip_name_resolve=1
character_set_server=utf8mb4
innodb_file_per_table=1
innodb_redo_log_capacity=1G
innodb_max_undo_log_size=1G
innodb_buffer_pool_size=1G
innodb_buffer_pool_load_at_startup=1
innodb_buffer_pool_dump_at_shutdown=1
innodb_open_files=65535
innodb_temp_data_file_path=ibtmp1:12M:autoextend:max:1G
max_connections=5000
max_connect_errors=100000
thread_cache_size=512
slow_query_log=1
slow_query_log_file=/var/log/mysql.slow
long_query_time=10
log_short_format
explicit_defaults_for_timestamp=1
log_timestamps=SYSTEM
lower_case_table_names=1
local_infile=ON

sort_buffer_size=4M
join_buffer_size=4M
key_buffer_size=32M
read_buffer_size=8M
read_rnd_buffer_size=4M
bulk_insert_buffer_size=64M

[client]
socket=/data/mysql/mysql.sock
user=root
password=MyNewPass4!

[mysql]
prompt=[\\d]> \\
no-auto-rehash

修改参数文件后,重启 3 台主机的 MySQL Server 实例:

  • s1:
[root@s1 ~]# systemctl restart mysqld.service
  • s2:
[root@s2 ~]# systemctl restart mysqld.service
  • s3:
[root@s3 ~]# systemctl restart mysqld.service

User Credentials For Distributed Recovery

当组成员加入组时,组复制使用分布式恢复进程进行同步。分布式恢复使用名为 group_replication_recovery 的复制通道将事务从源库传输到加入的成员,因此必须创建具有合适权限的复制用户以便组复制可以建立成员之间的复制通道。如果组成员支持使用远程克隆,也可以将复制用户用作克隆用户,为其分配合适权限。

每个组成员上的分布式恢复必须使用相同的复制用户。如果当前已经是复制环境,则只需要在源库创建复制用户即可,副本库会自动创建。如果不是复制环境,则需要先禁用二进制日志,再在每个组成员上手动创建用户,再重新启用二进制日志。

分别在所有组成员创建复制用户,步骤如下:

  1. 禁用二进制日志
[(none)]> SET SQL_LOG_BIN=0;
Query OK, 0 rows affected (0.00 sec)
  1. 创建用户并授权
[(none)]> CREATE USER 'mgr_user'@'%' IDENTIFIED WITH mysql_native_password BY 'pUrEv!2rAx';
Query OK, 0 rows affected (0.03 sec)

[(none)]> GRANT REPLICATION SLAVE ON *.* TO 'mgr_user'@'%';
Query OK, 0 rows affected (0.00 sec)

[(none)]> GRANT CONNECTION_ADMIN ON *.* TO 'mgr_user'@'%';
Query OK, 0 rows affected (0.01 sec)

[(none)]> GRANT BACKUP_ADMIN ON *.* TO 'mgr_user'@'%';
Query OK, 0 rows affected (0.01 sec)

[(none)]> GRANT GROUP_REPLICATION_STREAM ON *.* TO 'mgr_user'@'%';
Query OK, 0 rows affected (0.00 sec)

[(none)]> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)
  1. 启用二进制日志
[(none)]> SET SQL_LOG_BIN=1;
Query OK, 0 rows affected (0.00 sec)
  1. 为复制通道 group_replication_recovery 配置用户凭据。
[(none)]> CHANGE REPLICATION SOURCE TO SOURCE_USER='mgr_user', SOURCE_PASSWORD='pUrEv!2rAx' FOR CHANNEL 'group_replication_recovery'; 
Query OK, 0 rows affected, 2 warnings (0.02 sec)

Launching Group Replication

如果没有在参数文件中指定 plugin_load_add='group_replication.so,则需要手动安装组复制插件:

mysql> INSTALL PLUGIN group_replication SONAME 'group_replication.so';

使用 SHOW PLUGINS 查看插件是否安装成功:

[(none)]> SHOW PLUGINS;
+----------------------------------+----------+--------------------+----------------------+---------+
| Name                             | Status   | Type               | Library              | License |
+----------------------------------+----------+--------------------+----------------------+---------+
| binlog                           | ACTIVE   | STORAGE ENGINE     | NULL                 | GPL     |
...
| group_replication                | ACTIVE   | GROUP REPLICATION  | group_replication.so | GPL     |
+----------------------------------+----------+--------------------+----------------------+---------+
49 rows in set (0.00 sec)

Bootstrapping the Group

首次启动组的过程称为引导( Bootstrapping)。在主节点使用参数 group_replication_bootstrap_group 指定当前实例来引导组(可以理解为创建组),然后启动组复制。

[(none)]> SET GLOBAL group_replication_bootstrap_group=ON;
Query OK, 0 rows affected (0.00 sec)

[(none)]> START GROUP_REPLICATION;
Query OK, 0 rows affected (1.49 sec)

[(none)]> SET GLOBAL group_replication_bootstrap_group=OFF;
Query OK, 0 rows affected (0.00 sec)

启动组复制后,查询 performance_schema.replication_group_members 查看组成员:

[(none)]>  SELECT MEMBER_HOST,MEMBER_PORT,MEMBER_STATE,MEMBER_ROLE,MEMBER_VERSION,CHANNEL_NAME,MEMBER_ID FROM performance_schema.replication_group_members;
+-------------+-------------+--------------+-------------+----------------+---------------------------+--------------------------------------+
| MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | CHANNEL_NAME              | MEMBER_ID                            |
+-------------+-------------+--------------+-------------+----------------+---------------------------+--------------------------------------+
| s1          |        3306 | ONLINE       | PRIMARY     | 8.0.32         | group_replication_applier | f84c4937-eb10-11ed-a2ff-000c294b702c |
+-------------+-------------+--------------+-------------+----------------+---------------------------+--------------------------------------+
1 row in set (0.00 sec)

Adding Instances to the Group

此时组中只有 1 个成员 s1,通过增加主机来扩展组。

Adding a Second Instance

在主机 s2 上启动组复制加入到复制组中:

[(none)]> START GROUP_REPLICATION;
Query OK, 0 rows affected (1.88 sec)

由于此时组已经存在,故不再需要启用 group_replication_bootstrap_group 来引导组(创建组)。

再次查询 performance_schema.replication_group_members 查看组成员:

[(none)]> SELECT MEMBER_HOST,MEMBER_PORT,MEMBER_STATE,MEMBER_ROLE,MEMBER_VERSION, CHANNEL_NAME,MEMBER_ID FROM performance_schema.replication_group_members;
+-------------+-------------+--------------+-------------+----------------+---------------------------+--------------------------------------+
| MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | CHANNEL_NAME              | MEMBER_ID                            |
+-------------+-------------+--------------+-------------+----------------+---------------------------+--------------------------------------+
| s2          |        3306 | ONLINE       | SECONDARY   | 8.0.32         | group_replication_applier | 0023cb72-f7da-11ed-ac6c-000c2986525b |
| s1          |        3306 | ONLINE       | PRIMARY     | 8.0.32         | group_replication_applier | f84c4937-eb10-11ed-a2ff-000c294b702c |
+-------------+-------------+--------------+-------------+----------------+---------------------------+--------------------------------------+
2 rows in set (0.01 sec)
Adding Additional Instances

在主机 s3 上启动组复制加入到复制组中:

[(none)]> START GROUP_REPLICATION;
Query OK, 0 rows affected (1.88 sec)

由于此时组已经存在,故不再需要启用 group_replication_bootstrap_group 来引导组(创建组)。

再次查询 performance_schema.replication_group_members 查看组成员:

[(none)]> SELECT MEMBER_HOST,MEMBER_PORT,MEMBER_STATE,MEMBER_ROLE,MEMBER_VERSION, CHANNEL_NAME,MEMBER_ID FROM performance_schema.replication_group_members;
+-------------+-------------+--------------+-------------+----------------+---------------------------+--------------------------------------+
| MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | CHANNEL_NAME              | MEMBER_ID                            |
+-------------+-------------+--------------+-------------+----------------+---------------------------+--------------------------------------+
| s2          |        3306 | ONLINE       | SECONDARY   | 8.0.32         | group_replication_applier | 0023cb72-f7da-11ed-ac6c-000c2986525b |
| s1          |        3306 | ONLINE       | PRIMARY     | 8.0.32         | group_replication_applier | f84c4937-eb10-11ed-a2ff-000c294b702c |
| s3          |        3306 | ONLINE       | SECONDARY   | 8.0.32         | group_replication_applier | fe074cfd-ebe0-11ed-9b23-000c29c21d3a |
+-------------+-------------+--------------+-------------+----------------+---------------------------+--------------------------------------+
3 rows in set (0.01 sec)

Requirements and Limitations

本节列出并说明了组复制的要求和限制。

Group Replication Requirements

Infrastructure

  • InnoDB 存储引擎:数据必须存储在 InnoDB 存储引擎。
  • 主键:表必须有主键。
  • 网络性能:需要高带宽,低延迟网络。

Server Instance Configuration

组成员实例需要配置以下参数:

  • server_id:为 MySQL Server 配置一个唯一的 ID。
  • log_bin:从 MySQL 8.0 开始,默认启用二进制日志,无需设置。
  • log_replica_updates:从 MySQL 8.0 开始,默认副本库将从源库接收的更新写入到二进制日志,无需设置。
  • binlog_format:默认为 row,无需设置。
  • binlog_checksum:在 MySQL 8.0.20 及之前,需设置为 NONE,从 MySQL 8.0.21 开始,可以使用默认值,无需设置。
  • gtid_mode:需要设置为 ON,指定使用 GTID 复制。
  • enforce_gtid_consistency:需要设置为 ON,确保没有事务违反 GTID 一致性。
  • master_info_repositoryrelay_log_info_repository:从 MySQL 8.0 开始,默认为 TABLE,无需设置。从 MySQL 8.0.23 开始,这两个参数被废弃。
  • transaction_write_set_extraction:在 MySQL 8.0,使用默认值 XXHASH64,从 MySQL 8.0.26 开始,该参数被废弃。
  • default_table_encryption:所有组成员设置相同的值。
  • lower_case_table_names:所有组成员设置相同的值。对于 InnoDB 存储引擎,应该设置为 1
  • binlog_transaction_dependency_tracking:根据组的负载,设置为 WRITESET_SESSION 可以提高组成员性能。
  • replica_parallel_workers:从 MySQL 8.0.27 开始,默认设置为 4 个线程并行应用事务。
  • replica_preserve_commit_order:对于多线程并行应用事务,从 MySQL 8.0.27 开始,使用默认设置 ON,确保副本库中并行事务的提交顺序与源库一致。
  • replica_parallel_type:如果 replica_preserve_commit_order 设置为 ON,从 MySQL 8.0.27 开始,使用默认设置 LOGICAL_CLOCK,基于源库写入二进制日志的时间戳在副本库并行应用事务。
  • xa_detach_on_prepare:从 MySQL 8.0.29 开始引入,需要使用默认值 ON

Group Replication Limitations

组复制有以下限制:

  • --upgrade=MINIMAL option:使用 MINIMAL 选项进行升级后无法启动组复制。

  • Gap Locks:组复制的并发事务认证过程(检查和比较并发事务来检测冲突)不考虑间隙锁open in new window,因此在多主模式推荐使用 READ COMMITTED 事务隔离级别open in new window

  • Table Locks and Named Locks:认证过程不考虑表锁和命名锁。

  • Binary Log Checksums:在 MySQL 8.0.20 及之前,组复制不支持二进制日志中的校验和,必须配置 binlog_checksum=NONE,从 MySQL 8.0.21 开始,组复制支持二进制日志中存在校验和,并可以使用其来验证 group_replication_recovery 通道上事件的完整性,因此可以使用默认设置 binlog_checksum=CRC32

  • SERIALIZABLE Isolation Level:多主模式默认不支持 SERIALIZABLE 隔离级别。

  • Concurrent DDL versus DML Operations:当使用多主模式时,不支持在不同服务器上的相同对象执行并行的DDL 和 DML 语句。

  • Foreign Keys with Cascading Constraints:多主模式不支持级联外键约束,因为会导致未检测到的冲突和不一致的数据。推荐在多主模式下使用 group_replication_enforce_update_everywhere_checks=ON 避免未检测到的冲突,而单主模式则没有这个问题。

  • Multi-primary Mode Deadlock:当使用多主模式时,SELECT .. FOR UPDATE 语句会导致死锁。

  • Replication Filters:组复制不能使用全局的复制过滤器。但当组成员作为其他组外源库的副本库时,与组复制不相关的复制通道(除了 group_replication_appliergroup_replication_recovery )上可以指定复制过滤器。

  • Limit on Group Size:组成员最多为 9 个。

  • Limits on Transaction Size:在 Failure Detectionopen in new window 提到如果一个成员在 5 秒内未收到来自另一个成员的消息,则该成员会被认为已失败。那么对于大事务,如果没有在 5 秒内完成组成员之间的事务消息传输,则会被认为失败并被驱逐。为避免该问题,可以:

    • 使用参数 group_replication_member_expel_timeout 指定 5 秒后额外的容许时间,从 MySQL 8.0.21 开始,默认为 5 秒,最大为 3600 秒。
    • 如果可以,尽量限制事务的规模,避免大事务。
    • 使用参数 group_replication_transaction_size_limit 指定组复制允许的事务大小,在 MySQL 8.0,默认为 143 MB(150000000 字节),超过该值事务被回滚,根据需要进行调整。
    • 使用参数 group_replication_compression_threshold 指定何时进行压缩,默认为 1 MB(1000000 字节),事务消息超过此大小则自动被压缩。
    • 使用参数 group_replication_communication_max_message_size 指定何时进行分段,默认为 10 MB(10485760 字节),事务消息超过此大小则自动分段,对于压缩后的事务消息同样如此。

Monitoring Group Replication

组复制相关的性能模式表有:

  • replication_group_members
  • replication_group_member_stats
  • replication_group_communication_information
  • replication_connection_status
  • replication_applier_status

组复制创建的复制通道有:

  • group_replication_recovery
  • group_replication_applier

从 MySQL 8.0.21 开始,与组复制生命周期事件(错误除外)相关的消息被归类为系统消息,写入组成员的错误日志。

影响整个组的某些生命周期事件会记录在每个组成员上,其他事件仅记录在发生这些事件的组成员上。

Group Replication Server States

使用 performance_schema.replication_group_members 查看组成员状态。

[(none)]> 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 | 0023cb72-f7da-11ed-ac6c-000c2986525b | s2          |        3306 | ONLINE       | SECONDARY   | 8.0.32         | XCom                       |
| group_replication_applier | f84c4937-eb10-11ed-a2ff-000c294b702c | s1          |        3306 | ONLINE       | PRIMARY     | 8.0.32         | XCom                       |
| group_replication_applier | fe074cfd-ebe0-11ed-9b23-000c29c21d3a | s3          |        3306 | ONLINE       | SECONDARY   | 8.0.32         | XCom                       |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
3 rows in set (0.00 sec)

MEMBER_STATE 列可能的状态有:

  • ONLINE:组成员正常运行,并与其他组成员正常同步。
  • RECOVERING:处于分布式恢复中。
  • OFFLINE:不属于任何组。
  • ERROR:组成员运行异常,不参与组事务。
  • UNREACHABLE:组成员失联。

使用 performance_schema.replication_group_member_stats 查看认证信息,事务统计,定位性能问题。

[(none)]> SELECT * FROM performance_schema.replication_group_member_stats\G
*************************** 1. row ***************************
                              CHANNEL_NAME: group_replication_applier
                                   VIEW_ID: 16856664511922503:3
                                 MEMBER_ID: 0023cb72-f7da-11ed-ac6c-000c2986525b
               COUNT_TRANSACTIONS_IN_QUEUE: 0
                COUNT_TRANSACTIONS_CHECKED: 0
                  COUNT_CONFLICTS_DETECTED: 0
        COUNT_TRANSACTIONS_ROWS_VALIDATING: 0
        TRANSACTIONS_COMMITTED_ALL_MEMBERS: 0ae16bd1-fd34-11ed-9710-000c29c21d3a:1-12
            LAST_CONFLICT_FREE_TRANSACTION: 
COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE: 0
         COUNT_TRANSACTIONS_REMOTE_APPLIED: 1
         COUNT_TRANSACTIONS_LOCAL_PROPOSED: 0
         COUNT_TRANSACTIONS_LOCAL_ROLLBACK: 0
*************************** 2. row ***************************
                              CHANNEL_NAME: group_replication_applier
                                   VIEW_ID: 16856664511922503:3
                                 MEMBER_ID: f84c4937-eb10-11ed-a2ff-000c294b702c
               COUNT_TRANSACTIONS_IN_QUEUE: 0
                COUNT_TRANSACTIONS_CHECKED: 0
                  COUNT_CONFLICTS_DETECTED: 0
        COUNT_TRANSACTIONS_ROWS_VALIDATING: 0
        TRANSACTIONS_COMMITTED_ALL_MEMBERS: 0ae16bd1-fd34-11ed-9710-000c29c21d3a:1-12
            LAST_CONFLICT_FREE_TRANSACTION: 
COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE: 0
         COUNT_TRANSACTIONS_REMOTE_APPLIED: 3
         COUNT_TRANSACTIONS_LOCAL_PROPOSED: 0
         COUNT_TRANSACTIONS_LOCAL_ROLLBACK: 0
*************************** 3. row ***************************
                              CHANNEL_NAME: group_replication_applier
                                   VIEW_ID: 16856664511922503:3
                                 MEMBER_ID: fe074cfd-ebe0-11ed-9b23-000c29c21d3a
               COUNT_TRANSACTIONS_IN_QUEUE: 0
                COUNT_TRANSACTIONS_CHECKED: 0
                  COUNT_CONFLICTS_DETECTED: 0
        COUNT_TRANSACTIONS_ROWS_VALIDATING: 0
        TRANSACTIONS_COMMITTED_ALL_MEMBERS: 0ae16bd1-fd34-11ed-9710-000c29c21d3a:1-12
            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.00 sec)

Group Replication Operations

本节介绍管理组的常见操作。

Configuring an Online Group

从 MySQL 8.0.13 开始,可以在组复制运行时配置组。

  • 可以在任一组成员上进行配置。
  • 所有组成员必须处于 ONLINE 状态。
  • 在配置期间成员不能加入组。
  • 一次进行一个配置,并行配置操作会导致脑裂。
  • 所有组成员的版本必须为 MySQL 8.0.13 及以上。

Changing the Primary

使用 group_replication_set_as_primary() 函数设置组成员为主节点,可以在任一节点运行,完成后原主节点变为只读从节点。

如果除了组复制通道之外,在现有主节点上还运行着其他标准复制通道,则必须先停止该复制通道,然后才能更改主节点。查询 mysql.slave_master_info 表查看所有复制通道。

在 MySQL 8.0.29 之前,需要等待现有主节点的所有活动事务结束执行该函数。从 MySQL 8.0.29 起,可以指定事务超时时间,范围为 0 到 3600 秒。一旦指定,将阻止主节点启动事务。如果不设置超时时间,则将一直等待事务结束。

语法:

SELECT group_replication_set_as_primary(member_uuid[, timeout])

例如:将组成员 s2 设置为主节点

[(none)]> SELECT MEMBER_ID,MEMBER_HOST,MEMBER_STATE,MEMBER_ROLE FROM performance_schema.replication_group_members;
+--------------------------------------+-------------+--------------+-------------+
| MEMBER_ID                            | MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE |
+--------------------------------------+-------------+--------------+-------------+
| b4bdbc58-019c-11ee-9cc3-000c2986525b | s2          | ONLINE       | SECONDARY   |
| f84c4937-eb10-11ed-a2ff-000c294b702c | s1          | ONLINE       | PRIMARY     |
| fe074cfd-ebe0-11ed-9b23-000c29c21d3a | s3          | ONLINE       | SECONDARY   |
+--------------------------------------+-------------+--------------+-------------+
3 rows in set (0.01 sec)

[(none)]> SELECT group_replication_set_as_primary('b4bdbc58-019c-11ee-9cc3-000c2986525b', 300);
+-------------------------------------------------------------------------------+
| group_replication_set_as_primary('b4bdbc58-019c-11ee-9cc3-000c2986525b', 300) |
+-------------------------------------------------------------------------------+
| Primary server switched to: b4bdbc58-019c-11ee-9cc3-000c2986525b              |
+-------------------------------------------------------------------------------+
1 row in set (0.04 sec)

[(none)]> SELECT MEMBER_ID,MEMBER_HOST,MEMBER_STATE,MEMBER_ROLE FROM performance_schema.replication_group_members;
+--------------------------------------+-------------+--------------+-------------+
| MEMBER_ID                            | MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE |
+--------------------------------------+-------------+--------------+-------------+
| b4bdbc58-019c-11ee-9cc3-000c2986525b | s2          | ONLINE       | PRIMARY     |
| f84c4937-eb10-11ed-a2ff-000c294b702c | s1          | ONLINE       | SECONDARY   |
| fe074cfd-ebe0-11ed-9b23-000c29c21d3a | s3          | ONLINE       | SECONDARY   |
+--------------------------------------+-------------+--------------+-------------+
3 rows in set (0.00 sec)

查询 performance_schema.threadsPROCESSLIST_INFO 字段查看超时状态:

[(none)]> SELECT NAME, PROCESSLIST_INFO FROM performance_schema.threads
          WHERE NAME="thread/group_rpl/THD_transaction_monitor"\G
*************************** 1. row ***************************
            NAME: thread/group_rpl/THD_transaction_monitor
PROCESSLIST_INFO: Group replication transaction monitor: Stopped client connections

查询 performance_schema.events_stages_current 查看执行进度:

[(none)]> SELECT event_name, work_completed, work_estimated 
          FROM performance_schema.events_stages_current
          WHERE event_name LIKE "%stage/group_rpl%"\G
*************************** 1. row ***************************
    EVENT_NAME: stage/group_rpl/Primary Election: Waiting for members to turn on super_read_only
WORK_COMPLETED: 3
WORK_ESTIMATED: 5

Changing a Group's Mode

本节介绍如何调整组复制模式。

Changing to Single-Primary Mode

使用 group_replication_switch_to_single_primary_mode() 函数修改多主模式为单主模式。

语法:

SELECT group_replication_switch_to_single_primary_mode(member_uuid);

SELECT event_name, work_completed, work_estimated FROM performance_schema.events_stages_current WHERE event_name LIKE "%stage/group_rpl%";
+----------------------------------------------------------------------------+----------------+----------------+
| event_name                                                                 | work_completed | work_estimated |
+----------------------------------------------------------------------------+----------------+----------------+
| stage/group_rpl/Primary Switch: waiting for pending transactions to finish |              4 |             20 |
+----------------------------------------------------------------------------+----------------+----------------+
Changing to Multi-Primary Mode

使用 group_replication_switch_to_multi_primary_mode() 函数修改单主模式为多主模式。

语法:

SELECT group_replication_switch_to_multi_primary_mode();

SELECT event_name, work_completed, work_estimated FROM performance_schema.events_stages_current WHERE event_name LIKE "%stage/group_rpl%";
+----------------------------------------------------------------------+----------------+----------------+
| event_name                                                           | work_completed | work_estimated |
+----------------------------------------------------------------------+----------------+----------------+
| stage/group_rpl/Multi-primary Switch: applying buffered transactions |              0 |              1 |
+----------------------------------------------------------------------+----------------+----------------+

Using Group Replication Group Write Consensus

本节介绍如何检查和配置组的最大共识实例数。对于 性能较差的广域网,可以增加最大共识实例数来提高性能。

Inspecting a Group's Write Concurrency

使用 group_replication_get_write_concurrency() 函数获取组可以并行执行的最大共识实例数。

[(none)]> SELECT group_replication_get_write_concurrency();
+-------------------------------------------+
| group_replication_get_write_concurrency() |
+-------------------------------------------+
|                                        10 |
+-------------------------------------------+
1 row in set (0.00 sec)
Configuring a Group's Write Concurrency

使用 group_replication_set_write_concurrency() 函数设置组可以并行执行的最大共识实例数。默认值为 10,有效值范围为 10 到 200。

[(none)]> SELECT group_replication_set_write_concurrency(10);
+-----------------------------------------------------------------------------------+
| group_replication_set_write_concurrency(10)                                       |
+-----------------------------------------------------------------------------------+
| UDF is asynchronous, check log or call group_replication_get_write_concurrency(). |
+-----------------------------------------------------------------------------------+
1 row in set (0.00 sec)

Configuring Member Actions

从 MySQL 8.0.26 开始,可以在主节点使用 group_replication_enable_member_action()group_replication_disable_member_action() 函数为组成员设置在特定情况下采取的动作,并会将动作传递到其他组成员,但不会赋予 GTID,也不会写入二进制日志。

查询 performance_schema.replication_group_member_actions 查看组成员动作。

[(none)]> SELECT * FROM performance_schema.replication_group_member_actions;
+------------------------------------------+------------------------+---------+----------+----------+----------------+
| name                                     | event                  | enabled | type     | priority | error_handling |
+------------------------------------------+------------------------+---------+----------+----------+----------------+
| mysql_disable_super_read_only_if_primary | AFTER_PRIMARY_ELECTION |       1 | INTERNAL |        1 | IGNORE         |
| mysql_start_failover_channels_if_primary | AFTER_PRIMARY_ELECTION |       1 | INTERNAL |       10 | CRITICAL       |
+------------------------------------------+------------------------+---------+----------+----------+----------------+
2 rows in set (0.01 sec)

启用或禁用组成员动作都会增加组成员动作版本,查询 performance_schema.replication_group_configuration_version 查看版本,默认为 1。

[(none)]> SELECT * FROM performance_schema.replication_group_configuration_version;
+----------------------------------+---------+
| name                             | version |
+----------------------------------+---------+
| replication_group_member_actions |       1 |
+----------------------------------+---------+
1 row in set (0.01 sec)

当组成员离开组后,可以使用 group_replication_reset_member_actions() 函数重置其动作为默认值,移除组成员动作配置。

Restarting a Group

组复制用于保障数据库服务的连续可用,即使其中部分成员离开组,只要剩余大多数成员可用,则可以继续使用。但是,当关闭所有组复制或者实例,此时直接启动组就会报错,需要再次引导重建组。

例子:重启所有组成员实例,再次启动组复制报错

重启各个组成员的实例。

[root@s1 ~]# systemctl restart mysqld.service 
[root@s2 ~]# systemctl restart mysqld.service 
[root@s3 ~]# systemctl restart mysqld.service 

此时查看组成员:

[(none)]> SELECT MEMBER_ID,MEMBER_HOST,MEMBER_STATE,MEMBER_ROLE FROM performance_schema.replication_group_members;
+-----------+-------------+--------------+-------------+
| MEMBER_ID | MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE |
+-----------+-------------+--------------+-------------+
|           |             | OFFLINE      |             |
+-----------+-------------+--------------+-------------+
1 row in set (0.00 sec)

可以看到没有组成员,启动组复制报错:

[(none)]> 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.

报错日志:

2023-06-03T11:11:55.818457+08:00 0 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] Error on opening a connection to peer node s3:33061 when joining a group. My local port is: 33061.'
2023-06-03T11:11:55.818569+08:00 0 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] Error connecting to all peers. Member join failed. Local port: 33061'
2023-06-03T11:11:55.910396+08:00 0 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] The member was unable to join the group. Local port: 33061'
2023-06-03T11:12:06.114848+08:00 9 [ERROR] [MY-011640] [Repl] Plugin group_replication reported: 'Timeout on wait for view after joining group'
2023-06-03T11:12:06.115576+08:00 9 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] The member is leaving a group without being on one.'
2023-06-03T11:12:06.170760+08:00 9 [System] [MY-011566] [Repl] Plugin group_replication reported: 'Setting super_read_only=OFF.'

再次引导重建组时,由于此时各个成员事务不一致,需要找到有最近事务的成员(不一定是之前的主节点),在此成员上引导重建组。具体步骤如下:

  1. 对于每个组成员,如果组复制没有停止,使用 STOP GROUP_REPLICATION 语句停止组复制:
[(none)]> STOP GROUP_REPLICATION;
Query OK, 0 rows affected (0.00 sec)

在所有组成员确认参数 group_replication_start_on_boot 为默认值 OFF,阻止在启动 MySQL Server 时启动组复制。否则修改参数文件并重启 MySQL Server。

[(none)]> SHOW VARIABLES LIKE 'group_replication_start_on_boot';
+---------------------------------+-------+
| Variable_name                   | Value |
+---------------------------------+-------+
| group_replication_start_on_boot | OFF   |
+---------------------------------+-------+
1 row in set (0.01 sec)

启动 MySQL Server 后,在所有组成员上确认都没有启动组复制:

[(none)]> SELECT MEMBER_ID,MEMBER_HOST,MEMBER_STATE,MEMBER_ROLE FROM performance_schema.replication_group_members;
+-----------+-------------+--------------+-------------+
| MEMBER_ID | MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE |
+-----------+-------------+--------------+-------------+
|           |             | OFFLINE      |             |
+-----------+-------------+--------------+-------------+
1 row in set (0.01 sec)

然后在各个组成员上获取 gtid_executedreceived_transaction_set

s1:

[(none)]> SELECT @@GLOBAL.GTID_EXECUTED;
+-------------------------------------------+
| @@GLOBAL.GTID_EXECUTED                    |
+-------------------------------------------+
| 0ae16bd1-fd34-11ed-9710-000c29c21d3a:1-23 |
+-------------------------------------------+
1 row in set (0.00 sec)


[(none)]> SELECT received_transaction_set FROM performance_schema.replication_connection_status
          WHERE channel_name="group_replication_applier";
+--------------------------+
| received_transaction_set |
+--------------------------+
|                          |
+--------------------------+
1 row in set (0.00 sec)

s2:

[(none)]> SELECT @@GLOBAL.GTID_EXECUTED;
+-------------------------------------------+
| @@GLOBAL.GTID_EXECUTED                    |
+-------------------------------------------+
| 0ae16bd1-fd34-11ed-9710-000c29c21d3a:1-23 |
+-------------------------------------------+
1 row in set (0.00 sec)

[(none)]> SELECT received_transaction_set FROM performance_schema.replication_connection_status
          WHERE channel_name="group_replication_applier";
+--------------------------+
| received_transaction_set |
+--------------------------+
|                          |
+--------------------------+
1 row in set (0.00 sec)

s3:

[(none)]> SELECT @@GLOBAL.GTID_EXECUTED;
+-------------------------------------------+
| @@GLOBAL.GTID_EXECUTED                    |
+-------------------------------------------+
| 0ae16bd1-fd34-11ed-9710-000c29c21d3a:1-23 |
+-------------------------------------------+
1 row in set (0.00 sec)

[(none)]> SELECT received_transaction_set FROM performance_schema.replication_connection_status
          WHERE channel_name="group_replication_applier";
+--------------------------+
| received_transaction_set |
+--------------------------+
|                          |
+--------------------------+
1 row in set (0.00 sec)
  1. 根据以上查询结果,找到最大事务集所在实例,在其上进行引导重建组。
[(none)]> SET GLOBAL group_replication_bootstrap_group=ON;
Query OK, 0 rows affected (0.00 sec)

[(none)]> START GROUP_REPLICATION;
Query OK, 0 rows affected (1.21 sec)

[(none)]> SET GLOBAL group_replication_bootstrap_group=OFF;
Query OK, 0 rows affected (0.00 sec)

[(none)]> SELECT MEMBER_ID,MEMBER_HOST,MEMBER_STATE,MEMBER_ROLE FROM performance_schema.replication_group_members;
+--------------------------------------+-------------+--------------+-------------+
| MEMBER_ID                            | MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE |
+--------------------------------------+-------------+--------------+-------------+
| f84c4937-eb10-11ed-a2ff-000c294b702c | s1          | ONLINE       | PRIMARY     |
+--------------------------------------+-------------+--------------+-------------+
1 row in set (0.00 sec)
  1. 在其他节点执行 START GROUP_REPLICATION,增加其他组成员。
[(none)]> START GROUP_REPLICATION;
Query OK, 0 rows affected (1.63 sec)
  1. 完成后查看组成员。
[(none)]> SELECT MEMBER_ID,MEMBER_HOST,MEMBER_STATE,MEMBER_ROLE FROM performance_schema.replication_group_members;
+--------------------------------------+-------------+--------------+-------------+
| MEMBER_ID                            | MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE |
+--------------------------------------+-------------+--------------+-------------+
| b4bdbc58-019c-11ee-9cc3-000c2986525b | s2          | ONLINE       | SECONDARY   |
| f84c4937-eb10-11ed-a2ff-000c294b702c | s1          | ONLINE       | PRIMARY     |
| fe074cfd-ebe0-11ed-9b23-000c29c21d3a | s3          | ONLINE       | SECONDARY   |
+--------------------------------------+-------------+--------------+-------------+
3 rows in set (0.00 sec)

Transaction Consistency Guarantees

本节介绍如何配置组的事务一致性保证。

Understanding Transaction Consistency Guarantees

组复制是一个最终一致性系统。

Consistency Guarantees and Primary Failover

在单主模式,当发生主从切换时,新的主库采取的故障转移策略可以时:

  • 可用性优先,立即对外提供服务,不管数据差距。此时客户端有可能会查询到过时数据。
  • 一致性优先,限制外部访问,直到数据同步完成。此时客户端需要等待。

在 MySQL 8.0.14 之前,无法配置故障转移策略,默认为可用性优先。从 MySQL 8.0.14 起,才可以使用 group_replication_consistency 参数在主库故障转移时配置事务一致性保证级别。

Data Flow Operations

对于读写分离,即写入流量路由到主库,读取流量路由到从库,需要注意由于从库应用的延迟,在从库上读取的数据存在滞后。

Transaction Synchronization Points

根据需求,事务同步可以在:

  • 读取时同步,即读取会话将等待,直到给定的时间点后才查询。
  • 写入时同步,即写入会话将等待,直到所有从节点都写入了数据。

Configuring Transaction Consistency Guarantees

使用参数 group_replication_consistency 指定的组复制一致性级别有:

  • EVENTUAL:最终一致性,默认值。读、写事务无需等待事务被应用,这是在 MySQL 8.0.14 之前的行为,此时还没有引入参数 group_replication_consistency。此时读事务有可能会读取到过时数据,写事务有可能会导致冲突。
  • BEFORE_ON_PRIMARY_FAILOVER:新主库的本地一致性。新主库需要等到从旧主库来的事务被应用完成后,才会接收读、写事务。这可以确保在故障转移时,客户端始终获取最新的数据。
  • BEFORE:读取时同步,本地节点强一致性。必须等待事务被应用完成后,才会执行新的请求,否则会一直等待。等待的时间和中继日志里未应用的事务量成一定比率。包含 BEFORE_ON_PRIMARY_FAILOVER 级别。
  • AFTER:写入时同步,全局强一致性。设置为此模式的节点,必须等待集群内其他所有节点应用完自己中继日志里的事务,才能返回结果。包含 BEFORE_ON_PRIMARY_FAILOVER 级别。
  • BEFORE_AND_AFTER:读写时都同步,确保本地和全局都强一致性。所有事务必须等待本地先前事务被应用完成以及本事务在其他节点被应用完成。包含 BEFOREAFTERBEFORE_ON_PRIMARY_FAILOVER 级别。

BEFOREBEFORE_ON_PRIMARY_FAILOVER 一致性级别可用于只读和读写事务, AFTER 一致性级别不影响只读事务。

How to Choose a Consistency Level

根据使用场景选择一致性级别:

  1. 写多读少,不读取过时数据,写入时不等待,读取时等待,选择 BEFORE
  2. 读多写少,不读取过时数据,写入时等待,读取时不等待,一旦写入需等待事务被应用到所有组成员,选择 AFTER

根据需要,可以在会话级或者全局配置 group_replication_consistency。例如:

对于需要立即同步到其他组成员的授权操作:

 SET @@SESSION.group_replication_consistency= 'AFTER'

对于需要在从节点读取最新数据:

 SET @@SESSION.group_replication_consistency= 'BEFORE'

总而言之,不需要为所有事务指定一致性级别,只在某些事务实际需要的情况下才指定。

Impacts of Consistency Levels

根据对复制组的影响,一致性级别可以分为:

  • BEFORE:只影响本地节点。
  • AFTERBEFORE_AND_AFTER:影响所有组成员。

不是 EVENTUAL 一致性级别的事务,等待的最长时间由参数 wait_timeout 指定,默认为 8 小时。如果超时,则抛出 ER_GR_HOLD_WAIT_TIMEOUT 错误。

Impact of Consistency on Primary Election

在单主模式,将参数 group_replication_consistency 设置为 BEFORE_ON_PRIMARY_FAILOVER 一致性级别,当改变主节点时,一致性高于可用性,新主库需要等到从旧主库来的事务被应用完成后,才会接收读、写事务。

故为确保无论哪个组成员被选为主节点,组复制都有相同的一致性级别,所有组成员都必须配置 BEFORE_ON_PRIMARY_FAILOVER (或者更高)一致性级别到参数文件中。

[mysqld]
group_replication_consistency='BEFORE_ON_PRIMARY_FAILOVER';
Permitted Queries Under Consistency Rules

尽管 BEFORE_ON_PRIMARY_FAILOVER 一致性级别会在切换主节点时阻塞写,但并非所有读操作都会阻塞,可以使用以下读操作进行监控,观察以及排错:

  • SHOW 语句
  • SET 语句
  • USE 语句
  • 对于 performance_schemasysSELECT 语句
  • STOP GROUP_REPLICATION 语句
  • SHUTDOWN 语句
  • RESET PERSIST 语句

Distributed Recovery

每当成员加入复制组时,都必须追上其他组成员的事务,此过程称为分布式恢复。

组复制有以下两种分布式恢复方式:

  • 远程克隆open in new window,从 MySQL 8.0.17 起,安装克隆插件后,组复制会自动配置和管理远程克隆操作。适用于新加入成员与现有组成员之间事务差距很大,或者新成员需要的事务已经不存在与现有成员的二进制日志中。
  • 异步复制open in new window,使用名称为 group_replication_recovery 的标准异步复制通道。适用于新加入成员与现有组成员之间事务差距不大,且新成员需要的事务存在于与现有成员的二进制日志中。

在发出 START GROUP_REPLICATION 语句后,组复制会自动选择最佳的方式进行分布式恢复。

Connections for Distributed Recovery

现有组成员和要加入成员之间用于分布式恢复的连接与组复制用于组成员之间通信的连接不同:

  • 组复制用于组成员之间通信的连接由参数 group_replication_local_address 指定。
  • 对于分布式恢复,在 MySQL 8.0.20 及之前,组成员提供标准 SQL 客户端连接给要加入的成员,由参数 hostnameport 指定,如果指定了 report_port,则使用该参数替代 port
  • 从 MySQL 8.0.21 开始,使用参数 group_replication_advertise_recovery_endpoints 为要加入成员指定专门的分布式恢复接入点(Distributed Recovery Endpoints)用于分布式恢复,以便将分布式恢复的流量从普通的客户端连接分离开来,避免在进行分布式恢复的时候影响正常的业务。

为要加入成员创建连接用于分布式恢复的步骤如下:

  1. 当成员加入组时,使用参数 group_replication_local_address 发起连接,连接到参数 group_replication_group_seeds 指定列表中的一个种子成员。

  2. 在此连接上,种子成员使用组复制的组成员身份服务为要加入成员提供所有在线的成员列表信息,包含分布式恢复接入点或者标准 SQL 客户端连接。

  3. 要加入成员从上面的列表信息中选择合适的组成员作为源库,用于分布式恢复。

  4. 要加入成员使用源库的分布式恢复接入点连接到源库,如果源库没有指定分布式恢复接入点,则使用源库的标准 SQL 客户端连接。

  5. 如果失败,则尝试连接另一个组成员。

参数 group_replication_advertise_recovery_endpoints 的默认值为 DEFAULT,表示要加入成员使用现有组成员的标准 SQL 客户端连接,客户端连接由参数 hostnameport 指定,如果指定了 report_port,则使用该参数替代 port。表 performance_schema.replication_group_membersMEMBER_HOSTMEMBER_PORT 字段为连接的地址和端口。

可以使用参数 group_replication_advertise_recovery_endpoints 指定一个或者多个分布式恢复接入点,类似如下:

group_replication_advertise_recovery_endpoints= "127.0.0.1:3306,127.0.0.1:4567,[::1]:3306,localhost:3306"

Cloning for Distributed Recovery

从 MySQL 8.0.17 起,组复制可以使用克隆插件进行分布式恢复。

Prerequisites for Cloning

关于克隆,可以参考:The Clone Pluginopen in new window。关于远程克隆,可以参考:Cloning Remote Dataopen in new window

对于组复制中的克隆,需要注意以下关键点:

  • 要加入成员和现有组成员都必须安装和激活克隆插件。
  • 要加入成员和现有组成员的操作系统版本和 MySQL Server 版本必须一致。
  • 要加入成员上的插件需要与现有组成员保持一致。
  • 无需配置参数 clone_valid_donor_list,组复制会自动配置。
  • 要加入成员使用 mysql.session 用户进行克隆,此用户包含 CLONE_ADMIN 权限。
  • 需要为组成员复制用户授予 BACKUP_ADMIN 权限以支持克隆。
Threshold for Cloning

使用参数 group_replication_clone_threshold 指定事务数量,用于决定是否采用远程克隆来进行分布式恢复。如果从参数 gtid_executed计算出要加入成员和组成员之间的事务差距大于 group_replication_clone_threshold 值,则使用远程克隆。

参数 group_replication_clone_threshold 的默认值为 GTID 的最大值,表示只要可以使用二进制日志进行分布式恢复,则禁用克隆。可以设置一个合适的值以便启用远程克隆。

但是如果从参数 gtid_purged 发现所需的事务已经不在二进制日志中,组复制会试图进行远程克隆操作,而不考虑该参数的设置,因为此时只能使用远程克隆进行分布式恢复了。

Configuring Distributed Recovery

可以为组复制分布式恢复过程配置以下参数:

  • group_replication_recovery_retry_count:要加入成员连接到各个组成员获取二进制日志的尝试总次数,默认为 10,超过该值返回错误。如果现有组成员为 2 ,设置为 6,表示要加入成员会尝试 3 轮,每轮每个成员尝试 1 次。此参数不影响远程克隆操作。
mysql> SET GLOBAL group_replication_recovery_retry_count= 6;
  • group_replication_recovery_reconnect_interval:尝试连接所有组成员失败后,到发起下一次连接的时间间隔,默认为 60 秒,表示每一轮尝试连接后,间隔 60 秒再尝试连接。此参数不影响远程克隆操作。
mysql> SET GLOBAL group_replication_recovery_reconnect_interval= 30;
  • group_replication_recovery_complete_at:指定什么时间将要加入成员标记为 ONLINE,默认为 TRANSACTIONS_APPLIED,表示只有在接收,认证及应用完事务后才将要加入成员标记为 ONLINETRANSACTIONS_CERTIFIED 表示在接收,认证完事务后就将要加入成员标记为 ONLINE

Fault Tolerance for Distributed Recovery

组复制在进行分布式恢复时,出现以下情况会自动尝试连接其他组成员:

  • Connection error
  • Replication errors
  • Remote cloning operation errors
  • Donor leaves the group

查询 performance_schema.replication_applier_status_by_worker 查看最后一次尝试出现的错误。

组复制在进行分布式恢复时,出现以下情况会退出分布式恢复,成员加入失败:

  • Purged transactions
  • Extra transactions
  • Connection retry limit reached
  • No more donors
  • Joining member leaves the group

How Distributed Recovery Works

View and View Changes

组视图(View)对应当前活动的组成员。

组视图改变(View Change)发生在组配置被修改的时候,例如成员加入或离开。任何组成员身份更改都会导致在同一逻辑时间点向所有组成员传达独立的视图更改。

组视图标识符(View Identifier)唯一标识一个视图,在组视图改变发生时产生。

通过二进制日志事件 "view change log event" (VCLE),记录组视图标识符以划分在组成员关系发生变化之前和之后传输的事务。

组视图标识符包含两部分:

  • 随机部分:创建组时产生,不会改变。
  • 递增部分:组视图改变发生时递增。

完全关闭组复制后,再次引导重建组会产生新的组视图标识符

Begin: Stable Group

开始阶段,当前所有组成员处于联机状态。

Stable Group

View Change: a Member Joins

每当新成员(S4)加入组并因此执行组视图改变时,每个联机组成员都会将组视图改变日志事件(VC4)排队等待执行。

同时,要加入成员通过从组成员服务声明的联机组成员列表中选择合适的源库。

A Member Joins

State Transfer: Catching Up

组复制选择分布式恢复的方式:

  • 如果所需事务数量超过参数 group_replication_clone_threshold 设定的值,或者所需事务不存在于任何组成员二进制日志中,使用远程克隆open in new window进行分布式恢复。远程克隆完成并重启 MySQL Server 后,要加入成员从源库二进制日志中获取并应用在远程克隆期间源库产生的事务。
  • 如果所需事务数量少于参数 group_replication_clone_threshold 设定的值,或者没有安装克隆插件,使用异步复制open in new window进行分布式恢复。

组复制会创建要加入成员(S4)与作为其源库的组成员(S2)之间的连接并开始状态(二进制日志)传输,直到要加入成员(S4)的应用线程处理组视图改变日志事件(VC4),如前所述,此事件是在新成员加入时触发的。

State Transfer: Catching Up

在要加入成员(S4)从源库(S2)复制数据时,其同时也缓存来自于组的事务。分布式恢复完成时,要加入成员(S4)停止从源库(S2)复制数据,改为应用缓存的事务。

Queued Transactions

Finish: Caught Up

当要加入成员(S4)识别出具有预期视图标识符视图改变日志事件(VC4)时,到源库(S2)的连接将终止,并开始应用缓存的事务。

当要加入成员(S4)没有要处理的事务了,其状态将变为联机状态。

Instance Online

Group Replication Security

Group Replication IP Address Permissions

当且仅当使用 XCom 通信协议栈建立组复制时,即参数 group_replication_communication_stack 为默认值 XCOM 时,可以使用参数 group_replication_ip_allowlist 指定允许加入到复制组的主机,默认值为 AUTOMATIC,表示允许以下私有子网主机加入:

IPv4 (as defined in RFC 1918)
10/8 prefix       (10.0.0.0 - 10.255.255.255) - Class A
172.16/12 prefix  (172.16.0.0 - 172.31.255.255) - Class B
192.168/16 prefix (192.168.0.0 - 192.168.255.255) - Class C

IPv6 (as defined in RFC 4193 and RFC 5156)
fc00:/7 prefix    - unique-local addresses
fe80::/10 prefix  - link-local unicast addresses

127.0.0.1 - localhost for IPv4
::1       - localhost for IPv6

当主机不在以上子网时,则需要显式设置 group_replication_ip_allowlist 参数,包含所有组成员的参数 group_replication_local_address 指定的地址,可以使用的格式有:

  • IPv4 addresses (for example, 198.51.100.44)
  • IPv4 addresses with CIDR notation (for example, 192.0.2.21/24)
  • IPv6 addresses, from MySQL 8.0.14 (for example, 2001:db8:85a3:8d3:1319:8a2e:370:7348)
  • IPv6 addresses with CIDR notation, from MySQL 8.0.14 (for example, 2001:db8:85a3:8d3::/64)
  • Host names (for example, example.org)
  • Host names with CIDR notation (for example, www.example.com/24)

例如:

mysql> SET GLOBAL group_replication_ip_allowlist="192.0.2.21/24,198.51.100.44,203.0.113.0/24,2001:db8:85a3:8d3:1319:8a2e:370:7348,example.org,www.example.com/24";

可以列出所有组成员的 IP 地址,以逗号分隔,后续如果增加组成员,可以动态修改;也可以使用 CIDR 格式指定参数值为整个私有网段。如果使用主机名,则一定要在本地配置解析,避免因为 DNS 问题导致连接失败。

特别注意,需要为所有组成员设置相同的 group_replication_ip_allowlist 参数值。

Group Replication Performance and Troubleshooting

Fine Tuning the Group Communication Thread

当加载组复制插件时,组通信线程(Group Communication Thread GCT)就会不断循环运行,处理与仲裁和故障检测相关的任务,发送保持活动状态的消息,并处理 MySQL Server 与组之间传入传出事务。GCT 等待队列中的传入消息。当没有消息时,GCT 将等待。

在某些情况下,通过将这个等待配置得稍微长一些(进行主动等待),可以减少操作系统执行上下文切换时从处理器中换出 GCT 线程的次数。要强制 GCT 执行主动等待,使用参数 group_replication_poll_spin_loops 设置等待次数(等待通信引擎互斥锁(Mutex)被释放的次数,不是时间单位),这使得 GCT 在对下一个消息进行实际轮询队列之前,在已配置的等待次数内进行循环时不做任何相关操作。

mysql> SET GLOBAL group_replication_poll_spin_loops= 10000;

Single Consensus Leader

默认情况下,组复制的组通信引擎(XCom,Paxos 的一种变体)使用复制组的每个成员作为领导者运行。从 MySQL 8.0.27 开始,当组处于单主模式时,组通信引擎可以使用单个领导者来驱动共识,可以提高单主模式下的性能,尤其是当该组的某些从节点无法访问时。

要使用单个共识领导者,组必须配置如下:

  • 必须处于单主模式。
  • 指定参数 group_replication_paxos_single_leaderON,默认为 OFF。必须引导重建组才能生效。
  • 组复制通信协议版本必须设置为 8.0.27 或以上,使用函数 group_replication_get_communication_protocol() 查看通讯协议版本。

查询 performance_schema.replication_group_communication_information 表查看当前首选和实际的共识领导者,首选领导者是组复制的选择,实际领导者是组通信引擎选择的领导者。其字段 WRITE_CONSENSUS_SINGLE_LEADER_CAPABLE 显示该组是否支持使用单个领导者。

Message Compression

对于联机组成员之间的消息传递,组复制默认启用压缩。使用参数 group_replication_compression_threshold 指定压缩阈值,默认 1000000 字节,超过此阈值的消息将被压缩。

可以使用下面语句调整压缩阈值:

STOP GROUP_REPLICATION;
SET GLOBAL group_replication_compression_threshold = 2097152;
START GROUP_REPLICATION;

如果将参数 group_replication_compression_threshold 设置为 0,将禁用压缩。

组复制使用 LZ4 压缩算法,最大支持 2113929216 字节输入。参数 group_replication_compression_threshold 的最大值为 4294967295 字节,与 XCom 消息的最大值一致。故在使用 LZ4 压缩时,参数 group_replication_compression_threshold 不能超过 2113929216 字节。

所有组成员的参数 group_replication_compression_threshold 需保持一致。

使用参数 group_replication_recovery_compression_algorithmsgroup_replication_recovery_zstd_compression_level 指定分布式恢复压缩。

还可以使用参数 binlog_transaction_compression (从 MySQL 8.0.20 起) 启用二进制日志事务压缩以节省带宽。

组复制的压缩和解压缩由 Group Communication System API 层处理,该层之上为原始数据,之下为压缩后的数据,过程如下图:

Compression Support

Message Fragmentation

当组复制成员间发生非常大的消息时,可能会导致部分组成员被报告为失败并被驱逐出组。这是由于组复制的组通信引擎(XCom,Paxos 的一种变体)使用的单线程被占用来处理该消息的时间太长,以至于部分组成员可能会报告接收者失败。从 MySQL 8.0.16 开始,默认情况下,大消息会自动分段发送,并由接收者重新组装。

使用参数 group_replication_communication_max_message_size 指定组复制通信的最大消息大小,默认为 10 MB(10485760 字节),事务消息超过此大小则自动分段,对于压缩后的事务消息同样如此。此参数最大值与参数 replica_max_allowed_packet 的最大值相同,都是 1 GB(1073741824 字节),参数 group_replication_communication_max_message_size 必须小于参数 replica_max_allowed_packet 的值,因为应用线程无法处理大于最大允许数据包大小的消息片段。

修改此参数同样需要重启组复制才能生效:

STOP GROUP_REPLICATION;
SET GLOBAL group_replication_communication_max_message_size= 5242880;
START GROUP_REPLICATION;

XCom Cache Management

组复制的组通信引擎(XCom,Paxos 的一种变体)包含一个缓存,作为共识协议的一部分,用于在组成员之间交换消息(及其元数据),还用于在一段时间无法与其他组成员通信后重新连接到组的成员恢复丢失的消息。

从 MySQL 8.0.16 开始,可以使用参数 group_replication_message_cache_size 设置缓存最大值,如果达到该值,XCom 移除不需要的旧条目。所有组成员应设置相同的缓存最大值。

在 MySQL 8.0.16 之前,缓存大小固定为 1 GB,需确保服务器有足够的内存。

在 MySQL 8.0.20 及其之前,参数 group_replication_message_cache_size 的最小值为 1 Gb,从 MySQL 8.0.21 开始,最小可以设置为 128 MB(134217728 字节)。

根据服务器资源以及系统繁忙程度设置合适的缓存大小,避免暂时离开组的成员重新加入到组时无法从缓存中检索到需要的所有消息,否则成员只能使用分布式恢复加入组。

查询表 performance_schema.memory_summary_global_by_event_name 查看内存使用统计信息,包括缓存条目的当前数量和缓存的当前大小:

[(none)]> SELECT * FROM performance_schema.memory_summary_global_by_event_name 
          WHERE EVENT_NAME LIKE 'memory/group_rpl/GCS_XCom::xcom_cache'\G
*************************** 1. row ***************************
                  EVENT_NAME: memory/group_rpl/GCS_XCom::xcom_cache
                 COUNT_ALLOC: 890
                  COUNT_FREE: 0
   SUM_NUMBER_OF_BYTES_ALLOC: 352662
    SUM_NUMBER_OF_BYTES_FREE: 0
              LOW_COUNT_USED: 0
          CURRENT_COUNT_USED: 890
             HIGH_COUNT_USED: 890
    LOW_NUMBER_OF_BYTES_USED: 0
CURRENT_NUMBER_OF_BYTES_USED: 352662
   HIGH_NUMBER_OF_BYTES_USED: 352662
1 row in set (0.00 sec)

Responses to Failure Detection and Network Partitioning

组复制的故障检测机制用于识别不再与组通信的组成员,并在它们看起来可能已经失败时将其驱逐。

通常,所有组成员定期与所有其他组成员交换消息。如果一个组成员在 5 秒内没有收到来自某个特定成员的任何消息,则当此检测周期结束时,就会创建一个对该成员的怀疑(Suspicion)。当怀疑超时时,被怀疑的成员被认为有故障,并被驱逐出组。被驱逐的成员从其他成员上的成员身份列表中删除,但它不知道自己已被驱逐出组,因此认为自己联机,而其他成员无法访问。如果该成员实际上并没有故障(例如,只是由于瞬时网络问题而断开连接)并且能够恢复与其他成员的通信,将接收一个包含其已被从组中驱逐的信息的视图。

可以在过程中的多个点配置组成员(包括故障成员本身)对这些情况的响应。默认情况下,如果怀疑成员失败,则会发生以下行为:

  1. 在 MySQL 8.0.20 及之前,当一个怀疑(Suspicion)被创建时,会立即超时,成员将会被驱逐出组。从 MySQL 8.0.21开始,在怀疑超时之前增加了 5 秒的等待时间。
  2. 如果被驱逐的成员恢复通信并意识到它被驱逐,在 MySQL 8.0.20 及之前,它不会尝试重新加入该组。从 MySQL 8.0.21 开始,它会进行 3 次自动尝试重新加入组(每次尝试间隔 5 分钟)。如果失败,停止尝试重新加入组。
  3. 当被驱逐的成员不再尝试重新加入组时,会切换到超级只读模式(Super Read Only)。(但在 MySQL 8.0.12 到 8.0.15 的版本,默认情况下成员会自行关闭。)

可以根据系统环境调整以上默认行为。

由于网络分区(Network Partition),成员可能失去与部分(但不是全部)复制组的联系。例如,在一组 5 个服务器(S1,S2,S3,S4,S5)中,如果(S1,S2)和(S3,S4,S5)之间网络不通,则存在网络分区。由于(S1,S2)处于少数状态,无法与超过一半以上组成员取得联系,任何事务都会被阻止。

Expel Timeout

从 MySQL 8.0.13 开始,可以使用参数 group_replication_member_expel_timeout 指定在创建怀疑(Suspicion)后,将被驱逐之前,额外的超时等待时间,称为驱逐超时(Expel Timeout)。从 MySQL 8.0.21 开始,默认为 5 秒,最大为 3600 秒。在此期间,可疑成员状态为 UNREACHABLE ,但不会从组成员列表中删除。

  • 如果在超时前再次激活,则该成员将自动应用缓存在剩余组成员中的所有消息并进入 ONLINE 状态。在这种情况下,该成员被该组视为同一化身。
  • 如果在超时后再次激活,则该成员会收到一个其被驱逐的组视图,并在此时意识到自己被驱逐了。从 MySQL 8.0.16 开始,可以使用参数 group_replication_autorejoin_tries 指定成员此时自动尝试重新加入组。从 MySQL 8.0.21 开始,默认启用此参数并进行 3 次尝试,每次间隔 5 分钟。如果重新加入组失败,则执行参数 group_replication_exit_state_action 指定的退出操作。

在以下情况考虑增大参数 group_replication_member_expel_timeout 值:

  • 网速很慢,默认超时时间内,组成员无法完成消息传递。
  • 网络中断,避免不必要的驱逐和主库切换。

如果组的任一成员被怀疑(Suspicion),则无法重新配置组成员身份(添加或删除成员或选举新主库)。此时要么让被怀疑(Suspicion)成员再次激活,要么通过调整参数 group_replication_member_expel_timeout ,让其立即被驱逐出组。

通过设置参数 group_replication_start_on_bootON,可以在组成员意外重启后,自动尝试重新加入组。从 MySQL 8.0.19 开始,组复制自动使用 Group Communication System (GCS) 功能,进行 10 次 重新加入组尝试,每次间隔 5 秒。

Unreachable Majority Timeout

默认情况下,由于网络分区而发现自己处于少数的成员不会自动离开组。可以使用参数 group_replication_unreachable_majority_timeout 指定成员在与大多数组成员失去联系后,退出组前,等待超时的秒数。

少数组中的成员在等待超时后,会进入到 ERROR 状态。从 MySQL 8.0.16 开始,可以使用参数 group_replication_autorejoin_tries 指定成员此时自动尝试重新加入组。从 MySQL 8.0.21 开始,默认启用此参数并进行 3 次尝试。如果重新加入组失败,则执行参数 group_replication_exit_state_action 指定的退出操作。

设置此参数时需考虑:

  • 在偶数成员数量的组中,如果两个分区包含相同数量的服务器,则两个组都认为自己处于少数并进入ERROR状态。
  • 如果不设置该参数,少数组中的成员不会自动进入到 ERROR 状态,必须手动停止。
  • 在已经处于少数状态的服务器上面设置该参数,不会生效。

Auto-Rejoin

从 MySQL 8.0.16 开始,可以使用参数 group_replication_autorejoin_tries 为被驱逐组成员或者已超时等待的少数组成员指定自动尝试重新加入组。在 MySQL 8.0.20 及之前,默认为 0,不自动尝试加入组。从 MySQL 8.0.21 开始,默认启用此参数并进行 3 次尝试,每次间隔 5 分钟。在此期间,该成员保持超级只读(Super Read Only)模式,其组视图中的状态为 ERROR,不接受写入,但可以读取,此时会读取到过时的数据。

如果未启用该参数或者重新加入组失败,则执行参数 group_replication_exit_state_action 指定的退出操作。

使用以下性能模式表监控自动加入过程:

  • events_stages_current:其中 EVENT_NAME 字段包含 Undergoing auto-rejoin procedureWORK_COMPLETED 字段为重试次数。
  • events_stages_summary_global_by_event_name:其中 COUNT_STAR 字段为启动自动重新加入的次数。

Exit Action

从 MySQL 8.0.12 和 MySQL 5.7.24 开始,可以使用参数 group_replication_exit_state_action 指定在成员意外离开组或自动加入组失败时执行特定的退出操作。在成员被驱逐的情况下,由于成员在重新连接到组之前并不知道自己被驱逐,故只有在该成员重连或者对自己产生怀疑并且驱逐自己时才执行特定的退出操作。

按影响范围,特定的退出操作有:

  • READ_ONLY:通过设置参数 super_read_onlyON 将成员切换为超级只读模式,不接受写入,但可以读取,此时会读取到过时的数据。此为 MySQL 8.0.15 之后的默认操作,执行此退出操作后,成员在组视图中的状态为 ERROR
  • OFFLINE_MODE:从 MySQL 8.0.18 开始,可以通过设置参数 offline_modeON 将成员切换为离线模式(Offline Mode),此时只有具有 CONNECTION_ADMIN 权限(或者 SUPER)的管理员用户才能连接,同时还会将参数 super_read_only 设置为 ON,阻止所有用户进行写入操作,并使 MySQL Router 等代理工具能够识别服务器不可用并重定向客户端连接。此时实例保持运行状态,以便管理员可以在不关闭 MySQL 的情况下尝试解决问题。执行此退出操作后,成员在组视图中的状态为 ERROR
  • ABORT_SERVER:关闭 MySQL Server,是从 MySQL 8.0.12 到 MySQL 8.0.15 的默认值。执行此退出操作后,该成员将从组视图中删除。

由于以上操作是在重新加入组失败后进行,故都需要管理员介入进行后续处理。退出操作只影响客户端是否仍然可以读取无法重新加入组的 MySQL Server 上的数据,以及是否保持 MySQL Server 继续运行。

出现以下情况会执行指定的退出操作:

  • Applier error
  • Distributed recovery not possible
  • Group configuration change error
  • Primary election error
  • Unreachable majority timeout
  • Member expelled from group
  • Out of auto-rejoin attempts

下表总结了故障和退出操作:

Failure situationGroup Replication started with START GROUP_REPLICATIONGroup Replication started with group_replication_start_on_boot =ON
Member fails local configuration check

Mismatch between joining member and group configuration
super_read_only and offline_mode unchanged

MySQL continues running

Set super_read_only=ON at startup to prevent updates
super_read_only and offline_mode unchanged

MySQL continues running

Set super_read_only=ON at startup to prevent updates (Important)
Applier error on member

Distributed recovery no possiblet

Group configuration change errort

Primary election error

Unreachable majority timeout

Member expelled from group

Out of auto-rejoin attempts
super_read_only set to ON

OR

offline_mode and super_read_only set to ON

OR

MySQL shuts down
super_read_only set to ON

OR

offline_mode and super_read_only set to ON

OR

MySQL shuts down

Handling a Network Partition and Loss of Quorum

除了事务,组成员身份更改和一些保持组一致的内部消息也都需要达成共识,这要求大多数组成员就给定的决定达成一致,当大多数组成员丢失时,组复制将停止运行。

对于单主模式,在发生网络分区(Network Partition)时,主节点有可能比其他成员多一些事务,如果在新组中排除了此主节点,则这些事务可能会丢失。此时该主节点加入组就会报 This member has more executed transactions than those present in the group 错误消息。可以设置参数 group_replication_unreachable_majority_timeout 避免这种情况。

Detecting Partitions

正常情况下,每个组成员的表 performance_schema.replication_group_members 的内容都是一样的,状态都是 ONLINE。如果存在网络分区,则此表中失联成员的状态变为 UNREACHABLE

假设复制组有 5 个组成员,开始运行正常,在 S1 上查询状态:

mysql> SELECT MEMBER_ID,MEMBER_STATE, MEMBER_ROLE FROM performance_schema.replication_group_members;
+--------------------------------------+--------------+-------------+
| MEMBER_ID                            | MEMBER_STATE | MEMBER_ROLE |
+--------------------------------------+--------------+-------------+
| 1999b9fb-4aaf-11e6-bb54-28b2bd168d07 | ONLINE       | SECONDARY   |
| 199b2df7-4aaf-11e6-bb16-28b2bd168d07 | ONLINE       | PRIMARY     |
| 199bb88e-4aaf-11e6-babe-28b2bd168d07 | ONLINE       | SECONDARY   |
| 19ab72fc-4aaf-11e6-bb51-28b2bd168d07 | ONLINE       | SECONDARY   |
| 19b33846-4aaf-11e6-ba81-28b2bd168d07 | ONLINE       | SECONDARY   |
+--------------------------------------+--------------+-------------+

出现故障后,只有 S1 和 S2 这2 个成员处于 ONLINE 状态,如下图:

Losing Quorum

再在 S1 上查询状态:

mysql> SELECT MEMBER_ID,MEMBER_STATE FROM performance_schema.replication_group_members;
+--------------------------------------+--------------+
| MEMBER_ID                            | MEMBER_STATE |
+--------------------------------------+--------------+
| 1999b9fb-4aaf-11e6-bb54-28b2bd168d07 | UNREACHABLE  |
| 199b2df7-4aaf-11e6-bb16-28b2bd168d07 | ONLINE       |
| 199bb88e-4aaf-11e6-babe-28b2bd168d07 | ONLINE       |
| 19ab72fc-4aaf-11e6-bb51-28b2bd168d07 | UNREACHABLE  |
| 19b33846-4aaf-11e6-ba81-28b2bd168d07 | UNREACHABLE  |
+--------------------------------------+--------------+

由于现在组内大多数成员都处于 UNREACHABLE 状态,组复制将停止运行。此时可以先停止 S1 和 S2 的组复制,解决 S3,S4 和 S5 的故障,再启动组复制。如果故障短时间无法解决,可以配置 S1 和 S2 继续运行。

Unblocking a Partition

上面的示例中,只有 S1 和 S2 为 ONLINE ,但无法对外提供服务。此时可以参考:Restarting a Groupopen in new window,引导重建组,也可以使用参数 group_replication_force_members 强制指定新的成员身份配置,以便快速恢复服务。需要注意的是,使用 group_replication_force_members 应被视为最后的补救措施,必须小心使用,只能用于多数成员失败的场景。如果误用,可能会出现裂脑或整个系统不可用。

Forcing a New Membership

当强制指定新的成员身份配置时,必须确保处于 UNREACHABLE 状态的成员(S3,S4 和 S5)已停止运行。因为如果 S3,S4 和 S5 不可达(如断网)但是 MySQL 实例可用,则它们可能已经形成了自己的网络分区(它们是 5 个中的 3 个,占大多数)。这种情况下强制使用 S1 和 S2 的组成员列表可能会产生裂脑情况。因此,在强制指定新的成员身份配置前,要确保处于 UNREACHABLE 状态的 MySQL Server 已关闭,然后再继续执行。

  1. 首先获取 S1 和 S2 的组复制通信地址:
mysql> SELECT @@group_replication_local_address;
  1. 在 S1 上强制指定新的成员身份配置:
mysql> SET GLOBAL group_replication_force_members="s1:33061,s2:33061";
  1. 在 S1 和 S2 上查看成员状态:

S1:

mysql> SELECT MEMBER_ID,MEMBER_STATE FROM performance_schema.replication_group_members;
+--------------------------------------+--------------+
| MEMBER_ID                            | MEMBER_STATE |
+--------------------------------------+--------------+
| b5ffe505-4ab6-11e6-b04b-28b2bd168d07 | ONLINE       |
| b60907e7-4ab6-11e6-afb7-28b2bd168d07 | ONLINE       |
+--------------------------------------+--------------+

S2:

mysql> SELECT MEMBER_ID,MEMBER_STATE FROM performance_schema.replication_group_members;
+--------------------------------------+--------------+
| MEMBER_ID                            | MEMBER_STATE |
+--------------------------------------+--------------+
| b5ffe505-4ab6-11e6-b04b-28b2bd168d07 | ONLINE       |
| b60907e7-4ab6-11e6-afb7-28b2bd168d07 | ONLINE       |
+--------------------------------------+--------------+
  1. 清除参数:
mysql> SET GLOBAL group_replication_force_members="";

参数 group_replication_force_members 必须为空才能执行 START GROUP_REPLICATION 语句。

Monitoring Group Replication Memory Usage

从 MySQL 8.0.30 开始,性能模式(Performance Schema)提供了用于对组复制内存使用情况进行性能监控的工具。

[(none)]> SELECT NAME,ENABLED FROM performance_schema.setup_instruments
          WHERE NAME LIKE 'memory/group_rpl/%';
+-------------------------------------------------------------------+---------+
| NAME                                                              | ENABLED |
+-------------------------------------------------------------------+---------+
| memory/group_rpl/write_set_encoded                                | YES     |
| memory/group_rpl/certification_data                               | YES     |
| memory/group_rpl/certification_data_gc                            | YES     |
| memory/group_rpl/certification_info                               | YES     |
| memory/group_rpl/transaction_data                                 | YES     |
| memory/group_rpl/sql_service_command_data                         | YES     |
| memory/group_rpl/mysql_thread_queued_task                         | YES     |
| memory/group_rpl/message_service_queue                            | YES     |
| memory/group_rpl/message_service_received_message                 | YES     |
| memory/group_rpl/group_member_info                                | YES     |
| memory/group_rpl/consistent_members_that_must_prepare_transaction | YES     |
| memory/group_rpl/consistent_transactions                          | YES     |
| memory/group_rpl/consistent_transactions_prepared                 | YES     |
| memory/group_rpl/consistent_transactions_waiting                  | YES     |
| memory/group_rpl/consistent_transactions_delayed_view_change      | YES     |
| memory/group_rpl/GCS_XCom::xcom_cache                             | YES     |
| memory/group_rpl/Gcs_message_data::m_buffer                       | YES     |
+-------------------------------------------------------------------+---------+
17 rows in set (0.01 sec)

Enabling or Disabling Group Replication Instrumentation

启用性能监控工具:

UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' 
WHERE NAME LIKE 'memory/group_rpl/%';

禁用性能监控工具:

UPDATE performance_schema.setup_instruments SET ENABLED = 'NO' 
WHERE NAME LIKE 'memory/group_rpl/%';

在参数文件中指定,以便在启动 MySQL Server 的时候启用:

[mysqld]
performance-schema-instrument='memory/group_rpl/%=ON'

在参数文件中指定,以便在启动 MySQL Server 的时候禁用:

[mysqld]
performance-schema-instrument='memory/group_rpl/%=OFF'

Example Queries

查询表 performance_schema.memory_summary_global_by_event_name 获取某个事件的内存:

[(none)]> SELECT * FROM performance_schema.memory_summary_global_by_event_name
          WHERE EVENT_NAME = 'memory/group_rpl/write_set_encoded'\G
*************************** 1. row ***************************
                  EVENT_NAME: memory/group_rpl/write_set_encoded
                 COUNT_ALLOC: 0
                  COUNT_FREE: 0
   SUM_NUMBER_OF_BYTES_ALLOC: 0
    SUM_NUMBER_OF_BYTES_FREE: 0
              LOW_COUNT_USED: 0
          CURRENT_COUNT_USED: 0
             HIGH_COUNT_USED: 0
    LOW_NUMBER_OF_BYTES_USED: 0
CURRENT_NUMBER_OF_BYTES_USED: 0
   HIGH_NUMBER_OF_BYTES_USED: 0
1 row in set (0.00 sec)
Memory Used to Capture Transactions

用于捕获事务的内存是 write_set_encodedwrite_set_extractionLog_event 值的和:

mysql> SELECT * FROM (
         SELECT
           (CASE
              WHEN EVENT_NAME LIKE 'memory/group_rpl/write_set_encoded'
              THEN 'memory/group_rpl/memory_gr'
              WHEN EVENT_NAME = 'memory/sql/write_set_extraction'
              THEN 'memory/group_rpl/memory_gr'
              WHEN EVENT_NAME = 'memory/sql/Log_event'
              THEN 'memory/group_rpl/memory_gr'
              ELSE 'memory_gr_rest'
           END) AS EVENT_NAME, SUM(COUNT_ALLOC), SUM(COUNT_FREE),
         SUM(SUM_NUMBER_OF_BYTES_ALLOC),
         SUM(SUM_NUMBER_OF_BYTES_FREE), SUM(LOW_COUNT_USED),
         SUM(CURRENT_COUNT_USED), SUM(HIGH_COUNT_USED),
         SUM(LOW_NUMBER_OF_BYTES_USED), SUM(CURRENT_NUMBER_OF_BYTES_USED),
         SUM(HIGH_NUMBER_OF_BYTES_USED)
       FROM performance_schema.memory_summary_global_by_event_name
       GROUP BY (CASE
                    WHEN EVENT_NAME LIKE 'memory/group_rpl/write_set_encoded'
                    THEN 'memory/group_rpl/memory_gr'
                    WHEN EVENT_NAME = 'memory/sql/write_set_extraction'
                    THEN 'memory/group_rpl/memory_gr'
                    WHEN EVENT_NAME = 'memory/sql/Log_event'
                    THEN 'memory/group_rpl/memory_gr'
                    ELSE 'memory_gr_rest'
                  END)
       ) f
       WHERE f.EVENT_NAME != 'memory_gr_rest'\G
*************************** 1. row ***************************
                       EVENT_NAME: memory/group_rpl/memory_gr
                 SUM(COUNT_ALLOC): 130
                  SUM(COUNT_FREE): 126
   SUM(SUM_NUMBER_OF_BYTES_ALLOC): 92839
    SUM(SUM_NUMBER_OF_BYTES_FREE): 91244
              SUM(LOW_COUNT_USED): 0
          SUM(CURRENT_COUNT_USED): 4
             SUM(HIGH_COUNT_USED): 23
    SUM(LOW_NUMBER_OF_BYTES_USED): 0
SUM(CURRENT_NUMBER_OF_BYTES_USED): 1595
   SUM(HIGH_NUMBER_OF_BYTES_USED): 19199
1 row in set (0.01 sec)
Memory Used to Broadcast Transactions

用于广播事务的内存是 Gcs_message_data::m_buffertransaction_dataGCS_XCom::xcom_cache 值的和:

mysql> SELECT * FROM (
         SELECT
           (CASE
              WHEN EVENT_NAME =  'memory/group_rpl/Gcs_message_data::m_buffer'
              THEN 'memory/group_rpl/memory_gr'
              WHEN EVENT_NAME = 'memory/group_rpl/GCS_XCom::xcom_cache'
              THEN 'memory/group_rpl/memory_gr'
              WHEN EVENT_NAME = 'memory/group_rpl/transaction_data'
              THEN 'memory/group_rpl/memory_gr'
              ELSE 'memory_gr_rest'
           END) AS EVENT_NAME, SUM(COUNT_ALLOC), SUM(COUNT_FREE),
           SUM(SUM_NUMBER_OF_BYTES_ALLOC),
           SUM(SUM_NUMBER_OF_BYTES_FREE), SUM(LOW_COUNT_USED),
           SUM(CURRENT_COUNT_USED), SUM(HIGH_COUNT_USED),
           SUM(LOW_NUMBER_OF_BYTES_USED), SUM(CURRENT_NUMBER_OF_BYTES_USED),
           SUM(HIGH_NUMBER_OF_BYTES_USED)
         FROM performance_schema.memory_summary_global_by_event_name
         GROUP BY (CASE
                     WHEN EVENT_NAME =  'memory/group_rpl/Gcs_message_data::m_buffer'
                     THEN 'memory/group_rpl/memory_gr'
                     WHEN EVENT_NAME = 'memory/group_rpl/GCS_XCom::xcom_cache'
                     THEN 'memory/group_rpl/memory_gr'
                     WHEN EVENT_NAME = 'memory/group_rpl/transaction_data'
                     THEN 'memory/group_rpl/memory_gr'
                     ELSE 'memory_gr_rest'
                   END)
       ) f
       WHERE f.EVENT_NAME != 'memory_gr_rest'\G
*************************** 1. row ***************************
                       EVENT_NAME: memory/group_rpl/memory_gr
                 SUM(COUNT_ALLOC): 3848
                  SUM(COUNT_FREE): 1548
   SUM(SUM_NUMBER_OF_BYTES_ALLOC): 1245567
    SUM(SUM_NUMBER_OF_BYTES_FREE): 323127
              SUM(LOW_COUNT_USED): 0
          SUM(CURRENT_COUNT_USED): 2300
             SUM(HIGH_COUNT_USED): 2307
    SUM(LOW_NUMBER_OF_BYTES_USED): 0
SUM(CURRENT_NUMBER_OF_BYTES_USED): 922440
   SUM(HIGH_NUMBER_OF_BYTES_USED): 925642
1 row in set (0.01 sec)
Total Memory Used in Group Replication

用于组复制的总内存:

mysql> SELECT * FROM (
        SELECT
          (CASE
             WHEN EVENT_NAME LIKE 'memory/group_rpl/%'
             THEN 'memory/group_rpl/memory_gr'
             ELSE 'memory_gr_rest'
           END) AS EVENT_NAME, SUM(COUNT_ALLOC), SUM(COUNT_FREE),
           SUM(SUM_NUMBER_OF_BYTES_ALLOC),
           SUM(SUM_NUMBER_OF_BYTES_FREE), SUM(LOW_COUNT_USED),
           SUM(CURRENT_COUNT_USED), SUM(HIGH_COUNT_USED),
           SUM(LOW_NUMBER_OF_BYTES_USED), SUM(CURRENT_NUMBER_OF_BYTES_USED),
           SUM(HIGH_NUMBER_OF_BYTES_USED)
        FROM performance_schema.memory_summary_global_by_event_name
        GROUP BY (CASE
                    WHEN EVENT_NAME LIKE 'memory/group_rpl/%'
                    THEN 'memory/group_rpl/memory_gr'
                    ELSE 'memory_gr_rest'
                  END)
       ) f
       WHERE f.EVENT_NAME != 'memory_gr_rest'\G
*************************** 1. row ***************************
                       EVENT_NAME: memory/group_rpl/memory_gr
                 SUM(COUNT_ALLOC): 4238
                  SUM(COUNT_FREE): 1815
   SUM(SUM_NUMBER_OF_BYTES_ALLOC): 1332908
    SUM(SUM_NUMBER_OF_BYTES_FREE): 363209
              SUM(LOW_COUNT_USED): 0
          SUM(CURRENT_COUNT_USED): 2423
             SUM(HIGH_COUNT_USED): 2449
    SUM(LOW_NUMBER_OF_BYTES_USED): 0
SUM(CURRENT_NUMBER_OF_BYTES_USED): 969699
   SUM(HIGH_NUMBER_OF_BYTES_USED): 974981
1 row in set (0.01 sec)
Memory Used in Certification

用于认证的内存是 certification_datacertification_data_gccertification_info 值的和:

mysql> SELECT * FROM (
         SELECT
           (CASE
              WHEN EVENT_NAME = 'memory/group_rpl/certification_data'
              THEN 'memory/group_rpl/certification'
              WHEN EVENT_NAME = 'memory/group_rpl/certification_data_gc'
              THEN 'memory/group_rpl/certification'
              WHEN EVENT_NAME = 'memory/group_rpl/certification_info'
              THEN 'memory/group_rpl/certification'
              ELSE 'memory_gr_rest'
            END) AS EVENT_NAME, SUM(COUNT_ALLOC), SUM(COUNT_FREE),
            SUM(SUM_NUMBER_OF_BYTES_ALLOC),
            SUM(SUM_NUMBER_OF_BYTES_FREE), SUM(LOW_COUNT_USED),
            SUM(CURRENT_COUNT_USED), SUM(HIGH_COUNT_USED),
            SUM(LOW_NUMBER_OF_BYTES_USED), SUM(CURRENT_NUMBER_OF_BYTES_USED),
            SUM(HIGH_NUMBER_OF_BYTES_USED)
         FROM performance_schema.memory_summary_global_by_event_name
         GROUP BY (CASE
                     WHEN EVENT_NAME = 'memory/group_rpl/certification_data'
                     THEN 'memory/group_rpl/certification'
                     WHEN EVENT_NAME = 'memory/group_rpl/certification_data_gc'
                     THEN 'memory/group_rpl/certification'
                     WHEN EVENT_NAME = 'memory/group_rpl/certification_info'
                     THEN 'memory/group_rpl/certification'
                     ELSE 'memory_gr_rest'
                  END)
       ) f
       WHERE f.EVENT_NAME != 'memory_gr_rest'\G
*************************** 1. row ***************************
                       EVENT_NAME: memory/group_rpl/certification
                 SUM(COUNT_ALLOC): 39
                  SUM(COUNT_FREE): 39
   SUM(SUM_NUMBER_OF_BYTES_ALLOC): 2688
    SUM(SUM_NUMBER_OF_BYTES_FREE): 2688
              SUM(LOW_COUNT_USED): 0
          SUM(CURRENT_COUNT_USED): 0
             SUM(HIGH_COUNT_USED): 7
    SUM(LOW_NUMBER_OF_BYTES_USED): 0
SUM(CURRENT_NUMBER_OF_BYTES_USED): 0
   SUM(HIGH_NUMBER_OF_BYTES_USED): 488
1 row in set (0.00 sec)
Memory Used in Replication Pipeline

用于复制管道的内存是 certification_datatransaction_data 值的和:

mysql> SELECT * FROM (
         SELECT
           (CASE
              WHEN EVENT_NAME LIKE 'memory/group_rpl/certification_data'
              THEN 'memory/group_rpl/pipeline'
              WHEN EVENT_NAME LIKE 'memory/group_rpl/transaction_data'
              THEN 'memory/group_rpl/pipeline'
              ELSE 'memory_gr_rest'
            END) AS EVENT_NAME, SUM(COUNT_ALLOC), SUM(COUNT_FREE),
            SUM(SUM_NUMBER_OF_BYTES_ALLOC),
            SUM(SUM_NUMBER_OF_BYTES_FREE), SUM(LOW_COUNT_USED),
            SUM(CURRENT_COUNT_USED), SUM(HIGH_COUNT_USED),
            SUM(LOW_NUMBER_OF_BYTES_USED), SUM(CURRENT_NUMBER_OF_BYTES_USED),
            SUM(HIGH_NUMBER_OF_BYTES_USED)
          FROM performance_schema.memory_summary_global_by_event_name
          GROUP BY (CASE
                     WHEN EVENT_NAME LIKE 'memory/group_rpl/certification_data'
                     THEN 'memory/group_rpl/pipeline'
                     WHEN EVENT_NAME LIKE 'memory/group_rpl/transaction_data'
                     THEN 'memory/group_rpl/pipeline'
                     ELSE 'memory_gr_rest'
                   END)
       ) f
       WHERE f.EVENT_NAME != 'memory_gr_rest'\G
*************************** 1. row ***************************
                       EVENT_NAME: memory/group_rpl/pipeline
                 SUM(COUNT_ALLOC): 21
                  SUM(COUNT_FREE): 21
   SUM(SUM_NUMBER_OF_BYTES_ALLOC): 2025
    SUM(SUM_NUMBER_OF_BYTES_FREE): 2025
              SUM(LOW_COUNT_USED): 0
          SUM(CURRENT_COUNT_USED): 0
             SUM(HIGH_COUNT_USED): 4
    SUM(LOW_NUMBER_OF_BYTES_USED): 0
SUM(CURRENT_NUMBER_OF_BYTES_USED): 0
   SUM(HIGH_NUMBER_OF_BYTES_USED): 407
1 row in set (0.01 sec)
Memory Used in Consistency

用于事务一致性保证的内存是 consistent_members_that_must_prepare_transactionconsistent_transactionsconsistent_transactions_preparedconsistent_transactions_waitingconsistent_transactions_delayed_view_change 值的和:

mysql> SELECT * FROM (
         SELECT
           (CASE
              WHEN EVENT_NAME = 'memory/group_rpl/consistent_members_that_must_prepare_transaction'
              THEN 'memory/group_rpl/consistency'
              WHEN EVENT_NAME = 'memory/group_rpl/consistent_transactions'
              THEN 'memory/group_rpl/consistency'
              WHEN EVENT_NAME = 'memory/group_rpl/consistent_transactions_prepared'
              THEN 'memory/group_rpl/consistency'
              WHEN EVENT_NAME = 'memory/group_rpl/consistent_transactions_waiting'
              THEN 'memory/group_rpl/consistency'
              WHEN EVENT_NAME = 'memory/group_rpl/consistent_transactions_delayed_view_change'
              THEN 'memory/group_rpl/consistency'
              ELSE 'memory_gr_rest'
            END) AS EVENT_NAME, SUM(COUNT_ALLOC), SUM(COUNT_FREE),
           SUM(SUM_NUMBER_OF_BYTES_ALLOC),
           SUM(SUM_NUMBER_OF_BYTES_FREE), SUM(LOW_COUNT_USED),
           SUM(CURRENT_COUNT_USED), SUM(HIGH_COUNT_USED),
           SUM(LOW_NUMBER_OF_BYTES_USED), SUM(CURRENT_NUMBER_OF_BYTES_USED),
           SUM(HIGH_NUMBER_OF_BYTES_USED)
         FROM performance_schema.memory_summary_global_by_event_name
         GROUP BY (CASE
                     WHEN EVENT_NAME = 'memory/group_rpl/consistent_members_that_must_prepare_transaction'
                     THEN 'memory/group_rpl/consistency'
                     WHEN EVENT_NAME = 'memory/group_rpl/consistent_transactions'
                     THEN 'memory/group_rpl/consistency'
                     WHEN EVENT_NAME = 'memory/group_rpl/consistent_transactions_prepared'
                     THEN 'memory/group_rpl/consistency'
                     WHEN EVENT_NAME = 'memory/group_rpl/consistent_transactions_waiting'
                     THEN 'memory/group_rpl/consistency'
                     WHEN EVENT_NAME = 'memory/group_rpl/consistent_transactions_delayed_view_change'
                     THEN 'memory/group_rpl/consistency'
                     ELSE 'memory_gr_rest'
                   END)
       ) f
       WHERE f.EVENT_NAME != 'memory_gr_rest'\G
*************************** 1. row ***************************
                       EVENT_NAME: memory/group_rpl/consistency
                 SUM(COUNT_ALLOC): 0
                  SUM(COUNT_FREE): 0
   SUM(SUM_NUMBER_OF_BYTES_ALLOC): 0
    SUM(SUM_NUMBER_OF_BYTES_FREE): 0
              SUM(LOW_COUNT_USED): 0
          SUM(CURRENT_COUNT_USED): 0
             SUM(HIGH_COUNT_USED): 0
    SUM(LOW_NUMBER_OF_BYTES_USED): 0
SUM(CURRENT_NUMBER_OF_BYTES_USED): 0
   SUM(HIGH_NUMBER_OF_BYTES_USED): 0
1 row in set (0.00 sec)
Memory Used in Delivery Message Service

用于传递消息服务(仅适用于接收到的数据)的内存是 message_service_received_messagemessage_service_queue 值的和:

mysql> SELECT * FROM (
          SELECT
            (CASE
               WHEN EVENT_NAME = 'memory/group_rpl/message_service_received_message'
               THEN 'memory/group_rpl/message_service'
               WHEN EVENT_NAME = 'memory/group_rpl/message_service_queue'
               THEN 'memory/group_rpl/message_service'
               ELSE 'memory_gr_rest'
             END) AS EVENT_NAME, SUM(COUNT_ALLOC), SUM(COUNT_FREE),
            SUM(SUM_NUMBER_OF_BYTES_ALLOC),
            SUM(SUM_NUMBER_OF_BYTES_FREE), SUM(LOW_COUNT_USED),
            SUM(CURRENT_COUNT_USED), SUM(HIGH_COUNT_USED),
            SUM(LOW_NUMBER_OF_BYTES_USED), SUM(CURRENT_NUMBER_OF_BYTES_USED),
            SUM(HIGH_NUMBER_OF_BYTES_USED)
          FROM performance_schema.memory_summary_global_by_event_name
          GROUP BY (CASE
                      WHEN EVENT_NAME = 'memory/group_rpl/message_service_received_message'
                      THEN 'memory/group_rpl/message_service'
                      WHEN EVENT_NAME = 'memory/group_rpl/message_service_queue'
                      THEN 'memory/group_rpl/message_service'
                      ELSE 'memory_gr_rest'
                    END)
       ) f
       WHERE f.EVENT_NAME != 'memory_gr_rest'\G
*************************** 1. row ***************************
                       EVENT_NAME: memory/group_rpl/message_service
                 SUM(COUNT_ALLOC): 0
                  SUM(COUNT_FREE): 0
   SUM(SUM_NUMBER_OF_BYTES_ALLOC): 0
    SUM(SUM_NUMBER_OF_BYTES_FREE): 0
              SUM(LOW_COUNT_USED): 0
          SUM(CURRENT_COUNT_USED): 0
             SUM(HIGH_COUNT_USED): 0
    SUM(LOW_NUMBER_OF_BYTES_USED): 0
SUM(CURRENT_NUMBER_OF_BYTES_USED): 0
   SUM(HIGH_NUMBER_OF_BYTES_USED): 0
1 row in set (0.00 sec)
Memory Used to Broadcast and Receive Transactions

用于网络广播和接收事务的内存是 wGcs_message_data::m_bufferGCS_XCom::xcom_cache 值的和:

mysql> SELECT * FROM (
         SELECT
           (CASE
              WHEN EVENT_NAME = 'memory/group_rpl/Gcs_message_data::m_buffer'
              THEN 'memory/group_rpl/memory_gr'
              WHEN EVENT_NAME = 'memory/group_rpl/GCS_XCom::xcom_cache'
              THEN 'memory/group_rpl/memory_gr'
              ELSE 'memory_gr_rest'
            END) AS EVENT_NAME, SUM(COUNT_ALLOC), SUM(COUNT_FREE),
           SUM(SUM_NUMBER_OF_BYTES_ALLOC),
           SUM(SUM_NUMBER_OF_BYTES_FREE), SUM(LOW_COUNT_USED),
           SUM(CURRENT_COUNT_USED), SUM(HIGH_COUNT_USED),
           SUM(LOW_NUMBER_OF_BYTES_USED), SUM(CURRENT_NUMBER_OF_BYTES_USED),
           SUM(HIGH_NUMBER_OF_BYTES_USED)
         FROM performance_schema.memory_summary_global_by_event_name
         GROUP BY (CASE
                     WHEN EVENT_NAME = 'memory/group_rpl/Gcs_message_data::m_buffer'
                     THEN 'memory/group_rpl/memory_gr'
                     WHEN EVENT_NAME = 'memory/group_rpl/GCS_XCom::xcom_cache'
                     THEN 'memory/group_rpl/memory_gr'
                     ELSE 'memory_gr_rest'
                   END)
       ) f
       WHERE f.EVENT_NAME != 'memory_gr_rest'\G
*************************** 1. row ***************************
                       EVENT_NAME: memory/group_rpl/memory_gr
                 SUM(COUNT_ALLOC): 5278
                  SUM(COUNT_FREE): 2114
   SUM(SUM_NUMBER_OF_BYTES_ALLOC): 1713235
    SUM(SUM_NUMBER_OF_BYTES_FREE): 441465
              SUM(LOW_COUNT_USED): 0
          SUM(CURRENT_COUNT_USED): 3164
             SUM(HIGH_COUNT_USED): 3168
    SUM(LOW_NUMBER_OF_BYTES_USED): 0
SUM(CURRENT_NUMBER_OF_BYTES_USED): 1271770
   SUM(HIGH_NUMBER_OF_BYTES_USED): 1274645
1 row in set (0.00 sec)
上次编辑于:
贡献者: stonebox