ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

PolarDB只读节点不可用排查指南:从症状到根因的完整路径

PolarDB只读节点不可用排查指南:从症状到根因的完整路径 开年第一个工作日凳子还没坐热告警群就炸了。业务方甩过来一张截图只读库连不上了报表全部超时。看了一眼工单标题PolarDB从节点不可用心里咯噔一下这个开年“丢人”事故算是躲不掉了。PolarDB的从节点也就是官方叫法里的只读节点在云原生数据库架构里地位很微妙。它不像自建MySQL从库那样简单stop slave、start slave就能救回来也不一定是数据同步坏了。很多老DBA会习惯性地用传统主从复制那套经验去排查结果越查越蒙。这篇复盘我就把整个排查过程拆成一套能照着抄的路径从症状判型、架构原理、排查步骤到三个真实案例一次讲透。无论你是刚接手PolarDB的运维还是已经被只读节点折腾过几回都能找到对应的解法。1. 先搞清楚“从节点不能用”到底是个什么症状接到告警别急着连上去看。先问三句话什么业务受影响从什么时候开始监控报的是什么指标。这三句话决定了整个排查方向。1.1 三类典型表现PolarDB只读节点不可用外在表现大致分三种每种背后的原因完全不同。外在表现可能原因排查重点连接被拒、连接超时、报Too many connections节点健康检查失败被负载均衡摘除、连接数打满、节点重启中节点状态、连接数、健康检查日志能连上但SQL全部卡住、CPU打满、查询排队大查询占满CPU、锁等待、存储IO异常进程列表、慢日志、事务列表能连上但读到的数据严重滞后复制延迟、复制中断、大事务阻塞复制状态、延迟监控、大事务排查第一种表现最像“不可用”业务第一时间感知到的就是连不上。连接被拒不一定代表数据库进程挂了很可能是PolarDB感知到该节点不健康自动把它从集群地址中摘除了。第二种表现最迷惑节点状态显示运行中但业务实际访问已经超时这时候需要登录节点看内部状态。第三种表现最容易被误判为“数据错了”其实是延迟问题读到的还是老数据。1.2 影响范围界定收到“从节点不能用”的反馈第一件事是确认业务的访问路径。PolarDB集群通常有三种地址主地址、集群地址、只读地址。主地址只指向主节点集群地址和只读地址会做只读流量的负载均衡把请求分发到所有可用的只读节点上。如果业务用的是只读地址或集群地址PolarDB会自动摘除不健康的只读节点其他节点继续扛流量业务可能只有部分请求失败。如果整个集群只有一个只读节点且它挂了那所有只读流量都会失败影响直接被放大。如果业务出于某种原因在代码里配置了到某个只读节点的“直连地址”那就更惨节点一挂这个连接就彻底断了没有任何容错。先搞清楚影响范围再决定止损动作。多数情况下先把读流量切回主地址或临时摘掉故障节点让业务恢复比闷头查问题更重要。这也算是我丢过几次人之后总结出来的优先级先止损再定位。2. 为什么说PolarDB从节点故障和自建MySQL主从完全不是一回事如果还用自建MySQL主从的逻辑去处理PolarDB很快会发现各种操作“使不上劲”。根本原因在架构。2.1 一写多读架构与共享存储自建MySQL主从是典型的一主一从或多从架构主库把binlog推给从库从库通过SQL线程回放数据是“算”出来的每一份存储都在各自身上的磁盘上主备天然有两份数据副本。而PolarDB是共享存储架构主节点和只读节点连的是同一份分布式存储数据本身只有一份。主节点产生的redo日志通过物理复制同步给只读节点只读节点不需要重新执行一遍完整的SQL逻辑只需要把redo应用到自己本地的缓存和内存结构中。这意味着只要存储没坏从节点与主节点的数据不会出现“某张表没同步”这种传统复制里常见的分叉问题。从节点相对主节点更像一个“共享数据的热缓存”。这个架构带来的第一个排查思路变化是如果从节点数据不对优先怀疑的不是同步逻辑坏了而是延迟没追上或者读取路径出了问题。2.2 复制机制差异带来的排查思路变化传统主从里从库慢往往是因为IO线程或者SQL线程卡住回放速度跟不上主库写入解决办法通常是调整并行复制参数、拆分大事务、优化无主键表。PolarDB的物理复制走的是底层redo日志效率远高于binlog逻辑回放。但它也有自己的软肋。只读节点自己是有内存和本地临时存储的大查询、大排序、临时表都会消耗只读节点的本地资源。一旦只读节点的CPU或内存被一个慢查询拖垮redo日志的应用也会被拖慢表现就是复制延迟持续上涨涨到一定阈值PolarDB可能判定该节点不健康。所以在PolarDB里修复从节点复制延迟的思路不是重新拉齐位点而是先解决只读节点上正在跑的“坏查询”。这也是很多新手最容易转不过弯来的地方。2.3 先别急着干三件事下面三件事我在排查PolarDB从节点故障时一开始都干过后来发现要么没用要么有风险。建议先忍住。注意不要一上来就执行STOP REPLICA/START REPLICA。PolarDB只读节点的复制状态由云平台管控手动启停复制线程可能和管控侧状态机冲突一旦管控感知不到节点心跳可能会触发自动重建节点流程问题反而变复杂。不要一上来就重启只读节点。重启一个看起来“状态异常”的节点效果和拔插头一样治标不治本。如果节点底下有坏盘或者内存问题重启后很快就会复发。不要一上来就删了重建。创建新只读节点需要全量初始化如果一个几十TB的实例创建过程可能要数小时。这段时间业务完全没有那个节点可用损失更大。正确打开方式是先做诊断搞清楚节点当前处于什么状态再决定是等它自愈、提规格、还是重建。3. 从节点不可用的十大原因排查清单把我在实际运维中遇到的从节点异常原因汇总了一下不算特别全但覆盖了大多数场景开年到现在我已经靠这张清单避开了至少三次误判。3.1 存储与容量共享存储不代表没有容量问题。只读节点在查询过程中会产生临时表、排序文件、内部临时结果集这些数据会落在只读节点各自的临时存储上。如果某个只读节点上经常跑重聚合查询临时空间消耗会非常快。临时空间满了之后查询会直接报错现象就是只读节点上的SQL大面积失败。控制台里能看到每个只读节点的存储水位。别只盯着集群总存储用量要按节点维度看。节点各自的临时空间一般不会自动扩容属于日常巡检最容易漏掉的项目。3.2 复制延迟与大事务PolarDB监控面板里有“只读节点复制延迟”这个指标单位通常是秒。延迟抖动在业务高峰期比较常见几十秒之内一般无感。但如果延迟持续上涨且没有回落趋势说明只读节点正在被某个大操作卡住。最常见的大操作有几种无主键大表的一次全表更新、一条SQL涉及几千万行的批量修改、一个长时间运行的DDL。这些操作在主节点上可能只影响那一条SQL但在只读节点上redo应用会被阻塞最终表现为复制延迟越来越大业务读到的数据越来越旧直到告警阈值触发。3.3 规格变更与节点生命周期节前扩容、节后缩容是开年最常见的从节点故障诱因。PolarDB的规格变更比如从4C8G升到8C16G往往需要迁移节点到新的计算资源上这个过程中节点会进入“变更中”状态健康检查可能短暂失败如果变更耗时较长管理侧可能直接标记节点不可用。还有一种情况是同时进行多个运维操作。比如一边在缩容只读节点一边又触发内核小版本升级两个操作之间没有做好等待很容易导致节点状态卡在一个中间态。如果碰到这种情况不要反复点击“重试”先看变更任务的历史记录确认当前卡在哪一步再决定是否终止任务。3.4 连接层与健康检查只读节点“不可用”不等于数据库挂了可能是健康检查失败被摘除了流量。PolarDB对只读节点有一套探测机制如果节点长时间无响应、查询超时、或内部状态异常它会被标记为不健康从服务列表里摘掉。摘除之后业务连接池里的旧连接可能还指向这个节点新连接会绕开它于是出现“部分请求失败、部分请求正常”的诡异现象。排查时看控制台里该节点是否显示“不可用”或“维护中”如果显示不可用但节点进程还活着多半是健康检查探测失败需要从业务侧排查是否有锁表、死循环、高CPU占用。3.5 内核小版本与参数不一致PolarDB控制台可以给整个集群设置参数模板但如果有节点是后来单独创建的或者某个节点经历过手工参数修改就可能出现同一集群内不同只读节点参数不一致的情况。比如一个节点long_query_time是1秒另一个是10秒一个节点的max_connections是2000另一个是200。这种不一致在平时没有明显问题一旦遇到突发流量小参数节点先扛不住表现就是只读节点里的“单点故障”。我在实际维护中踩过这个坑最后养成了习惯每次给集群调整参数后都会逐个检查所有只读节点的参数值是否同步尤其是新扩容节点加入集群之后必须核对参数模板版本。4. 实操排查从接到告警到定位问题下面是一条我自己验证过多次的排查路径照着走可以比较快地定位PolarDB从节点不可用的原因。整个过程不需要特别多的“神仙工具”用好控制台和几条SQL就够了。4.1 先看控制台三张图任何一次从节点故障诊断我都是先打开控制台的三张监控图节点状态、复制延迟、CPU/内存/IO使用率。节点状态图看的是当前从节点是否处于运行中、变更中、异常中。如果状态直接是异常后面所有诊断都会轻松很多因为问题大概率出在节点本身的资源或生命周期上。复制延迟图很关键尤其要把时间轴拉长看延迟是从哪个时间点开始上涨的。延迟上涨点往往就是业务变更点比如某条批量任务启动、某个表结构变更执行、某次大促流量开始。CPU/内存/IO使用率关注的是趋势而不是瞬时值。只看某一瞬间可能什么都看不出来但拉长到故障前后一个小时就能看到到底是哪个资源先被耗尽这通常就是根因。4.2 登到只读节点看复制状态控制台看完了再登到只读节点上验证。PolarDB MySQL版兼容MySQL生态所以很多熟悉的命令依然可用。-- 查看复制状态MySQL 8.0.22及以上语法 SHOW REPLICA STATUS\G;重点看几项Replica_IO_Running是否为Yes、Replica_SQL_Running是否为Yes、Seconds_Behind_Source的具体数值。在PolarDB的只读节点上复制线程的概念和传统主从不完全一样但这两个字段仍然能反映大致的健康状态。再看一下当前进程列表有没有一堆堆积的查询SHOW PROCESSLIST;如果State里大量是“Waiting for table metadata lock”“Sending data”“Copying to tmp table”那基本说明只读节点内部有查询在拖后腿。Sending data不一定是坏查询但如果同一个SQL出现几十次它占用的CPU一定不小。4.3 抓出拖垮从节点的长事务如果复制延迟高下一步就要看事务列表。information_schema里的innodb_trx表是排查长事务的第一现场。SELECT trx_id, trx_state, trx_started, TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS trx_age_seconds, trx_mysql_thread_id, LEFT(trx_query, 100) AS trx_query FROM information_schema.innodb_trx ORDER BY trx_started ASC LIMIT 20;这个SQL能按开始时间从早到晚列出所有未结束的事务一眼就能看出哪个事务已经跑了很久。TRX_AGE_SECONDS超过几百秒的基本就是嫌疑对象。拿到trx_mysql_thread_id之后再去SHOW PROCESSLIST里看这个连接当前在执行什么SQL。很多时候你会发现根本不是SQL本身慢而是它在一个事务里做了大量行级修改整个修改过程产生的redo要同步到只读节点而只读节点一时半会儿应用不完延迟自然就上去了。4.4 慢日志与错误日志怎么读控制台里的慢日志和错误日志是两条被很多人忽略的线索。慢日志能帮你找到只读节点上长期存在的高频慢查询。如果一个业务查询在只读节点上经常要跑几十秒它平时可能不致命但在流量高峰期就会把节点压垮。错误日志则会记录节点的重启原因、连接被kill的提示、内部异常报错。看慢日志时不要只看某一条SQL要看执行次数和总耗时。有些SQL单次只要500毫秒但1分钟执行几千次累加起来就是巨大的CPU开销。看错误日志时重点找“Node is not healthy”“Connection closed”“Out of memory”这类关键字这些往往是节点被摘除或重启的直接证据。4.5 恢复操作优先级定位到原因之后恢复操作按优先级来。如果是连接数打满先让业务侧缩小连接池规模或者临时调大max_connections让节点先恢复响应。如果是CPU被慢查询打满找到对应SQL并终止它而不是重启节点终止单个查询的影响面小得多。如果是规格问题比如内存长期水位高才考虑提升只读节点规格。如果节点已经处于异常状态且无法自行恢复控制台的重启节点操作可以用但要有心理预期冷启动后节点需要重新加载数据缓存短时间内查询性能会下降。如果节点频繁异常且确认底层有问题再考虑新建只读节点替换。替换时先创建一个新节点确认状态正常并追平数据再删除旧节点避免出现只有一个从节点且它还在重建中的窘境。5. 开年“丢人”案例复盘三次让我印象深刻的从节点故障理论讲再多不如复盘几个真实场景。这里讲三个我开年以来遇到过的案例有我自己踩的坑也有帮别人擦的尾巴。细节做了脱敏处理但排查路径是原样的。5.1 案例一节后缩容遇上升级窗口只读节点反复重启背景是节前业务扩容加了三个只读节点节后需要缩到两个。运维同学在控制台里提交了缩容任务同时因为节前发布了内核小版本升级通知也顺手勾选了升级。两个任务并到一个变更窗口里结果缩容任务先执行其中一个只读节点的规格变更还没完成升级任务又开始两边在节点的生命周期状态上打架。现象就是那个只读节点一直显示“变更中”然后反复重启业务侧看到的就是节点时好时坏监控反复告警。查控制台的变更记录发现升级任务在等待缩容任务释放资源而缩容任务又因为升级任务占用资源卡住形成了一个死锁式的运维状态。最后是终止了其中一个变更任务只保留缩容操作等它彻底完成后再手动触发一次升级才恢复正常。复盘之后的结论是PolarDB的节点生命周期变更必须串行执行任何两个涉及同一个节点的异步任务都不要同时提交运维操作前先看变更记录。5.2 案例二一个没带WHERE的UPDATE把只读节点CPU打到100%这个案例特别“丢人”因为根因非常简单一条UPDATE语句忘了加WHERE条件把一张两千万行的表整表更新了。主节点因为所有写入都在按顺序执行没有太大感知但只读节点要应用对应的redo日志CPU瞬间被打满紧接着所有从只读节点读取的查询全部排队业务报表接口超时。接到告警后我第一反应是看只读节点的进程列表发现大量查询堆积在同一个节点上然后查innodb_trx看到一个事务已经把某张表锁了很久再追到这条UPDATE立刻通过连接ID终止了它。终止之后只读节点CPU降下来复制延迟在几分钟内追平业务恢复。后续给这张表加了更新条件和分批更新方案另外把大事务告警的阈值降到了60秒任何事务运行超过60秒都会自动通知避免下次再“裸奔”。5.3 案例三新创建的只读节点“永远追不上”第三个案例和“丢人”关系不大但很常见。某业务新加了一个只读节点用于承担一个新的报表分析场景。创建完成后节点状态显示运行中但业务反馈查询这个只读节点时数据总是滞后。查复制延迟发现一直维持在几十分钟的水平而且没有收敛趋势。刚开始怀疑是不是复制卡住了但看各个指标都很正常。后面才发现这个只读节点上有一个长期运行的复杂分析查询是大范围扫描数据的报表任务直接把节点的CPU占用了大半redo日志的应用始终排在后面延迟自然下不来。这个案例的解法不是优化复制而是优化查询。把那个复杂报表改成走独立的数仓分析链路只读节点专职服务在线业务延迟马上就降到了秒级以内。新建只读节点就有一个容易被忽略的成本它刚创建完数据缓存是空的不要立刻把大查询压上去要让节点先“暖”一会儿我一般会用业务实际读流量预热半小时再开放给报表场景。6. 常见问题速查表从现象一步跳到处理方法把上面这些经验整理成了一张速查表故障时照着查能少走不少弯路。现象可能原因第一步动作根治方案只读节点连接超时业务大面积报错节点被健康检查摘除或连接数打满控制台看节点状态和连接数指标调整连接池大小优化长连接管理只读节点状态运行中但查询全部卡住大查询占满CPU或存在锁等待SHOW PROCESSLIST找到堆积查询终止异常SQL优化慢查询复制延迟持续上涨业务读不到新数据大事务阻塞redo应用或节点规格不足查innodb_trx找长事务看复制延迟趋势拆分大事务提升只读节点规格节点反复重启状态在“变更中”打转多个变更任务冲突或内核升级未完成查变更任务历史记录确认卡点串行执行运维操作避免任务并发只读节点重新创建后数据一直滞后新节点缓存为空且有大查询压上来先限制大查询访问观察延迟趋势预热缓存逐步放开流量单个只读节点故障其他节点正常该节点临时空间不足或参数与集群不一致检查该节点的存储水位和参数值清理临时空间统一参数模板这张表里没有特别高深的技术但每一条都是实打实踩过的坑。把这些内容固化下来比自己每次从零开始排查效率高得多。我个人现在处理PolarDB从节点故障的原则很朴素控制台看三张图节点上跑三条SQL再确定恢复动作。遇到从节点不可用不慌着重启和重建先花十分钟做一次状态快照。多数从节点不是“坏”了而是被流量、被查询、被变更压得喘不过气。你给它一点时间把背后的元凶找出来它自己就会恢复。至于那些反复出现的故障把根因写进告警规则和巡检脚本里下次就不会再在同一个地方丢人了。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进