7.1. 在一个组中兼容不同版本的成员
组复制根据与MGR插件绑定的MySQL Server版本进行版本控制。例如,如果一个成员运行在MySQL 5.7.26版本中,那么这就是MGR 插件的版本。使用如下语句可以在组成员中检查MySQL Server版本:
root@localhost : (none):35: > SELECT MEMBER_HOST,MEMBER_PORT,MEMBER_VERSION FROM performance_schema.replication_group_members;
+-------------+-------------+----------------+
| MEMBER_HOST | MEMBER_PORT | MEMBER_VERSION |
+-------------+-------------+----------------+
| node2 | 3306 | 8.0.17 |
| node3 | 3306 | 8.0.17 |
| node1 | 3306 | 8.0.17 |
+-------------+-------------+----------------+
3 rows in set (0.01 sec)
为了获得最佳的兼容性和性能,组中的所有成员建议运行相同版本的MySQL Server,也建议运行相同版本的组复制通讯协议。但是,在线升级组的过程中,为了最大化组的可用性,可能需要同时运行不同MySQL Server版本的成员。由于不同MySQL版本之间的一些特性可能有所不同(例如:较新的版本可能支持一些新功能而旧版本不支持,在较新的版本中删除了一些较旧的版本中支持的一些功能),在这种情况下新旧版本之间可能会遇到不兼容问题。在此情况下,如果将不同版本的MySQL Server配置到一个组中,则可能会导致依赖弃用特性的成员失败、也可能导致较旧的版本不支持新版本中的新特性而出现问题。为防止出现这些问题,组复制支持一些兼容性策略,使得一组运行在不同MySQL Server版本的组成员之间能够安全兼容。组成员应用这些策略来决定是否正常加入组、或以只读模式加入组、或不加入组,这具体取决于哪种选择可以保证新加入Server和组中的现有成员之间的操作安全。在对组执行滚动升级时,每个成员都需要先脱离组,然后升级到新版本,然后再次重新以新版本加入组,此时,组中的成员需要为新版本做对应的策略匹配(该策略可能与组成员升级之前应用的策略不同)。尝试加入组的成员所应用的兼容性策略如下:- 如果一个MySQL Server 的版本低于组中的现有组成员运行的最低版本,则该Server不允许加入组。
- 如果一个MySQL Server 的版本与组中现有成员所运行的最低版本相同,那么该Server通常会(也允许)加入组。
- 一个Server加入组后,如果它运行的MySQL Server版本高于组中现有组成员运行的最低版本,则该成员将保持只读模式(如果组运行在单主模式,新加入组的成员在任何情况下都默认为只读;如果组运行在多主模式下,则新加入组的成员可能处于读写模式)。
- 一个运行MySQL 5.7.x版本的Server不允许加入组。
- 一个运行MySQL 8.0.16版本的Server允许正常加入组(因为这里只考虑主要版本字符串8.0,与组中现有成员考虑的字符串8.0匹配)。
- 一个运行MySQL 8.0.17版本的Server允许加入组,但会保持只读模式(因为新加入的Server它也会同时考虑补丁版本17,即8.0.17,而组中的现有成员只考虑字符串8.0)。
7.1.1. 升级组成员
在线升级过程中,如果组处于单主模式,则在执行升级操作的成员脱机之后(执行升级操作的组成员需要脱机,未执行升级操作的组成员不需要脱机),组中未执行升级操作的组成员会根据"1.3.1 单主模式”中所述的选举策略选举一个新的主要节点。注意,如果要求主要节点始终保持不变(除非它本身正在执行升级),则必须先对所有其他的组成员执行升级,然后最后对主要节点执行升级(当升级主要节点时,由于主要节点脱机,所以组内会重新选举新的主要节点,等待其升级完成之后,如果你希望主要节点还是原来的成员,则,此时可以使用group_replication_set_as_primary() UDF将其重新指定为主要节点)。
如果组处于多主模式,则在升级过程中可以正常执行写操作的组成员将越变越少,因为升级后的成员将被置为只读模式并重新加入组(这是为了防止新版本的特性在旧版本中不能被成功复制)。如果所有成员都升级到MySQL 8.0.17及其更高的版本时,则它们都会自动恢复到读写模式。但对于较早的版本,在升级完成之后,必须手动将每个组成员上的系统变量super_read_only和read_only系设置为OFF(设置读写模式)以将其作为主要节点。
-
注意:对于新旧版本的比较而言,从MySQL 8.0.17开始,比较时需要考虑到次要版本号,而对于8.0.16及其之前的版本,做版本比较时只考虑主要版本号。
如果在组成员升级过程中需要做升级回滚的成员,或者需要为组新增一个成员,则,在紧急情况下,可以允许一个MySQL Server版本低于组中最低版本的成员加入组(使用组复制系统变量group_replication_allow_local_lower_version_join来覆盖正常的兼容性策略。需要注意,将该系统变量设置为ON并不会使新成员与组兼容。因此,该系统变量只能在特定情况下谨慎使用。有关更多预防措施,请参见"8、组复制系统变量"中的group_replication_allow_local_lower_version_join系统变量的描述)。
7.1.2. 组复制通信协议版本
组复制使用的组通讯协议版本号与组成员的MySQL Server版本号不一定完全一致(例如:MySQL Server 8.0.17使用的通讯协议版本为8.0.16,而不是8.0.17,该协议版本代表着该组当前支持的最低的MySQL Server版本,MySQL 5.7.14版本支持压缩消息,MySQL 8.0.16版本支持消息分段)。要查看组的通信协议版本,可以在组中任意成员执行如下语句进行查询:
root@localhost : (none):35: > SELECT group_replication_get_communication_protocol();
+------------------------------------------------+
| group_replication_get_communication_protocol() |
+------------------------------------------------+
| 8.0.16 |
+------------------------------------------------+
1 row in set (0.00 sec)
注意:group_replication_get_communication_protocol() UDF返回的组通讯协议版本表示组当前支持的最低MySQL Server版本,当使用group_replication_set_communication_protocol() UDF执行组通讯协议配置时,最终生效的值可能与传递给该函数的值不相同,例如:传递给该函数的值为8.0.17,最终生效的是8.0.16。
当将一个复制组的所有成员升级到一个新的MySQL Server版本时,组复制通信协议版本不会自动升级,以防仍然需要允许早期版本的成员加入组。如果在升级到新版本之后,确定不需要支持老版本的Server,且也希望使用新版本的一些附加功能,则可以在升级完成之后使用group_replication_set_communication_protocol() UDF函数升级组通信协议版本(在组中任意一个成员中执行修改组通讯协议版本操作即可)。有关更多信息,请参见"4.1.4 设置组的通信协议版本”。7.2. 组复制离线升级
要对组复制执行离线升级,需要将组中的所有成员逐一从组中剔除,保证组离线之后,再对各个Server执行版本升级,升级完成所有Server版本之后,正常重新启动该组。在多主模式的组中,可以按任何顺序剔除与关闭组成员。在单主模式的组中,需要先逐个剔除与关闭只读成员的Server,最后关闭主要节点(写节点)成员的Server。有关如何从组中剔除成员与关闭MySQL Server,请参见"7.3.2. 升级组复制成员”。
一个组中的所有成员都成功升级到新版本之后,重新启动该组时,这些成员之间将会使用新版本的组复制通讯协议进行连接。如果此时需要允许早期版本的Server加入组,则可以使用group_replication_set_communication_protocol() UDF来将组通信协议版本降级到旧版本支持的通讯协议版本号(或者直接填写旧版本的MySQL Server版本号)
7.3. 组复制在线升级
当你想要对一个正在运行中的组执行版本升级、且必须保证组在升级过程中同时对外提供服务,则,你需要采用正确的升级方法。本节将针对如何执行在线升级组的一些方法进行介绍。
7.3.1. 在线升级注意事项
在线升级一个组时,需要考虑以下几点:
-
无论以何种方式升级组成员,在组成员升级完成且重新加入组之前,禁止对成员执行任何写操作。
-
当一个成员停止组复制时,系统变量super_read_only将被自动设置为on,但是这个更改值不是持久化的,即,重启Server之后该更改值会被配置文件中的设置或者默认值覆盖。
-
当MySQL 5.7.22或MySQL 8.0.11版本的成员试图加入一个运行MySQL 5.7.21或更低版本的组时会失败,因为MySQL 5.7.21版本不会发送系统变量lower_case_table_names的值
7.3.2. 升级组复制成员
本节介绍了升级组成员所需的步骤。此过程是"7.3.3 组复制在线升级方法” 的一部分。升级组中所有成员的方法都是通用的,但要注意,组运行在单主模式还是多主模式,会影响组成员升级并重新加入组的顺序。
升级组成员的过程包括:将其从组中剔除,按照选择的升级成员的方法执行升级,然后重新将升级后的成员加入到组中。对单主模式的组中的成员执行升级时,建议先依次升级只读节点,最后再升级读写节点(主要节点),如果读写节点需要先于只读节点升级,则需要先选择一个旧版本的其他成员作为新的主要节点。所以,为减少工作量,通常建议读写节点最后升级。
升级组成员:
-
使用客户端登录到将要执行升级的组成员中执行STOP GROUP_REPLICATION语句停止组复制,然后,查看performance_schema.replication_group_members表来确保该成员的已经处于OFFLINE状态(注意,此时在该表中应该只存在一行记录,且该行记录的MEMBER_STATE字段值为OFFLINE)。
-
通过设置系统变量group_replication_start_on_boot=0来禁止该成员在重新启动时自动加入组,当升级与配置操作完成之后,再手工安全地将该成员重新加入组。注意:如果执行升级的成员设置系统变量group_replication_start_on_boot=1,那么如果升级操作失败,则重启Server时,发生故障的成员会尝试自动加入组。
-
停止组成员的MySQL Server进程。例如,在Server所在服务器中使用mysqladmin shutdown命令或登录到数据库中使用shutdown语句停止需要升级的组成员Server进程,此时,组中的其他成员继续保持运行状态而不会受到影响。
-
使用原地(使用mysql_upgrade命令升级数据字典方式)或使用预先安装配置好新版本Server进行逻辑导数的方法升级。升级后执行重新启动Server时,由于系统变量group_replication_start_on_boot设置为0,不会自动启动组复制并重新加入组,所以需要人工操作重新加入组。
-
当成员执行升级完成之后,必须将系统变量group_replication_start_on_boot设置为1,以确保成员下次重启后能够自动重新加入组。
-
使用客户端登录到升级后的Server中并执行START GROUP_REPLICATION语句启动组复制。使该Server重新加入到组中。此时,组复制的元数据在已经升级完成的成员中已经准备就绪,因此通常不需要重新配置组复制。但必须要追赶组中最新的事务,当它追赶上了组中的最新事务之后,它就会成为这个组的在线成员。注意:升级Server所需的时间越长(即,离开组的时间就越长),该Server与组中的数据差异可能就越大,在将其重新添加回组时,所需的时间也就越多(因为可能有更多的事务需要追赶)。
当升级后的Server加入一个组时,如果发现组中成员运行的MySQL Server版本比较旧时,升级后的Server在重新加入组时会将自身的系统变量super_read_only设置为on。这能够确保在所有成员都升级到新版本之前不会在刚升级完成的Server中写入新的数据(防止新版本Server中写入的数据无法兼容旧版本的Server)。在多主模式的组中,当组升级完成且准备对外提供服务时,打算作为可写主模式的成员必须设置为读写模式。从MySQL 8.0.17版本开始,当一个组的所有成员都升级到相同的版本时,会自动将所有成员设置为读写模式。但对于较早的版本,您必须手动将每个需要执行读写操作的成员设置为读写模式。通过如下语句进行设置:
root@localhost : (none):30: > SET GLOBAL super_read_only=OFF; set global read_only=OFF;
Query OK, 0 rows affected (0.00 sec)
7.3.3. 组复制在线升级方法
组复制在线升级有如下几种可选的方案,可根据自身实际情况进行选择。7.3.3.1. 组内滚动升级
组内滚动升级:指的是组成员逐个剔除出组、执行原地升级(不需要迁移数据,不需要更换服务器),升级完成之后,再重新加入组。在执行升级的过程中,组全程能够对外提供读写服务,但被剔除出组并执行升级的成员在执行升级的过程中不承载任何工作负载(不提供只读或读写服务),当成员升级完成之后,重新加入组时,如果组内存在着更低版本号的成员,则它会以只读方式重新加入组(此时只是提供只读服务)。对于新加入的成员后续是否需要从只读模式变更为读写模式,则需要看组运行在单主模式还是多主模式。
-
单主模式:组内滚动升级方式,非常适合单主模式的组,在单主模式下,执行滚动升级时,将辅助节点(只读节点)先依次逐个执行升级,主要节点(读写节点)最后执行升级,这样,在整个升级过程中,就可以尽可能避免重新选主。当然,在最后对主要节点执行升级时,也是无法避免重新选主的,但,按照这个推荐顺序升级,只会发生一次重新选主,即,在主要节点被剔除出组并执行升级时会触发一次重新选主。当最后一个成员(也就是之前的主要节点)升级完成之后,可以使用group_replication_set_as_primary() UDF将其重新指定为主要节点。当然,如果你不介意哪个组成员作为主要节点,则可以不使用UDF重新指定主要节点。关于单主模式下的选主策略,详情可参考"1.3.1 单主模式”。
-
多主模式:对于多主模式的组,其中可能存在多个主要节点(读写节点),使用组内滚动升级时,没有特定的更新顺序规则(可根据实际情况决定更新成员的先后顺序),但,在组内所有成员更新为最新版本之前,已经完成更新的成员重新加入组时,由于组内存在更低版本的成员,所以会被强制设置为只读模式(设置系统变量super_read_only=ON。注意:设置super_read_only=ON时,read_only会自动设置为ON,但在将super_read_only=OFF时,不会自动将read_only设置为OFF),由于多主模式的组中多个节点都可以同时提供读写服务,所以,这种情况下,组的写可用性会逐渐下降,直到组内所有成员都更新为最新版本。当组内所有成员都更新为最新版本时,如果MySQL Server版本号小于8.0.16的,则被强制设置为只读模式的组成员,需要手动设置系统变量super_read_only=OFF,read_only=OFF来关闭只读(回到读写模式)。如果MySQL Server版本号大于等于8.0.17,则在所有成员都更新为新版本之后,会自动设置系统变量super_read_only=OFF,read_only=OFF来关闭只读(回到读写模式)。关于多主模式下的版本兼容性,详情可参考"1.3.2.3. 版本兼容性”。
有关组中的版本兼容性及其如何影响组在升级过程中的行为的完整信息,请参见"7.1. 在一个组中兼容不同版本的成员"。
PS:组内滚动升级的方案,组成员在升级完成之后,重新加入原来的组。
7.3.3.2. 迁移滚动升级
迁移滚动升级:指的是组成员逐个剔除出组、执行升级之后,不重新加入原来的组中,而是使用升级后的成员创建第二个组(新组)。对于以多主模式运行的组,在此过程中主要节点(读写节点)的数量会逐渐减少,导致写可用性降低。但对于以单主模式运行的组,这不会影响组的写可用性(但在组最后升级读写节点时,应用需要在新旧组之间执行一次写请求切换,此时应用会受到影响)。
在对组执行升级的过程中,因为运行旧版本的组一直处于在线状态(持续对外提供读写和只读服务),所以更新完版本的成员组成的新的组必须要追赶升级过程中,旧组中新写入的任何事务。因此,需要在新组中选定一个成员作为从库,与旧组中的主要节点(读写节点)之间建立一个异步的主从复制通道来追赶最新的数据。为避免通过异步复制通道追赶数据出现意外,需要保证新旧组之间关于组复制和主从复制相关的系统配置参数完全一致。对于以单主模式运行的组,新组中作为从库的成员必须是新组中的主要节点(读写节点),对于多主模式运行的组,新组中作为从库的成员可以是新组中的任意成员(读写节点)。
整个升级操作过程大致如下:
-
逐个从运行旧版本的原始组中剔除成员,参见"7.3.2. 升级组复制成员"。
-
升级被剔除组的成员上运行的MySQL Server版本,升级步骤参见《千金良方-MySQL 性能优化金字塔法则》.第2章 MySQL常用的两种升级方法。
-
用升级到新版本的组成员创建一个新组,部署新组的步骤详见"2、组复制安装部署"。要注意,由于旧组正在运行,因此需要给新组命名一个新的组名称,并使用第一个升级完成的成员来引导新的组,后续升级完成的成员加入新组即可。
-
在旧组和新组之间设置异步复制通道。将旧组中的主要节点设置为异步复制的主库,将新组中的主要节点配置为基于GTID复制的从库。
在将应用程序重定向(切换)到新组之前,必须确保新组具有适当数量的组成员,以便新组在有成员发生故障时可以正常应对。可以通过在新组的任意成员中通过performance_schema.replication_group_members表查询组成员视图来查看旧组和新组的组大小,在确保该信息之后,可以在旧组中阻塞数据写入(需要观察新旧组之间的异步复制延迟,延迟不大时可以执行此步骤),并等待新组中追赶旧组中的最新数据,直到新组追赶上旧组的所有数据为止,然后,切换应用程序到新组中,并删除新旧组之间的异步复制连接,最后,升级所有旧版本的组成员,升级完成之后,将其逐个加入到新组中。
PS:迁移滚动升级的方案,组成员在升级完成之后,成员会组成新组,服务器(主机)还是原来的。
7.3.3.3. 副本滚动升级
副本滚动升级:与"组内滚动升级"和"迁移滚动升级"两种方案比起来,组成员不需要做剔除出组的操作,而是使用备份工具,将组中的数据副本完整拷贝到一组新的服务器中,逐个执行升级,并创建第二个组(新组),在执行升级期间,由于旧组持续在线对外提供服务,新旧组之间的增量数据,需要通过在新旧组之间建立异步的复制通道进行数据同步(对于新旧组之间创建异步复制通道的要求,详见"7.3.3.2. 迁移滚动升级"),当新组追赶上旧组中的最新数据时,将应用程序的访问切换到新组。对于以多主模式运行的组,升级过程中主要节点(读写节点)的数量不会减少,所以,写的可用性不会受到影响(因此,这种升级方式非常适合运行多主模式的组)。对于以单主模式运行的组,写可用性同样不会受到影响。
整个升级操作过程大致如下:
-
使用新版本Server程序包在新的服务器中进行初始化安,需要确保新版本的成员数量在有成员发生故障时能够正常应对。注意,此时不需要在新版本的服务器上创建组,只需要使用新版本程序包初始化安装数据库Server,并确保Server能够正常启动即可。
-
从原组中的某个成员中备份现有数据(完整备份),关于备份的详细步骤,可参考"4.6. 在组复制中使用备份数据恢复失败的成员或扩容新成员"。
-
使用备份数据恢复到准备创建新组的服务器中,执行原地升级。
-
用升级到新版本的Server创建一个新组,部署新组的步骤详见"2、组复制安装部署"。要注意,由于旧组正在运行,因此需要给新组命名一个新的组名称,并使用第一个升级完成的Server来引导新的组,后续升级完成的Server加入新组即可。
-
在旧组和新组之间设置异步复制通道。将旧组中的主要节点设置为异步复制的主库,将新组中的主要节点配置为基于GTID复制的从库。
一旦新组中的数据相较于旧组的复制延迟足够小时,就可以执行应用的切换(将应用程序的访问重定向到新组中),然后,删除新旧组之间的异步复制通道(详情可参考"7.3.3.2. 迁移滚动升级"中对应用程序的切换要求)。
PS:副本滚动升级方案,组成员在升级完成之后,组成员和服务器(主机)都是全新的。
| 作者简介
罗小波·数据库技术专家
《千金良方——MySQL性能优化金字塔法则》作者之一。熟悉MySQL体系结构,擅长数据库的整体调优,喜好专研开源技术,并热衷于开源技术的推广,在线上线下做过多次公开的数据库专题分享,发表过近100篇数据库相关的研究文章。全文完。
Enjoy MySQL 8.0 :)
叶老师的「MySQL核心优化」大课已升级到MySQL 8.0,扫码开启MySQL 8.0修行之旅吧