一.配置组复制模式
组复制可以以单主模式或多主模式运行,缺省采用单主模式。
单主模式下一个库可读写,其它的库只读。
多主模式下所有的库都可读写,现在还不支持部分读、部分写。
无论部署模式如何,组复制都不处理客户端故障转移,而必须由应用程序本身、连接器或中间件(如MySQL router)处理此问题。
多主模式容易造成数据不一致情况,谨慎使用。
1.1 单主模式
在此模式下,组中具有单一可读写的主服务器,通常是第一个引导组的服务器,所有其它成员都为只读。这种设置自动发生。当主服务器失败时,会自动选择新的主服务器,如下图所示:
image.png
当主库出现故障,有部分变动还未同步到其它节点,如上图(中),要等到这些变动在其它节点都应用了之后,才会推举一个为主库。
那么如何选择一个新的主库呢?这个就由 group_replication_member_weight系统变量控制。
主服务器可由下面查询确认:
mysql> select member_id, member_host, member_role from performance_schema.replication_group_members;
+--------------------------------------+-------------+-------------+
| member_id | member_host | member_role |
+--------------------------------------+-------------+-------------+
| 2b588106-e6b8-11ea-9823-000c295d5891 | 10.31.1.114 | SECONDARY |
| c258c587-d22c-11ea-a73e-000c293fa60d | 10.31.1.112 | PRIMARY |
| d0cdbe70-e6b7-11ea-ae64-000c297ccd64 | 10.31.1.113 | SECONDARY |
+--------------------------------------+-------------+-------------+
3 rows in set (0.00 sec)
1.2 多主模式
多主模式中所有服务器角色等价,各服务器都能读写。客户端故障转移如下图所示:
image.png
多主模式下部署组复制时,将进行以下检查:
如果事务在SERIALIZABLE隔离级别下执行,则在与组同步时其提交失败。
如果事务在具有级联外键约束的表上执行,则在与组同步时事务无法提交。
通过将选项group_replication_enforce_update_everywhere_checks设置为FALSE,可以禁用这些检查。单主模式下此选项必须设置为FALSE。
1.3 联机配置组复制模式
可以使用一组依赖于组操作协调器的函数在组复制运行时联机配置组,这些函数由版本8.0.13及更高版本中的组复制插件提供。为使协调器能够在正在运行的组上进行配置,所有成员必须MySQL 8.0.13或更高版本。
可以在任何成员上运行函数。操作运行时可以通过发出以下命令来检查其进度:
select event_name, work_completed, work_estimated from performance_schema.events_stages_current where event_name like "%stage/group_rpl%";
1.3.1 单主模式下修改主服务器
使用group_replication_set_as_primary() 函数更改单主组中的主服务器,对于多主组此功能无效。只有主库才允许写入,因此如果该成员上正在运行异步通道复制,则在异步通道复制停止之前不允许切换。通过发出以下命令传递要成为该组新主服务器成员的server_uuid:
把单主模式下的读写库由10.31.1.112 改为 10.31.1.113
mysql> select member_id, member_host, member_role from performance_schema.replication_group_members;
+--------------------------------------+-------------+-------------+
| member_id | member_host | member_role |
+--------------------------------------+-------------+-------------+
| 2b588106-e6b8-11ea-9823-000c295d5891 | 10.31.1.114 | SECONDARY |
| c258c587-d22c-11ea-a73e-000c293fa60d | 10.31.1.112 | PRIMARY |
| d0cdbe70-e6b7-11ea-ae64-000c297ccd64 | 10.31.1.113 | SECONDARY |
+--------------------------------------+-------------+-------------+
3 rows in set (0.00 sec)
mysql> select group_replication_set_as_primary('d0cdbe70-e6b7-11ea-ae64-000c297ccd64');
+--------------------------------------------------------------------------+
| group_replication_set_as_primary('d0cdbe70-e6b7-11ea-ae64-000c297ccd64') |
+--------------------------------------------------------------------------+
| Primary server switched to: d0cdbe70-e6b7-11ea-ae64-000c297ccd64 |
+--------------------------------------------------------------------------+
1 row in set (1.01 sec)
mysql> select member_id, member_host, member_role from performance_schema.replication_group_members;
+--------------------------------------+-------------+-------------+
| member_id | member_host | member_role |
+--------------------------------------+-------------+-------------+
| 2b588106-e6b8-11ea-9823-000c295d5891 | 10.31.1.114 | SECONDARY |
| c258c587-d22c-11ea-a73e-000c293fa60d | 10.31.1.112 | SECONDARY |
| d0cdbe70-e6b7-11ea-ae64-000c297ccd64 | 10.31.1.113 | PRIMARY |
+--------------------------------------+-------------+-------------+
3 rows in set (0.00 sec)
1.3.2 单主修改为多主
group_replication_switch_to_multi_primary_mode()函数用于单主改多主:
mysql> select member_id, member_host, member_role from performance_schema.replication_group_members;
+--------------------------------------+-------------+-------------+
| member_id | member_host | member_role |
+--------------------------------------+-------------+-------------+
| 2b588106-e6b8-11ea-9823-000c295d5891 | 10.31.1.114 | SECONDARY |
| c258c587-d22c-11ea-a73e-000c293fa60d | 10.31.1.112 | SECONDARY |
| d0cdbe70-e6b7-11ea-ae64-000c297ccd64 | 10.31.1.113 | PRIMARY |
+--------------------------------------+-------------+-------------+
3 rows in set (0.00 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 (1.00 sec)
mysql> select member_id, member_host, member_role from performance_schema.replication_group_members;
+--------------------------------------+-------------+-------------+
| member_id | member_host | member_role |
+--------------------------------------+-------------+-------------+
| 2b588106-e6b8-11ea-9823-000c295d5891 | 10.31.1.114 | PRIMARY |
| c258c587-d22c-11ea-a73e-000c293fa60d | 10.31.1.112 | PRIMARY |
| d0cdbe70-e6b7-11ea-ae64-000c297ccd64 | 10.31.1.113 | PRIMARY |
+--------------------------------------+-------------+-------------+
3 rows in set (0.00 sec)
1.3.3 多主修改为单主
group_replication_switch_to_single_primary_mode 函数
mysql> select member_id, member_host, member_role from performance_schema.replication_group_members;
+--------------------------------------+-------------+-------------+
| member_id | member_host | member_role |
+--------------------------------------+-------------+-------------+
| 2b588106-e6b8-11ea-9823-000c295d5891 | 10.31.1.114 | PRIMARY |
| c258c587-d22c-11ea-a73e-000c293fa60d | 10.31.1.112 | PRIMARY |
| d0cdbe70-e6b7-11ea-ae64-000c297ccd64 | 10.31.1.113 | PRIMARY |
+--------------------------------------+-------------+-------------+
3 rows in set (0.00 sec)
mysql>
mysql> select group_replication_switch_to_single_primary_mode();
+---------------------------------------------------+
| group_replication_switch_to_single_primary_mode() |
+---------------------------------------------------+
| Mode switched to single-primary successfully. |
+---------------------------------------------------+
1 row in set (1.01 sec)
mysql> select member_id, member_host, member_role from performance_schema.replication_group_members;
+--------------------------------------+-------------+-------------+
| member_id | member_host | member_role |
+--------------------------------------+-------------+-------------+
| 2b588106-e6b8-11ea-9823-000c295d5891 | 10.31.1.114 | PRIMARY |
| c258c587-d22c-11ea-a73e-000c293fa60d | 10.31.1.112 | SECONDARY |
| d0cdbe70-e6b7-11ea-ae64-000c297ccd64 | 10.31.1.113 | SECONDARY |
+--------------------------------------+-------------+-------------+
3 rows in set (0.00 sec)
1.4 配置并发写实例数
group_replication_get_write_concurrency()函数用于在运行时检查组的最大并行可写实例数。缺省值为10适用于LAN上运行的组,对于通过较慢网络上(如WAN)运行的组,增加此数量可以微调组复制的性能。
mysql> select group_replication_get_write_concurrency();
+-------------------------------------------+
| group_replication_get_write_concurrency() |
+-------------------------------------------+
| 10 |
+-------------------------------------------+
1 row in set (0.00 sec)
1.5 设置组的通信协议版本
从MySQL 8.0.16开始,组复制具有通信协议的概念。可以显式管理组复制通信协议版本,并将其设置为支持的最老的MySQL服务器版本。这使得组可以由不同MySQL服务器版本的成员组成,同时确保向后兼容性。该组的所有成员必须使用相同的通信协议版本,以便发送所有组成员都能理解的消息。
如果组的通信协议版本小于或等于X,则版本X的MySQL服务器加入到复制组并达到ONLINE状态。新成员加入复制组时,该组的现有成员会检查加入成员的通信协议版本。如果支持该版本,则将它加入该组并使用该组已宣布的通信协议。如果不支持通信协议版本,则将其从组中移除。
只有新成员的通信协议版本与该组的通信协议版本兼容时,它们才能加入。加入该组的具有不同通信协议版本的成员必须单独加入。例如:
一个MySQL Server 8.0.16实例可以成功加入使用通信协议版本5.7.24的组。
一个MySQL Server 5.7.24实例无法成功加入使用通信协议版本8.0.16的组。
两个MySQL Server 8.0.16实例无法同时加入使用通信协议版本5.7.24的组。
两个MySQL Server 8.0.16实例可以同时加入使用通信协议版本8.0.16的组。
虽然支持这种跨版本的,但是尽量不要使用
group_replication_get_communication_protocol()函数用于检查组使用的通信协议,该函数返回该组支持的最老的MySQL服务器版本。该组的所有现有成员都返回相同的通信协议版本。
mysql> select group_replication_get_communication_protocol();
+------------------------------------------------+
| group_replication_get_communication_protocol() |
+------------------------------------------------+
| 8.0.16 |
+------------------------------------------------+
1 row in set (0.00 sec)
二.保证数据一致性
2.1 组复制数据一致性简介
对分布式系统的一个重要需求是它能够提供一致性保证。就分布式一致性而言,无论是在正常还是失败修复的操作中,组复制始终是始终保持最终一致性。可以将影响数据一致性的事件分为两类:一是手动或由故障自动触发的控制操作;二是数据流。
2.1.1 只读保护
当有一个成员离开了复制组,这个时候它不能接受写的请求,否则会造成数据不一致。
为了保持一致性,可以在 STOP GROUP_REPLICATION,在服务器上启用超级只读模式。
mysql> select member_id, member_host, member_role from performance_schema.replication_group_members;
+--------------------------------------+-------------+-------------+
| member_id | member_host | member_role |
+--------------------------------------+-------------+-------------+
| 2b588106-e6b8-11ea-9823-000c295d5891 | 10.31.1.114 | PRIMARY |
| c258c587-d22c-11ea-a73e-000c293fa60d | 10.31.1.112 | SECONDARY |
| d0cdbe70-e6b7-11ea-ae64-000c297ccd64 | 10.31.1.113 | SECONDARY |
+--------------------------------------+-------------+-------------+
3 rows in set (0.00 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 (1.01 sec)
mysql>
mysql>
mysql>
mysql> select member_id, member_host, member_role from performance_schema.replication_group_members;
+--------------------------------------+-------------+-------------+
| member_id | member_host | member_role |
+--------------------------------------+-------------+-------------+
| 2b588106-e6b8-11ea-9823-000c295d5891 | 10.31.1.114 | PRIMARY |
| c258c587-d22c-11ea-a73e-000c293fa60d | 10.31.1.112 | PRIMARY |
| d0cdbe70-e6b7-11ea-ae64-000c297ccd64 | 10.31.1.113 | PRIMARY |
+--------------------------------------+-------------+-------------+
3 rows in set (0.00 sec)
mysql> show variables like '%read_only%';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| innodb_read_only | OFF |
| read_only | OFF |
| super_read_only | OFF |
| transaction_read_only | OFF |
+-----------------------+-------+
4 rows in set (0.01 sec)
mysql> stop group_replication;
Query OK, 0 rows affected (4.47 sec)
mysql> select member_id, member_host, member_role from performance_schema.replication_group_members;
+--------------------------------------+-------------+-------------+
| member_id | member_host | member_role |
+--------------------------------------+-------------+-------------+
| c258c587-d22c-11ea-a73e-000c293fa60d | 10.31.1.112 | |
+--------------------------------------+-------------+-------------+
1 row in set (0.00 sec)
mysql> show variables like '%read_only%';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| innodb_read_only | OFF |
| read_only | ON |
| super_read_only | ON |
| transaction_read_only | OFF |
+-----------------------+-------+
4 rows in set (0.00 sec)
mysql> start group_replication;
Query OK, 0 rows affected (3.91 sec)
mysql> show variables like '%read_only%';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| innodb_read_only | OFF |
| read_only | OFF |
| super_read_only | OFF |
| transaction_read_only | OFF |
+-----------------------+-------+
4 rows in set (0.01 sec)
mysql> select member_id, member_host, member_role from performance_schema.replication_group_members;
+--------------------------------------+-------------+-------------+
| member_id | member_host | member_role |
+--------------------------------------+-------------+-------------+
| 2b588106-e6b8-11ea-9823-000c295d5891 | 10.31.1.114 | PRIMARY |
| c258c587-d22c-11ea-a73e-000c293fa60d | 10.31.1.112 | PRIMARY |
| d0cdbe70-e6b7-11ea-ae64-000c297ccd64 | 10.31.1.113 | PRIMARY |
+--------------------------------------+-------------+-------------+
3 rows in set (0.00 sec)
2.1.2 主库故障转移
单主模式中,当主库发生故障,一个从库被提升为主库时,对于积压事务与新事务有两种可选的处理方式:
1)可以立即服务应用程序,无论复制积压的数量有多大;
2)在处理积压事务之前限制访问。
第一种方案中,系统将花费最少的时间在主库故障之后通过选择新主库来保护稳定的组成员资格,然后在应用旧主库积压的事务时立即允许数据访问。这种方式能够保证写入一致性,但可能读取到过时的数据。使用第二种方法,系统将在主库故障后保护稳定的组成员资格,并以与第一种方案相同的方式选择新主库。但在这种情况下,该组将等待新主库应用所有积压事务,之后才允许数据访问。故障转移所需的时间与积压大小成正比,在均衡的复制组中,积压应该很小。
MySQL 8.0.14之前采用可用性最大化策略(第一种方法),并且不可配。
MySQL 8.0.14及更高版本中可以使用group_replication_consistency变量配置组成员在主库故障转移期间提供的事务一致性保证级别。
2.2 一致性级别介绍
group_replication_consistency 有几个值:
EVENTUAL
BEFORE_ON_PRIMARY_FAILOVER
BEFORE
AFTER
BEFORE_AND_AFTER
2.2.1 EVENTUAL(缺省值)
读、写事务都直接执行,不等待之前复制积压的事务,也不等待其它组成员应用这些事务。这是MySQL 8.0.14之前的组复制行为。
2.2.2 BEFORE_ON_PRIMARY_FAILOVER
新的主库的读写事务要等新的主库应用了旧主库所有的日志之后,保证新的读写事务访问到的都是最新的数据。
2.2.3 BEFORE(读时同步)
在开始执行之前,事务将等待所有先前的事务完成。这可确保此事务将在最新的数据快照上执行,而不管在哪个成员上执行。此一致性级别涵盖BEFORE_ON_PRIMARY_FAILOVER提供的一致性保证。
2.2.4 AFTER(写时同步)
读写事务将等待其更改已应用于其它成员,对只读事务没有影响。此模式确保在本地成员上提交事务时,后续事务会读取最新值,而无论在哪个成员上执行。将此模式与主要只读操作的组一起使用,保证应用的读写事务在提交后随处可用。通过仅在读写事务上使用同步,减少了只读事务上的同步开销。此一致性级别涵盖BEFORE_ON_PRIMARY_FAILOVER提供的一致性保证。
2.2.5 BEFORE_AND_AFTER(读写时都同步)
此事务开始执行时等待:1)所有该事务先前的事务都执行完成;2)该事务的变更已应用于其它成员。这可确保:1)此事务将在最新的数据快照上执行;2)一旦此事务完成,所有后续事务都会读取到包含其更改的数据库状态,无论它们在哪个成员上执行。此一致性级别涵盖BEFORE_ON_PRIMARY_FAILOVER提供的一致性保证。
2.3 如何选择合适的一致性级别
场景1: 读多写少,不读取过期数据的情况下对读取进行负载平衡。选择AFTER。
场景2: 写多读少,不读取过期数据。选择BEFORE。
场景3:希望工作负载中的特定事务始终从组中读取最新数据。选择BEFORE。
场景4:复制组主要为只读,希望读写事务一旦提交就应用于任何地方,以便后续读取最新数据,并且不为只读事务产生同步开销。选择AFTER。
场景5:复制组主要为只读,希望读写事务始终从组中读取最新数据,并在提交后随处应用,以便后续读取最新数据,并且不为只读事务产生同步开销。选择BEFORE_AND_AFTER。
2.4 一致性级别范围
选择强制执行一致性级别的范围非常重要,如果将它们设置在全局范围内,一致性级别可能会对性能产生负面影响。可以在全局或会话级别设置group_replication_consistency系统变量:
-- 强制执行当前会话的一致性级别
set @@session.group_replication_consistency = 'before';
-- 强制执行所有会话的一致性级别
set @@global.group_replication_consistency = 'before';
场景6:只有一种更新,如设置对文档的访问权限,希望更改访问权限后,确保所有客户端都能看到正确的权限。其它更新没有强一致性要求。只需要在该事务上执行 set @@session.group_replication_consistency = 'after',其它事务使用缺省的EVENTUAL级别。
场景7:在场景6中描述的同一系统上,每天需要读取最新数据进行一些分析处理。只需要在该特定事务上执行 set @@ session.group_replication_consistency = 'before'。
作为原则,如果只有一些特性事务需要强一致性,就在会话级别设置group_replication_consistency。需要强调的一点是,所有事务在组复制中是完全排序的,因此即使发出以下命令仅设置当前会话的一致性级别为'AFTER':
set @@session.group_replication_consistency = 'after';
此事务也将等待其更改应用于所有组成员,即等待执行队列上的此事务和所有先前事务。在实践中,AFTER一致性级别将等待所有先前事务和当前事务在其它成员执行完。只能在状态为ONLINE的组成员上设置BEFORE、AFTER和BEFORE_AND_AFTER的一致性级别,尝试在其它状态的成员上使用它们会导致会话错误。一致性级别不是EVENTUAL的事务等待的最长时间由wait_timeout系统变量指定,缺省为8小时。如果超时,则抛出ER_GR_HOLD_WAIT_TIMEOUT错误。
2.5 一致性级别的影响
可以根据对复制组其它成员的影响,对一致性级别进行分类。
除了在事务流上排序之外,BEFORE一致性级别仅影响本地成员。
也就是说,它不需要与其它成员协调,也不会对其它事务产生影响,或者说BEFORE仅影响使用它的事务。
AFTER和BEFORE_AND_AFTER一致性级别对在其它成员上执行的并发事务具有副作用。
这两个一致性级别可以使其它成员事务陷入等待。
假设一个事务T2在具有EVENTUAL级别的成员M2上启动时,成员M1正在AFTER或BEFORE_AND_AFTER级别下执行一个事务T1,则T2必须等待T1在M2上提交后才能开始执行。
对于其它成员也是如此,即使它们具有EVENTUAL一致性级别。就是说设置AFTER和BEFORE_AND_AFTER会影响所有ONLINE成员。
参考文献: