创业公司线上服务频繁崩溃,十年老板总结全链路排查方案
凌晨三点手机震得床头柜嗡嗡响,不用接都知道,线上又崩了。
创业第七年,这种场面早就麻了。最开始团队拢共五个人,连专职运维都没有,前端后端打包兼运维,出了事故所有人光着脚往电脑前冲,查半小时发现是某台云主机磁盘满了,业务日志生生打满了 100G 的系统盘,MySQL 直接挂死写不进去。这种低级坑踩多了,慢慢攒出来一套野路子排查流程,不是大厂那种标准 SOP,也没什么高大上的方法论,胜在快,适合人手不足、监控体系半残的中小团队。
别信那些什么从上到下按链路一步步排查的狗屁教程,真到了线上着火用户炸群的时候,谁还按顺序来?哪可疑先扎哪,先止损再查根因,这是创业公司保命的第一准则。
先说说最容易被忽略的连接池那点事。上个月刚踩过,新上的一个报表服务,白天低峰期好好的,一到晚上批量跑任务就卡成狗,接口 RT 直接飙到十几秒,最后直接挂死。一开始新人上来就查 SQL,explain 了半天说索引都命中了,没慢查询。我让他去看一眼连接池状态,show processlist一拉,好家伙,一百多个 Sleep 连接占着不放,连接池直接打满,新请求根本拿不到连接。
查了半天才发现,新写的导出接口里开了事务,中间调了个第三方接口超时,事务一直没提交,连接就占着不放。高峰期一冲,连接池直接耗尽。很多人写代码根本不关注连接池配置,maxActive 拍脑袋写个 200,也不管数据库扛不扛得住;要么就是事务边界乱开,一个接口里套着远程调用还开着事务,不出事才怪。
排查的时候别光盯着代码,先敲一行命令看看连接数正不正常,比你瞎改半小时代码管用。
bash
运行
netstat -anp | grep 3306 | wc -l说起来上次更离谱,网关服务每隔两天准点 OOM 一次,堆内存拉满之后直接重启,告警消息天天凌晨发。堆 dump 拉下来用 MAT 分析,大对象全是字节数组,查了半天才定位到是有人把请求体全打进日志里了,文件上传接口连整个文件流都往 logback 里塞,几 M 的文件一个请求打一次日志,高峰期分分钟把堆内存撑爆。
很多人排查问题总喜欢加日志,加的时候一时爽,线上火葬场。日志打多了不仅占磁盘拖 IO,严重的时候直接能把服务搞崩。真要排查 OOM,别上来就瞎调 JVM 参数,先拉 dump,拉下来慢慢分析,比你猜来猜去靠谱。Xmx 设再大,架不住代码里写 bug 造大对象。
bash
运行
jmap -dump:format=b,file=heap.hprof pid别总盯着应用层,网络抽风的时候比代码 bug 还恶心。去年有阵子服务间调用超时率突然飙到 30%,所有接口都慢,查了一圈代码没发版,数据库 CPU 内存都正常,缓存也没问题,折腾了俩小时,最后 mtr 测了一下跨可用区的路由,中间节点丢包率快 20%,云厂商的交换机出故障了。
新人最容易犯的错就是一上来就改代码,觉得肯定是自己写的逻辑有问题。真遇到全链路普遍超时,先 ping 一下依赖的服务,telnet 测端口,tcpdump 抓个包看看,先把网络、DNS 这些基础设施的问题排除了,再往代码层面查。省得忙活半天,最后发现是机房光缆被挖断了。
缓存这东西,救得了你也能坑死你。前年做一次大促,提前压测了好几轮,觉得稳了,结果活动刚开始十分钟数据库直接被打挂。查下来是热点商品的缓存集体过期,流量直接击穿打到库上,MySQL 瞬间连接数打满,直接雪崩。
当时哪有空查根因,先临时在网关层给热点接口加了本地缓存,扛过高峰期再说。事后复盘,缓存过期时间全设的统一值,一到点集体失效,不雪崩才怪。后来所有缓存过期时间都加了随机偏移,热点数据提前预热,再加上本地缓存兜底,才算稳住。还有 Redis 的内存淘汰策略,很多人默认用 volatile-lru,结果热点数据没设置过期时间,内存一满先把热点数据清了,越忙越乱。
说起来监控这东西,创业公司最容易走两个极端,要么啥都没有,服务挂了用户反馈了才知道;要么整一堆花里胡哨的,Prometheus、Grafana、SkyWalking 全装上,最后连告警阈值都没配,出了事还是靠人喊。
我们最开始就装了个 Zabbix,只看 CPU 内存磁盘,服务挂了半天都发现不了。后来慢慢加了业务指标,QPS、RT、错误率,核心接口的成功率,才算能在用户炸群之前先收到告警。但也踩过坑,SkyWalking 刚搭的时候采样率设了 100%,ES 磁盘三天就满了,差点把日志服务搞挂。后来改成正常流量 10% 采样,异常请求全采样,既能留足排查线索,也不至于把存储打爆。
真要排查问题,监控面板扫一眼,错误率有没有突增,RT 有没有飙高,QPS 有没有异常流量,基本能先定个大概方向。别上来就登服务器翻日志,先看大盘,缩小范围。真要翻日志,也别瞎翻,先过滤异常栈,比你逐行看快得多。
bash
运行
tail -f error.log | grep -A 20 Exception很多人一遇到线上崩溃就慌,上来就想找根因,用户都炸锅了还在那逐行 debug。创业公司没那么多容错空间,第一优先级永远是止损。能重启先重启,能切流量先切流量,能降级先降级,把服务先拉起来,用户能用了,再慢慢查原因。
当然不是让你瞎重启,重启之前先留现场。线程栈打一下,堆 dump 拉一下,错误日志备份一下,数据库当前的连接、慢查询快照存一下。不然重启完啥证据都没了,下次该崩还得崩,永远查不出来根因。
我见过太多开发,出事先重启,重启完好了就完事了,结果过两天同样的问题再出现,还是一脸懵。线上问题不怕偶发,怕的是重复踩同一个坑。
还有个容易被忽略的点,主从延迟。之前有个业务,写操作走主库,读完马上写,结果读从库的时候数据还没同步过来,导致业务逻辑异常,反复重试,最后把接口打挂了。很多人觉得主从延迟就几百毫秒,无所谓,真到了高峰期,主从延迟几秒都有可能,尤其是大事务、DDL 操作之后。
排查的时候别光看主库状态,也看看从库的 Seconds_Behind_Master,业务逻辑里别写完马上读从库,核心场景强制走主库,比你事后排查半天管用。
零零散散写了这么多,其实线上崩溃的原因翻来覆去就那几类,但每次出问题的姿势都千奇百怪。可能是运维改 Nginx 少写了个分号,可能是前端写了个死循环疯狂调用接口,可能是第三方服务挂了导致连锁反应,也可能就是云主机突然抽风。
没有什么万能的排查方案,说白了就是踩的坑多了,扫一眼监控大概就知道是哪层的问题。创业公司没人没资源,不可能像大厂那样搞全套稳定性体系,先把最基础的监控、日志、应急流程搭起来,能覆盖 80% 的问题。
就先写到这,想起来啥再补。线上的坑永远踩不完,共勉。
