ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MySQL InnoDB Cluster架构解析与高可用故障切换实践

MySQL InnoDB Cluster架构解析与高可用故障切换实践 MySQL InnoDB Cluster后面我习惯直接叫MIC这个系列写到第十一篇前面的内容基本把“怎么搭一套能跑的集群”讲透了包括组复制原理、MySQL Shell的用法、MySQL Router的读写分离等等。今天这篇我不想再重复那些入门级的安装手册而是换个思路把MIC当作一个真实的生产组件去拆开看它到底由哪些角色组成网络端口和版本这些基础条件怎么选初始化集群之后还有哪些必须立刻处理的收尾工作主节点故障时数据库和Router是怎么联动的以及日常巡检我到底看哪些指标才不会被假象骗到。不管你是刚部署完MIC被一堆术语绕晕的运维新人还是已经在用但没认真研究过故障切换细节的开发同学这篇文章里应该能找到一些可以直接拿去用的东西。1. 先把MIC的架构底牌翻清楚很多第一次接触MIC的人会把“集群”想成一个黑盒好像跑了一条命令之后一组MySQL节点就自动变成一个能自愈的分布式系统。实际上MIC不是一个单独的软件包而是由四类角色拼起来的组合MySQL服务器实例、MySQL Shell、MySQL Router再加上MySQL里默认集成的Group Replication插件。我习惯把MySQL Shell看成“指挥台”它往实例上执行createCluster指令时会配置好组复制、创建InnoDB Cluster元数据MySQL Server才是真正存数据的节点数据库实例通过Group Replication相互实时同步MySQL Router则是客户端接入的“门卫”负责把读写流量自动分发到正确的节点上。这四者的关系可以用一个生活化类比来解释。你开了一家连锁店MySQL Shell就是总部管理员负责给各家分店装监控、定规章MySQL Server是各个店面的收银台和仓库Group Replication是仓库之间实时调货派车的调度系统MySQL Router则是挂在门口的引导牌告诉顾客“要买新东西去总店想查库存任意分店都行”。这样拆开理解之后后面所有配置项就都能找到对应的“为什么”了。1.1 MIC到底由哪几块拼成先看一张最简单的角色划分表后续讲配置时我会反复提到这些名字。组件职责默认肉眼看得到的端口MySQL Server Group Replication数据存储、事务提交、组内同步3306MySQL协议、33061组复制通信MySQL Shell集群初始化、扩容缩容、元数据管理3306或33060X协议MySQL Router读写端口、只读端口、故障感知6446/6447业务端口、8441路由器管理端口可以看到并不是只有“数据库节点”加了组复制就叫MIC还要有Shell负责创建元数据有Router负责入口流量调度。只有这四块全部就位你才能说这是一套完整的InnoDB Cluster。组里每台Server节点都保存着完整的元数据它们通过MySQL Shell注册时生成一个集群名例如mycluster这个名字会记录在mysql_innodb_cluster_metadata库里。之后MySQL Router启动时会去读这份元数据自动知道当前节点谁主谁从、集群有没有异常。这也是MIC比传统主从复制好用不少的地方不用手动在Router端改后端节点列表。1.2 为什么说MIC的核心是Group Replication可以说MIC的灵魂就是Group Replication组复制。组复制不是传统意义上的主从同步它用分布式共识协议把多个MySQL实例组成一个“复制组”组内每个成员的提交事务都会经过一个certification认证过程确保同一个事务不会在多个节点上产生冲突。常规主从复制是异步的主库写完binlog后从库慢慢拉主库没法确认从库到底有没有追上。组复制则不一样一个事务在某个节点提交前会先在组里广播其他成员收到并确认后才会真正提交。这个机制决定了两个关键行为一是数据一致性比异步复制靠谱二是整个组对网络延迟非常敏感。后面我会专门说网络问题这里是先埋个底。MIC默认使用单主模式组里只有一个节点是primary其他都是secondary。写事务只能走到primary上secondary同样持有全量数据但默认会被设为只读保证客户端误连也不会乱写。如果primary出问题组复制协议会自动从存活的secondary里重新选主这个过程不需要人工切换VIP也不需要外部编排工具介入这是MIC高可用能力的核心来源。1.3 几个容易被忽略的数据流细节不少朋友第一次用MIC会踩同一个坑写数据成功后立刻通过Router的只读端口去查结果查不到。这不是集群坏了而是读写分离的正常表现。写请求从应用发到Router的6446端口Router会把它转发给当前primaryprimary提交事务后组复制把binlog事务同步到其他secondary这中间存在一个极小的复制窗口。如果此时你的查询走的是Router的6447只读端口请求可能落到还没应用完这个事务的secondary上自然读不到刚写入的数据。所以业务上对“写后读”一致性要求高的场景必须强制走读写端口或者在做只读路由时接受这个延迟窗口。另一个数据流细节是组复制要求binlog格式为ROW并且必须开启log_slave_updates因为每个节点既要记录自己的日志也要把从其他节点应用的事务继续记录到自己的binlog里这样才能让新加入的节点通过binlog拿到完整历史。如果你在部署时习惯性沿用旧版的Statement格式后面初始化集群时会直接被MySQL Shell拦下来报错说配置不满足组复制要求。2. 部署MIC前必须先想清楚的几件事我见过不少人拿到文档就照着敲命令敲完发现集群状态是ERROR回头看原因基本都是版本不一致、端口没放通、网络延迟过高。MIC本身不复杂但它对底层环境的要求比单机MySQL严格不少提前想清楚能省掉后面一晚上的排查时间。2.1 版本选择和端口规划最基础的一条集群内所有MySQL实例的版本必须保持一致。这里说的版本不只是大版本小版本也尽量统一。Group Replication在不同小版本之间的协议行为有一些细微调整混着跑虽然不一定爆雷但一旦触发边界问题排查成本陡增。我实际跑过的环境是全员MySQL 8.0.33MySQL Shell和Router也选用同版本发布包。推荐直接使用8.0.27以上的版本这个版本之后InnoDB Cluster的元数据管理、Router的自动配置都相对成熟。低于8.0.20的版本需要额外处理很多兼容性问题不必去折腾。端口规划上每个实例至少需要两类端口端口用途注意事项3306MySQL客户端连接需要能被应用、Shell、Router访问33061组复制成员之间的通信必须是实例间可互通的TCP端口不能只开330633060MySQL Shell的X协议端口使用Shell时最好保持默认避免部分操作走错协议6446/6447Router的读写/只读端口需要对外开放给具体业务8441Router管理端口内部使用可只开放给运维网段注意33061这个端口非常容易被防火墙忽略。如果三台实例的MySQL端口能通但组复制端口没放通实例初始化时可能不报错运行几分钟后因为心跳丢失被组踢出去状态变成ERROR。检查集群第一件事先确认所有节点都能telnet到对方的33061端口。2.2 单主还是多主别拍脑袋MIC的初始化可以选择单主模式Single-Primary和多主模式Multi-Primary。很多人在选型时觉得“多主是不是更先进所有节点都能写性能一定更好”这个理解需要纠正。单主模式下只有一个primary节点可写其余secondary全部只读。多主模式下每个节点都能写组复制靠事务认证机制避免同一条数据被并发修改。看起来多主很美好但它有几个现实约束外键和有级联操作的表在多主下有限制同一行数据如果被不同节点同时修改会出现事务回滚应用必须自己处理事务冲突重试每个节点都要全量保存所有分片的binlog网络和磁盘开销更大。我通常给业务的建议是99%的场景都选单主。单主的写入流量集中在一点反而更容易做连接数规划、慢查询分析和参数调优。只有当你有多个机房、多个写入入口且业务能容忍极少数冲突回滚时才考虑多主。这里没有谁更高级只有谁更合适。2.3 网络延迟和写入路径的影响组复制是一个对网络RTT很敏感的协议。一次写入事务在主节点本地提交之外还至少要跟其他成员进行一次网络通信来确认事务版本。假设两台机器之间网络RTT是1ms这个延迟几乎可以忽略但如果跨城市组集群RTT可能达到20ms甚至更高那么每次写入都会凭空多出几十毫秒的额外延迟这对在线业务是灾难。更麻烦的是网络抖动会导致成员被临时标记为“可疑”。组复制里每个成员会定期广播心跳其他成员如果在一个周期内没收到心跳会认为该节点已经失联然后把它踢出组。网络抖动引发的误踢虽然会自动恢复但会打断复制流应用层还可能看到连接中断。所以MIC的几个节点最好部署在同一个机房或同一个可用区延迟低于2ms才算理想。千万别把MIC当成跨地域容灾方案来用跨地域高可用建议靠上层流量调度而不是靠组复制硬撑。3. MIC初始化与常见部署套路回顾前面聊了一堆理论和选型现在落回实操。我会把初始化流程拆成三块交互式创建、非交互式脚本、初始化后的收尾动作。这三块是递进关系尤其最后一块很多人搭完集群就走了后面遇到各种刺手问题。3.1 用MySQL Shell一键拉起集群假设我有三台Linux服务器IP分别是10.0.0.1、10.0.0.2、10.0.0.3每台都已经装好了MySQL 8.0root账号允许远程连接并且保证实例是干净的没有旧数据没有旧复制配置。用MySQL Shell连上第一台实例mysqlsh --uri admin10.0.0.1:3306进入Shell交互环境后执行var cluster dba.createCluster(prodcluster);这一步会在10.0.0.1上初始化组复制创建InnoDB Cluster元数据并把当前实例设置成第一个节点也就是初始primary。Shell会提示你确认实例是否已经配置好一般输入y确认即可。接着添加第二个节点cluster.addInstance(admin10.0.0.2:3306);Shell会检查目标实例状态如果是全新实例它会自动恢复配置并加入组如果实例上已经有数据Shell会提示使用Clone插件从现有节点克隆数据。克隆完成后这个节点会以RECOVERING状态开始追日志等追上后变成ONLINE。第三个节点同理cluster.addInstance(admin10.0.0.3:3306);全部添加完后用cluster.status()查看状态。正常情况下三个成员的状态都应该是ONLINE其中一个是PRIMARY另外两个是SECONDARY。3.2 非交互模式下的命令清单如果你的环境需要自动化部署可以不用Shell交互模式直接用命令行模式执行JavaScript脚本。我常在测试环境用下面这种写法mysqlsh --uri admin:password10.0.0.1:3306 --js EOF var cluster dba.createCluster(prodcluster); cluster.addInstance(admin:password10.0.0.2:3306); cluster.addInstance(admin:password10.0.0.3:3306); print(cluster.status()); EOF这段脚本能实现和交互模式完全相同的效果。需要提醒的是把密码直接写在命令行里会留存在shell历史记录中生产环境不建议这么干。更好的做法是使用MySQL Shell的配置文件或者环境变量把敏感信息独立出来只有自动化平台才能读取。执行脚本前强烈建议先跑一次实例状态检查确认三台机器的super_read_only是OFF且数据目录里没有遗留的旧库。如果是从业务库直接改造成集群必须先解决数据一致性问题不然clone会覆盖掉旧数据。3.3 初始化之后要立刻做的事集群初始化成功并不代表可以马上交付给业务我习惯至少做四件事。第一查看完整状态var cluster dba.getCluster(prodcluster); cluster.status({extended: 1});extended: 1会输出更多底层信息包括每个成员的QUORUM、当前primary、复制延迟等。日常巡检我也会用这个命令。第二单独创建Router用的业务账号。不要使用root作为Router的后端连接账号因为Router会把账号信息写进元数据如果管理不当风险很大。一般我会创建权限受限的账号只授予必要的读写权限。第三配置Router。用mysqlrouter的bootstrap命令让Router自动读取集群元数据并生成配置mysqlrouter --bootstrap admin10.0.0.1:3306 \ --directory /etc/mysqlrouter/prodcluster \ --usermysqlrouter初始化完成后Router会自动生成6446/6447端口配置这时应用就能通过Router访问集群了。第四完成防火墙和安全组配置。别只顾MySQL端口Router的6446/6447、组复制的33061都要检查。这几步做好集群才算真正达到可交付状态。4. 高可用切换与故障恢复实录用了MIC最关心的问题通常是主节点挂了会发生什么业务中断多久有没有可能丢数据这一部分我会把故障场景从头到尾拆一遍然后说手动切换和强制恢复时那些容易踩的坑。4.1 主节点宕机后到底发生了什么以一个三节点集群为例假设10.0.0.1是primary。应用通过Router的读写端口连接实际读写都打在10.0.0.1上。当10.0.0.1突然宕机组复制协议会检测到这个成员失联剩余两个节点会通过共识机制重新选出一个primary。这个过程不需要人工触发系统会自动完成。通常情况下剩余的两个secondary中会有一个被提升为新的primary另一个仍然是secondary。这时MySQL Router已经感知到集群内部状态变化。Router启动时读过元数据它会定期与各节点保持探测。当原primary失联后Router会把读写端口自动切换到新的primary上。应用层看到的效果是某个瞬间连接闪断重新连接后读写请求会继续成功。因此应用必须配置连接重试机制否则即使Router切好了连接池里的旧连接还是会报错。整个切换过程的数据安全核心在看节点是否拥有最新数据。组复制会保证只有应用了最新事务的节点才能被选为primary所以正常故障切换不会丢已经提交的数据。但有一种情况需要小心原primary不是彻底宕机而是网络分区它被剩余节点踢出组后自己还不知道已经失联此时客户端如果还在往原primary写数据这些数据不会同步到其他节点一旦原节点恢复并被加回集群需要以组里其他节点为准进行回滚或重建。这就是脑裂风险MIC通过仲裁机制尽量避免但网络分区恢复后一定要检查原主节点上的额外写入。4.2 手动切换和强制恢复的坑有时主节点没坏但需要做硬件升级、变更内核参数或者想主动把流量切到另一台机器就要手动切换。手动切换的命令很简单var cluster dba.getCluster(prodcluster); cluster.switchToSinglePrimaryMode(10.0.0.2:3306);这个操作会要求目标实例在线且数据最新切换过程中原primary会变成secondary新的primary会开启写权限。整个切换过程是平滑的但我建议在低峰期操作因为切换瞬间Router会重新刷新元数据有少量连接中断是正常的。强制恢复是另一个容易翻车的场景。如果集群三个成员里挂了两个剩下一个孤立节点无法达成仲裁集群会拒绝服务。此时必须强制恢复var cluster dba.getCluster(prodcluster); cluster.forceQuorumUsingPartition(10.0.0.2:3306);这条命令会告诉选中的节点“你就是现在唯一合法的成员可以继续提供服务”。听起来很方便但风险也很高。如果剩下这个节点刚好不是数据最新的强制恢复后可能丢失其他节点上已经提交但尚未同步过来的事务。所以执行前必须先对比数据确认该节点的数据是最新的或者明确接受少量丢失。被强制恢复的集群如果还有旧成员想重新加入不能直接addInstance通常需要先清掉旧成员的组复制配置再通过cluster.rejoinInstance()把节点重新拉回组。这时候很容易遇到身份冲突问题我的建议是把掉队节点恢复到“未加入集群”的状态后再操作比强行重连更可靠。4.3 路由器层要怎么配合MySQL Router是应用和数据库之间的入口如果Router本身是单点即使数据库集群再高可用入口挂掉业务依然中断。所以在生产环境我至少会部署两个Router实例分别放在不同主机上上层再用域名或负载均衡器把请求分到两台Router上。Router本身不存业务数据它只是一个聪明的代理。它的配置由bootstrap生成如果有节点变更可以用重启Router的方式重新读取元数据也可以让它持续自动感知。不过要注意Router的自动感知依赖元数据数据库的可用性。如果整个MIC集群都故障了Router即使没有挂也没法正常工作。一个常见的Router相关坑是业务只配置了6446读写端口但代码里有些慢查询或者报表查询也想走只读端口结果发现Router的只读端口连接成功却返回的数据不新鲜。这种情况不是Router配置错误而是读写分离策略问题。建议业务侧在SQL级别区分实时查询和可容忍延迟查询不要把只读端口当成万能缓存。5. 日常运维与性能调优备忘集群交付后真正的考验才开始。MIC实例之间相互同步任何一个节点的抖动都会放大成整个集群的问题。这一部分我分享一下日常运维时重点看哪些数据以及我实际跑下来验证过的参数配置。5.1 关注哪些监控指标以下指标是我每次巡检都会看的整理成一个速查表。指标查看方式正常状态异常时的含义成员状态SELECT * FROM performance_schema.replication_group_members;ONLINE恢复中或ERROR需要立刻介入成员角色同一张表里的MEMBER_ROLE字段PRIMARY/SECONDARY多主模式可能出现多个PRIMARY确认是否符合预期复制延迟SELECT * FROM sys.replication_applier_status_by_worker;延迟很小延迟持续上涨说明secondary追不上日志事务冲突SHOW GLOBAL STATUS LIKE Mysql_group_replication_writer_set_size;数值稳定冲突率异常上升要重点关注网络心跳SELECT * FROM performance_schema.replication_group_members;里MEMBER_STATEONLINE频繁ERROR又自动恢复多半是网络抖动我还会额外关注实例之间的建连时间。如果Threads_connected频繁暴涨可能是应用连接池配置不当导致Router到后端实例的连接数被打满。毕竟MIC只是高可用方案不能替业务承担连接管理。5.2 常见错误和排查速查表在这几年的使用里我遇到过不少报错下面几个是出现频率最高的排名不分先后。错误信息可能原因处理建议ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sockMySQL服务未启动或socket路径不对先检查进程是否存在ps -ef | grep mysqld再确认my.cnf里的socket路径SSL connection error客户端和服务端SSL配置不匹配查看实例的require_secure_transport参数客户端补上证书或关掉强制SSLError 3092: Group Replication configuration error实例上残留旧的复制配置必须清理gtid和旧复制槽不能直接加入新组The member was expelled from the group被其他成员怀疑失联重点检查33061端口连通性和网卡丢包率Lock wait timeout exceeded多个事务同时竞争同一行数据在单主模式下排查长事务和慢SQL确认是否真的需要多主ERROR 3098: Member with address... is already in the group节点身份冲突先清掉该节点的组复制状态再重新rejoin这些错误里最容易被新人忽视的是第一项。很多安装教程教的是使用mysql命令连接但MIC环境中节点可能配置了socket连接你本地如果没设置正确的socket路径就会出现2002错误。解决办法是连接服务器时显式指定端口和协议mysql -h127.0.0.1 -P3306 -uadmin -p在排查MIC相关错误时一定要把performance_schema.replication_group_members作为第一手信息来源。它比业务日志快很多能直接告诉你成员状态和角色。5.3 一套我实际跑下来的配置参考下面是一份我常用的单主模式实例配置片段。假设每台服务器有8核16G内存两套RAID盘系统是RHEL 8系Linux。[mysqld] server-id1 gtid_modeON enforce_gtid_consistencyON binlog_formatROW log_bin/data/mysql/logs/mysql-bin log_slave_updatesON relay_log_recoveryON transaction_write_set_extractionXXHASH64 # Group Replication 参数 loose-group_replication_group_nameaaaaaaaa-bbbb-cccc-dddd-eeeeffff0000 loose-group_replication_local_address10.0.0.1:33061 loose-group_replication_group_seeds10.0.0.1:33061,10.0.0.2:33061,10.0.0.3:33061 loose-group_replication_start_on_bootOFF loose-group_replication_bootstrap_groupOFF loose-group_replication_single_primary_modeON loose-group_replication_enforce_update_everywhere_checksOFF loose-group_replication_transaction_size_limit150000000每个参数都有意义。server-id在集群里必须每台不同binlog_formatROW是组复制硬性要求log_slave_updatesON保证被应用的事务继续写入本地binlog便于新成员追日志loose-group_replication_start_on_bootOFF避免MySQL启动时自动试图加入还没建好的组bootstrap_groupOFF防止意外开启新组权限。生成UUID可以用命令uuidgen填进group_name里。对应的MySQL Router配置不用改太多bootstrap生成的mysqlrouter.conf基本可用。如果你对读写端口不太满意可以调整mysqlrouter.conf里read_only_port和read_write_port的值。Router会拿bootstrap时的账号做探活所以那个账号要长期有效。这套配置在我们生产环境已经稳定跑了差不多一年最主要的作用就是减少由网络抖动和节点故障引发的长时间不可用。最后分享一个我踩过的坑刚搭好MIC时忘记把组复制的33061端口放进云安全组业务半天没发现问题直到有一台机器重启才发现这个节点一直无法加入集群状态卡在RECOVERING。后来把端口放通并重启MySQL后它才恢复正常。所以无论你用阿里云、腾讯云还是自建机房端口放通后一定在每台机器上实测一遍跨节点的33061连通性这是MIC稳定运行的基础也是最容易被忽略的细节。
返回列表