系统上线三个月后的某一周,工时填报突然变卡:打开要三秒,提交要五秒,用户群里怨气冲天。这轮排查从请求耗时拆解入手,两天定位到三个叠加的问题——一个慢查询、一处串行调用、一个连接池隐患,三个问题三种典型,几乎是一本卡顿排查的教科书案例。这篇把排查全过程摊开,方法比结论更有参考价值。背景交代:二十来人创业公司,系统是自管自维的低代码应用,没有专职运维,排查全靠自己和一套日志。正因如此,这篇的方法论都是穷人打法:不依赖高端工具,靠打点和耐心。
接到反馈头一件事不是猜测,是抓数据。打开浏览器的请求面板,把工时表单的加载过程录了一遍:首屏总耗时三秒二,其中四个请求占了二秒八。慢的不是页面渲染,是后端接口。
再往后端追:应用日志里这四个接口的耗时分布,两个在五百毫秒上下,一个一点二秒,一个一秒一。目标清晰了——把这四个接口的耗时拆开看,慢在哪一段。
拆接口耗时有固定框架:总耗时等于网络传输加网关排队加应用处理加下游等待。应用处理再细分:控制器逻辑、数据库访问、外部调用。每一段单独计时,耗时分布一目了然。
我们在网关和应用层各加了分段计时日志,格式统一:请求进来打点,每过一层打点,出去再打点。日志聚合后,每个接口的分段耗时中位数、九分位数都有。这套打点是后来所有性能排查的基础设施,一次投入反复受益。打点本身有讲究:统一格式是为了聚合,关键层级是为了不漏,耗时口径是为了可比。乱打的点只是日志,成体系的点才是地图。
三个问题的排查顺序有讲究:先查慢查询——数据库最常见最典型,命中概率高;再查外部调用——看拆解数据里下游等待段的长短;最后追偶发尾巴——它需要时间窗和数据量。从高概率到低概率,从稳定复现到偶发,排查顺序本身就是效率。
头一个接口的一点二秒,拆解后数据库访问占一点一秒。抓出慢查询一看:任务列表的查询没走索引,表数据涨到八万条后,每次查询都在全表扫描。
为什么没走索引?查询条件里对时间字段做了函数包装,索引直接失效。这是经典坑:索引建了,查询写法让它成了摆设。修复很简单,改写查询条件让索引生效,耗时从一点一秒降到四十毫秒。
索引失效的隐蔽性在于它不报错,只是悄悄慢。数据量小的时候毫无察觉,数据涨起来 突然崩给你看。这个案例之后,慢查询监控进了上线检查清单。这里还有个插曲:修复后我把所有核心表的索引使用情况整体查了一遍,又抓出两个建了索引却从没被用到的查询。索引不是建完就功德圆满,它和查询写法是一对,要配对体检。
第二个接口的一秒一,拆开看是两个外部调用串行等待:查一次成员服务零点四秒,再查一次权限服务零点五秒,加起来快一秒。这两个调用没有依赖关系,纯粹是代码写得顺手,一个接一个。
改成并行:两个调用同时发出,总耗时取决于最慢的那个,接口从一秒一降到五百毫秒。改动只有几行代码,收益立竿见影。串行改并行是性能优化里的低垂果实,前提是你得先看见串行——耗时拆解就是那双眼睛。并行改造还有个注意事项:两个调用失败时要独立降级,别让一个外部服务的抖动把另一个的结果也拖死。性能和稳定性,经常要在同一处代码里一起想。
修完前两个,接口快了大半,但偶发的高耗时还在:九分位数上有个诡异的尾巴,偶尔蹿到三秒。跟踪单个慢请求的日志,发现耗时卡在获取数据库连接上——连接池偶尔被占满,请求排队等连接。
谁占满了连接池?追查发现是个定时任务:每晚跑的数据汇总,一口气开几十个连接不释放。白天连接池够用,定时任务和业务高峰偶尔撞车,就出现偶发卡顿。
修复分两步:定时任务改成分批处理,单批连接用完即还;连接池加上限和等待超时,占满时快速失败而不是无限排队。之后九分位数回归平稳,偶发卡顿消失。回头看这个问题最值得记一笔的是它的隐蔽性:如果排查那天晚上定时任务没跑,连接池数据一切正常,我可能就收工了,问题继续潜伏。偶发问题的排查,时机也是缘分,但打点齐全的系统,缘分来了就能抓住。
排查期间每天在用户群里同步进展,哪怕只是今天排除了一个方向。用户的怨气大多来自黑箱:不知道有没有人在管、什么时候能好。每天一句话的同步,把等待变成了参与,修好后群里反而热闹地庆祝了一把。技术问题和人心问题,常常要一起修。
这轮排查最值钱的产出不是修了三个问题,而是那套分段计时日志。从此任何性能问题,先看耗时分布:网络段、应用段、数据段、下游段,哪段异常追哪段。没有分段计时,性能排查就是抽签;有了分段计时,就是按图索骥。
建议所有团队把分段计时做成标准件:格式统一、自动注入、集中聚合。这笔基建投入,在头一次排查里就能回本。后来团队养成的习惯是:新接口上线,打点先行。没有分段计时的接口不允许发布,就像没装仪表盘的车不允许上路。
平均耗时是性能数据里最大的谎言。这轮偶发卡顿如果只看平均值,完全发现不了——平均值被正常请求稀释得漂漂亮亮。中位数看典型体验,九分位数看长尾体验,两个都稳才算真稳。用户抱怨卡而平均值正常时,八成长尾有问题。这个经验后来救过一次场:报销系统上线后平均值完美,但用户零星抱怨卡。直接查九分位数,果然一条长尾,追进去是附件上传偶发超时。统计口径选对,问题自己招供。
每个问题修复后都做了复测:同口径的耗时数据前后对比,确认改善来自修复本身。三个问题修完,工时表单首屏从三秒二降到零点八秒,提交从五秒降到一秒内。有复测数据,功劳账算得清,也防止改了半天其实没生效的自嗨。复测数据还带来一个意外好处:三组前后对比的数字发到全员群,比任何语言都直观地证明了技术团队在做事。数据是排查的终点,也是沟通的起点。
三个问题修完不是终点,重点是让同类问题不再需要人来发现。三道闸的共同思路:把人的经验固化成系统的规则。慢查询监控是眼,性能回归是门,连接巡检是岗。眼门岗齐备,团队的睡眠质量才有了制度保障。
数据库慢查询阈值定为五百毫秒,超线告警。问题一的隐形全表扫描,以后活不过一个告警周期。监控的意义就是把需要运气发现的问题,变成必然被发现的问题。人的注意力是稀缺资源,把重复的盯守交给机器,人去做只有人能做的判断。
给核心接口定了性能基线:耗时中位数和九分位数不得超基线两成,超了发布拦截。问题二那类顺手写出的串行调用,在回归测试里就现形。基线随优化动态下调,性能只进不退。
连接池的使用率、等待数进了日常巡检表,每周看一眼。问题三那种定时任务和业务撞车的隐患,巡检时能提前看到使用率异常。三个问题三种防法,闸门比记性可靠。
这轮排查最后留下一句团队共识:卡顿不是玄学,是耗时分布里等着被读出来的答案。数据给足,人人都是侦探。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。