ARTICLE DETAIL

资讯详情

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

MGR集群SEC节点ERROR状态排查与恢复全流程实战

MGR集群SEC节点ERROR状态排查与恢复全流程实战 半夜两点监控平台弹出一条红色告警MGR 集群里有节点状态不是 ONLINE。点开详情member_state 是 ERROR角色是 SECONDARY也就是我们日常说的 SEC 节点。那一瞬间的紧张感维护过 MySQL Group ReplicationMGR的人应该都懂——SEC 节点平时安安静静待在角落里一旦掉进 ERROR不只是高可用冗余少了一台这么简单更意味着后续主库如果出问题你可能连一个可靠的切换对象都没有。这篇文章不聊泛泛的架构理论直接把我维护的一套一主两从 MGR 集群里SEC 节点报 ERROR 之后的分析、处理、复盘全过程写出来包括先查哪几张状态视图、怎么从错误日志拉时间线、如何一步步把节点拉回 ONLINE以及常规手册里不会写的那些坑。适合正在管理 MGR 集群的 DBA 和运维同学参考也适合刚接触组复制的人建立一套排障大脑图。1. 先把SEC节点ERROR这件事看透很多人一看到 ERROR 就急着重启其实先把“它为什么会变成 ERROR”搞清楚比直接操作重要得多。MGR 里每个节点都要参与分布式事务的认证和提交SEC 节点虽然不写业务数据却是这个协议闭环里实实在在的一环。状态一掉整个组的决策能力都会被打折扣。1.1 组复制成员状态机ONLINE不是常态MGR 对每个成员都维护一套状态机在performance_schema.replication_group_members里可以直接看到ONLINE节点正常参与组复制事务收发消息都正常。RECOVERING节点正在加入组或者正在追赶组内缺失的事务这个状态可以持续几分钟甚至更久。ERROR节点在组内被判定为异常无法参与组复制协议通常需要人工干预。OFFLINE该实例上的组复制插件处于停止状态可能是手动停的也可能是启动失败。UNREACHABLE组内其他成员无法与该节点通信在 8.0 里比较常见。这套状态不是 MySQL 服务本身的进程状态而是组通信层GCS维护的分布式状态。每个成员会周期性地给其他成员发送心跳消息如果连续收不到某成员的响应系统就会进入“疑似故障”流程。超过一定时间后其他成员会把该节点从组里踢出去于是它在成员视图里的状态就会从 ONLINE 跌到 ERROR 或 UNREACHABLE。我见过很多同学一看到 ERROR 就以为 mysqld 崩了其实不少场景下进程还活着只是组复制这个“身份”掉了。1.2 为什么掉进ERROR的往往是SEC节点单主模式下PRIMARY 只有一台它要对外提供写服务一旦它出问题整个组会很快感知并发起新的选主所以主库的问题往往藏不住。SEC 节点不一样它要承担两件事一是通过 group_replication_applier 通道回放主库的写入二是参与组内事务的认证和一致性协议。当主库有大事务、DDL或者高峰期写入量比较大时SEC 节点的回放队列会肉眼可见地积压。积压一旦超过故障检测的容忍范围它就会被其他成员判定为“无响应”随后被驱逐。还有一点容易被忽略SEC 节点通常也在承接读流量。读压力大起来会抢占 CPU 和 IO间接拖慢 applier 线程。所以我的经验是SEC 节点出 ERROR不要只怪网络先看它的回放队列顶了多久。排队越长它进入 RECOVERING 甚至 ERROR 的概率就越大。2. 拿到ERROR告警后的第一轮排查告警来了以后第一件事不是上手改配置而是把现场固定下来。状态这种东西稍纵即逝重启了或者停掉组复制之后很多线索就没有了。2.1 先固定现场成员视图加复制通道状态登录任意一个还在线的成员最好是 PRIMARY执行这条 SQLSELECT member_id, member_host, member_port, member_state, member_role, member_version FROM performance_schema.replication_group_members;你会看到类似下面的输出PRIMARY 的状态是 ONLINE另一个正常的 SEC 也在 ONLINE唯独出问题的那台显示 ERROR 或者干脆从成员列表里“消失”了。先把这个结果保存下来因为它记录了故障发生时整个集群的拓扑快照。然后登录出问题的 SEC 节点本身查它的复制通道状态。MGR 的复制通道名字很固定叫group_replication_applierSELECT CHANNEL_NAME, SERVICE_STATE FROM performance_schema.replication_connection_status;如果这条通道的 SERVICE_STATE 不是 ON说明这个节点本地的组复制线程已经停了。这时候再看一眼最终一致性视图里的事务统计SELECT MEMBER_ID, COUNT_TRANSACTIONS_IN_QUEUE, COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE, COUNT_TRANSACTIONS_REMOTE_APPLIED FROM performance_schema.replication_group_member_stats;重点关注COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE。如果这个值一直在涨说明节点不是被网络踢出来的而是自己回放跟不上数据侧出问题的可能性更大。2.2 分清两种ERROR被踢出和主动退出这一步非常关键直接决定后续处理方向但很少有人会专门去区分。第一种是节点被其他成员驱逐。它的特征是组内其他成员长时间等不到该节点的响应于是把它从成员列表移出。SEC 节点自己的错误日志里会出现类似 has been expelled 的信息。这时候节点的 mysqld 通常还活着只是组复制停掉了。只要网络恢复重新启动组复制就能加组。第二种是节点自己退出。典型场景是 applier 线程遇到不可回放的事务错误组复制插件主动中止。这种情况下错误详情会留在performance_schema.replication_applier_status_by_worker表里。如果直接 START GROUP_REPLICATION大概率还是失败因为底层数据已经处于“卡住”状态不解决事务冲突节点就加不回来。要区分这两种用这条 SQL 就够SELECT WORKER_ID, LAST_ERROR_NUMBER, LAST_ERROR_MESSAGE, LAST_ERROR_TIMESTAMP FROM performance_schema.replication_applier_status_by_worker WHERE CHANNEL_NAME group_replication_applier;如果返回结果不为空说明是 applier 出了问题如果全表都是空那优先怀疑通信和驱逐。2.3 顺着错误日志拉时间线组复制的插件日志统一写在 MySQL 错误日志里。别用grep ERROR一把梭那样抓出来的都是噪音最好带上插件上下文过滤grep -inE group_replication|GCS|replication /var/log/mysql/mysql-error.log | tail -n 120我开始排障时会重点抓这几个关键词expelled、timeout、failed to connect、leave the group、applier。然后按照时间戳把事件串起来比如凌晨 1:58 网络断开2:00 节点被其他成员驱逐2:01 配置里的exit_state_action把实例切成了只读或直接关闭。时间线一拉出来根因基本就缩小到两类通信层问题或者数据层问题后面的事就清晰了。3. ERROR根因分类与对应处理SEC 节点掉进 ERROR原因五花八门但归纳起来就是四类通信类、数据回放类、恢复链路类、配置类。这里一个个拆开讲每一种都有对应的判断方法和处理套路。3.1 通信类故障被误判离线的典型场景通信问题是最常见的也是最容易解决的。典型情况包括SEC 节点所在机房网络闪断、防火墙或安全组策略变更、跨区域专线路由异常、备份任务把带宽占满导致心跳延迟。排查时先在故障节点和 PRIMARY 之间测试两个端口的连通性一个是 MySQL 服务端口比如 3306一个是组通信端口根据group_replication_local_address配置常见 33061nc -vz 192.168.1.10 3306 nc -vz 192.168.1.10 33061如果只是瞬时抖动等网络恢复后在故障节点上重新启动组复制就能加组。注意这里有个参数直接关系到误驱逐概率group_replication_member_expel_timeout。它控制的是从成员被判定失败到实际驱逐之间的等待窗口。默认值对跨机房、经常有网络小抖动的环境来说偏紧张我一般会结合网络质量调到 5 到 10 秒。别怕调大一点会拖慢故障切换MGR 的检测本来就是秒级机制设得太短反而容易造成误伤。3.2 数据回放故障applier卡死组复制对表结构有硬性要求——所有被复制的表都必须是 InnoDB 存储引擎并且必须有主键或非空唯一键否则无法保证事务冲突检测。这是 SEC 节点回放失败最常见的根因之一。applier 卡死的迹象很明显SEC 节点状态长期停在 RECOVERINGCOUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE只增不减replication_applier_status_by_worker里能看到具体的报错比如某张表不存在、字段类型转换失败、字符集不一致或者提示表没有主键。处理这类问题有一个很重要的原则先在主库上把源头修好比如给表补上主键、修正字段定义然后再处理 SEC 节点。组复制不像传统主从复制不能随便用sql_slave_skip_counter跳过事务。强行跳过等于把事务的一致性讨论按在地上摩擦后续认证阶段会出现更大更隐蔽的坑。我一开始也试过在主库修完表结构后直接在 SEC 节点重启组复制结果因为数据缺口已经存在节点还是进不了 ONLINE。最后老老实实走了一遍备份恢复才干净。所以后来只要定位到 applier 类错误我基本直接进入“重建节点”通道不在这上面死磕。3.3 恢复链路断裂GTID差距过大还有一种情况很有意思SEC 节点本身没毛病就是落后太多了。节点加入组时如果本地gtid_executed和组内其他成员差距很大它会自动向 donor 节点请求缺失的 binlog 进行补数据。但如果它落后的事务已经超过主库的 binlog 保留周期donor 上找不到对应的日志recovery 就会失败。节点会在 RECOVERING 状态上反复挣扎最后落到 ERROR。判断方法很简单对比双方的 GTID 集合-- 在故障节点上 SELECT GLOBAL.gtid_executed; -- 在 PRIMARY 上 SELECT GLOBAL.gtid_executed;两个集合的差就是节点落后的缺口。接着去主库上看SHOW BINARY LOGS;确认最早的 binlog 文件是否覆盖了这个缺口。如果已经 purge 了就别指望增量补齐了。这个场景在“节点离线时间过长”的环境里特别常见比如 SEC 节点因为维护停机了一周期间主库 binlog 按保留策略被清理等再开机想加组就已经回不去了。我的实践结论是只要 GTID 缺口大过 binlog 保留窗口直接走克隆或备份恢复性价比远高于手工补数。3.4 配置类故障不丢人但很常见配置问题导致 ERROR 的案例在群里问的也不少而且经常是一些很低级的错误。我整理了一张高频清单问题现象处理server_id 重复节点加组被拒绝修改 my.cnf 里的 server_id确保全局唯一server_uuid 重复加组时报错或组内冲突确认 auto.cnf不能直接拷贝其他节点的数据目录local_address 配错本地端口没监听组内连不上检查 IP 和组通信端口确认防火墙放行group_seeds 写错节点找不到种子节点保证 seeds 里有当前在线的成员地址start_on_bootOFF重启后节点不自动加组建议开启并写入配置文件配合进程守护exit_state_action 默认 ABORT_SERVER被驱逐后 mysqld 直接关闭8.0.16 建议改成 READ_ONLY避免实例消失还有一个我特别想强调的点很多同学组复制参数只用了SET GLOBAL临时设置没写进my.cnf结果节点一重启参数全回到默认值节点默默处于 OFFLINE。从组视角看就好像这个节点又掉线了。如果用的是 8.0.16 及以上版本一定要把group_replication_exit_state_action从默认的 ABORT_SERVER 改成 READ_ONLY否则被驱逐的那一刻实例会直接退出排障时你看到的就不是 ERROR而是连接不上。4. 恢复实操把ERROR节点拉回ONLINE定位到根因之后恢复就有套路了。我按从轻到重的顺序把三种恢复方式都写出来按实际情况选。4.1 轻量恢复直接重启组复制如果判定是通信原因导致的误驱逐且没有数据回放错误直接在出问题的 SEC 节点本地执行STOP GROUP_REPLICATION; START GROUP_REPLICATION;执行完马上查成员状态观察节点是不是先进 RECOVERING、再变 ONLINE。如果一直停在 RECOVERING多半是数据侧有问题回到上一节去查 applier。这里提醒一句不要在主库上随便执行 STOP GROUP_REPLICATION。单主模式下主库退出会触发成员重新选主虽然 MGR 有自动切换机制但主动把主库摘下来制造一次不是必要的切换纯属给自己增加风险。除非你在变更窗口内专门做切换演练否则别碰 PRIMARY。4.2 补GTID差距后回归谨慎使用这个方法只适合差距很小、差异在很短时间内能补齐的场景。具体做法是在 PRIMARY 上找到 SEC 节点缺失的 binlog 文件用 mysqlbinlog 在线拉取并回放到 SEC 节点mysqlbinlog --read-from-remote-server --hostPRIMARY_IP --port3306 \ --userrepl_user --passwordxxx --start-positionxxx mysql-bin.000123 \ | mysql -h127.0.0.1 -P3306 -urepl_user -pxxx回放完成后确认 SEC 节点的gtid_executed包含了所有缺失事务然后再执行 START GROUP_REPLICATION。这里我必须说一句大实话手工回放 binlog 很容易破坏 GTID 的一致性漏一个事务、重复一个事务都会导致节点加入组的时候认证失败甚至污染整个节点。组复制对事务顺序和 GTID 集合的完整性极其敏感。所以这种方法我只在缺失事务量非常小、并且能完全对照 binlog 内容时才敢用。常规生产环境我建议直接走下一步的全量重建不要再在这里赌运气。4.3 全量重建SEC节点推荐路径这是最靠谱的恢复方式也是官方文档里会建议的标准路径。拿 XtraBackup 备份一台健康节点恢复到故障节点再重新加入组。完整流程我拆成十步确认 donor 节点状态 ONLINE备份用户有 RELOAD、PROCESS、SELECT 等权限在 donor 上执行备份并通过网络传送到目标节点xtrabackup --backup --streamxbstream --target-dir/tmp/mgr_backup \ --hostdonor_host --userbackup_user --passwordxxx | \ ssh target_host xbstream -x -C /var/lib/mysql_recover在目标节点上停掉组复制和 mysqldmysql -h127.0.0.1 -e STOP GROUP_REPLICATION; systemctl stop mysql清理原数据目录并解压备份确保数据目录属主和权限是 mysql 用户检查auto.cnf里的 server_uuid 和my.cnf里的 server_id不能和集群内其他节点冲突这是备份恢复场景最容易翻车的一步把组复制相关参数写进 my.cnfserver_id13 group_replication_group_nameaaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee group_replication_start_on_bootON group_replication_local_address192.168.1.12:33061 group_replication_group_seeds192.168.1.10:33061,192.168.1.11:33061 group_replication_allowlist192.168.1.0/24 group_replication_exit_state_actionREAD_ONLY group_replication_member_expel_timeout10启动 mysqld执行START GROUP_REPLICATION;用 SQL 观察节点状态从 RECOVERING 变成 ONLINE对比 primary 和该节点上的关键表数据确认同步正常。如果你的集群是 MySQL 8.0.17 以上而且网络条件允许我更推荐用克隆插件省掉手动备份的手续CLONE INSTANCE FROM repl_userdonor_host:3306 IDENTIFIED BY password;CLONE 会自动把 donor 数据复制过来并重建数据目录完成后 mysqld 自动重启。它不会覆盖目标实例自己的 server_uuiduuid 冲突的概率小很多但 server_id 还是要人工确认。克隆完成后同样按第 6 步配置组复制参数再入组。4.4 恢复过程中的避坑清单这几条是我踩过或者见别人踩过之后总结出来的值得贴在脑子里不要在 PRIMARY 上擅自 STOP GROUP_REPLICATION哪怕只是“试一下”也可能触发不必要的切换。RECOVERING 状态不是失败别急着杀节点。看COUNT_TRANSACTIONS_IN_QUEUE是否在减少只要在推进就说明它还在自愈。千万别把group_replication_bootstrap_groupON留在业务节点上。误重启一次就可能同时出现两个 PRIMARY脑裂事故就是这么来的。恢复完节点之后顺手把 expel_timeout 和 exit_state_action 这些参数调整好否则节点可能刚回来又被踢走。如果实例因为默认的 ABORT_SERVER 行为直接关闭了不要慌按 4.3 的流程重来同时把 exit_state_action 改成 READ_ONLY。5. 复盘与预防把SEC节点ERROR的发生率打下来处理完一次故障如果不复盘下次大概率还要用同样的姿势摔一跤。我现在的习惯是把每次 SEC 节点 ERROR 的根因和处置动作归档同时把容易引发这类故障的环境因素和参数配置提前加固。5.1 网络和主机层要盯的细节组通信端口是最容易被遗忘的。很多环境防火墙只放行了 3306忘了组内还要通过 33061 这类通信端口互相连接结果节点加组就是超时。另外备份大任务和业务高峰期叠加网络带宽被占满也会让心跳延迟明显上升。建议把备份计划和业务高峰错开。时区同步也别忘了跨机房部署尤其要注意 NTP 配置。最后是磁盘binlog 和 relay log 写满磁盘是 ERROR 的高发前奏监控要提前到磁盘使用率接近 90% 时就告警别等满了再去补救。5.2 参数调优参考把常用的一组生产参数整理成表照着评估自己的环境就行参数推荐值说明group_replication_member_expel_timeout5-15容忍瞬时网络抖动防止误驱逐group_replication_exit_state_actionREAD_ONLY掉出组后保持实例可读便于进一步处理group_replication_start_on_bootON配合守护脚本避免重启后默默离线group_replication_allowlist显式成员列表云环境安全组多变显式放行更稳group_replication_consistency按业务需求选择一致性要求越高SEC 节点压力越大大部分参数支持动态修改但我还是强烈建议都写进配置文件。这样无论是重启还是克隆恢复参数都不会丢。5.3 告警和日常巡检不能只盯着PRIMARY很多团队的监控只做了“主库是否存活”SEC 节点不报警等到切换时才发现 backup 已经坏了几个月。建议用一个小脚本每隔 30 秒查一次performance_schema.replication_group_members只要发现成员状态不是 ONLINE就触发告警。同时监控replication_applier_status_by_worker有非零错误立即通知。每季度至少做一次角色互换演练在变更窗口内把 SEC 节点顶上去的过程提前跑熟。平时多演练故障真正到来时才不会手忙脚乱。这次 SEC 节点 ERROR 的分析和处理如果只记住一句话我的体会是不要对着一个 ERROR 状态去猜先看成员视图、再看 applier 报错、最后拉错误日志时间线按顺序走大部分情况十分钟内就能定位。每次恢复完顺手把当次日志归档把超时参数和 exit_state_action 重新过一遍。我自己踩过几次坑之后现在一开口就知道该先查哪张性能表SEC 节点出 ERROR 的频率也明显低了下来。希望这篇经验能派得上用场。
返回列表