Unix调试总卡壳?5个隐性陷阱90% DBA都踩过
|
跟产品吵。去年三月那个接口背锅的事,我原以为查 strace 就能结案——结果卡在 setsockopt 的返回值上整整十八小时,最后发现是内核模块里一个早被标记 deprecated 的 flag 在新 glibc 里悄悄变了语义。这个我敢打包票,翻过三次源码树,连注释里那行“for legacy compat only”都截图发群里了。 灰度开关没推完。前端老张蹲在测试环境刷了四十七遍页面,每次 response header 里的 X-DB-TraceID 都差两位,我们仨盯着 Wireshark 看到凌晨三点,谁也没提要不要先去倒杯水。后来真有人去了——运维小陈,他端着那个印着公司 logo 的蓝边瓷杯回来时,监控大屏右下角的 network.latency.p95 曲线突然抖了一下,像被什么硌了牙。删掉这句?意思完全不受影响。但我就得写。 你查 ulimit -a 查到天亮,查 /proc/$PID/limits 查出幻觉,查 sysctl -a | grep somaxconn 查到想删库……可没人告诉你 net.core.somaxconn 这个值在容器里根本不是你起的那个 pod 自己改的,是宿主机 kubelet 拿着 apiserver 里 annotation 覆盖的——还是只覆盖一半。我见过两个 DBA 同时改同一个参数,一个改容器内,一个改节点级,吵了三天,最后发现他们连用的都不是同一个 Kubernetes 版本分支。无所谓,反正 patch 早就合进 master 了,只是 doc 里压根没提这事。 oncall 手机震了一下。不是告警,是 PM 发来消息问「这个 SQL 能不能加 hint 强制走索引」。我回了个「加了也走不了」,然后切回 terminal 继续看 perf script 输出里那堆重复出现的 futex_wait_cancelable,没注意自己把 -g 选项漏写了,白跑了十一分钟。这种事多得很:明明 pg_stat_statements 显示慢查询只有三十七次调用,可 top 里 postgres 进程 RSS 却顶到了十一点七 GB;明明 tcpdump 看包全到了,应用层就是 read() 不返回;明明 /dev/shm 下文件都 clean 了,pg_ctl restart 还是报 Failed to bind shared memory segment。我说不准这是不是 shmget() 和 mmap() 的实现差异导致的竞态残留——但我知道,你重启实例之后再看 /proc/sys/vm/shmall,它准比你上次记下来的数字小三百七十二次调用的量级。
文章配图,仅供参考 KPI 里有项叫「故障根因定位时效」,要求 TTR ≤ 四十五分钟。上季度我达标了十七次,其中有九次是靠直接 kill -9 掉疑似进程抢时间,剩下八次全是靠复现——不是靠分析。复现不难:建同样结构的表、灌同样分布的数据、跑同样业务脚本,再在第十八毫秒插个 sigstop……可问题是你怎么知道是第十八毫秒?没人教,全靠踩。老李去年在生产库上试了七次才摸到那个内存碎片临界点,第八次他没试成,因为测试库磁盘满了,他顺手删了 /var/log/postgresql/.log..gz,结果删出个归档中断报警,又扯进另一个 case。这事儿后来没进报告,因为归档本身就不该依赖本地磁盘。最要命的是那个「反常识」的:你以为你在 debug 连接池耗尽,其实问题出在客户端的 TCP keepalive interval 设成了零,而服务端 kernel 参数 net.ipv4.tcp_fin_timeout 是三十秒——这俩撞一起,就会在连接数还没爆之前,让 pg_stat_activity 里挤满状态为 idle in transaction (aborted) 的假死会话。我查了两天,看到 pg_terminate_backend() 日志里反复打印“server closed the connection unexpectedly”,就认死了是服务端扛不住。结果呢?抓包一看,客户端发完最后一个 FIN,隔了二十九秒半才发 RST。冷眼?呵,我那天盯着 wireshark 里那两帧间隔,手抖得连滚轮都调不准时间刻度。 测试同事阿琳拿 Postman 手动发请求时,顺手把 Accept-Encoding 改成了 gzip,结果响应快了四倍——当然,这和数据库毛关系没有,但我们就这么在会议里讨论了二十三分钟,因为 PM 说「既然 gzip 行,为什么不能在 JDBC URL 里加 useCompression=true」。我当场没说话。我不是反对,我是怕一开口就要解释 mysql-connector-java 的 compression buffer 是单线程同步刷的,刷崩过三个线上实例,其中两次还带走了同 JVM 里的 HikariCP 的 close() 方法……算了,这句删掉也不影响。咖啡杯上印的公司 logo 是斜的,一直没换新的。 现在我debug前必做三件事:第一,cat /proc/sys/kernel/pid_max 看一眼,不是查值,是看它是不是被改过(改过说明有人动过内核参数);第二,在 strace -p 后头多加个 -T,哪怕慢点,至少知道是 syscall 入口卡还是返回卡;第三,打开 /proc/$PID/status,直接搜 CapEff,别信 docker run 时写的 --cap-add,有些 capability 在 init 容器启动时就被 drop 了,你还得去查 kubectl get node xxx -o yaml 里 annotations 有没有 security.alpha.kubernetes.io/unsafe-sysctls。这些方法没文档,但管用。至于那个优胜劣汰……对,它真有用。上周五又一个新人栽在 /proc/sys/net/core/somaxconn 和 /proc/sys/net/core/netdev_max_backlog 的顺序依赖上,我没拦,让他自己跑三遍 perf probe -a 'tcp_v4_do_rcv' 再回来。他没回来,去茶水间了。我也懒得喊。 (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


DBA熬夜改表?这本MySQL手册让设计运维一步到位
嵌入式开发中,逻辑清晰代码如何缩短70%调试时间?
Unix高效包管理:DBA眼中的创业者技术刚需