周五晚上十一点,手机收到云监控告警:CPU 使用率 98%,持续 5 分钟。第一反应是被打了?登录服务器开始查。把整个过程记下来,CPU 飙高的排查思路基本都能套用。
第一步:先看是谁在吃 CPU
top # 按 P 键按 CPU 排序
看到一个 java 进程吃满了 190%+(机器 2 核)。不是陌生进程,是业务进程本身,先排除了挖矿(挖矿通常是伪装成系统进程的陌生东西)。再看负载是 CPU 密集还是 IO 等待:top 里 %wa 很低、%us 很高,纯计算型。
第二步:定位到线程
top -Hp 21409 # 看 21409 这个进程里的线程
# 找到最吃的线程 PID,比如 21537
printf '%x
' 21537 # 转成十六进制 0x5421
然后拿线程栈。Java 进程直接 jstack:
jstack 21409 | grep -A 30 0x5421
栈帧显示这个线程卡在一个正则匹配上,而且连抓三次栈都在同一个位置——典型的正则回溯失控。去代码里搜,是新上的一个手机号校验,正则写成了:
^(1[3-9]\d{1,}\d*)$
看着没毛病,但当输入是超长数字串(有用户把订单号粘进去了,160 多位)时,\d{1,}\d* 这种写法会让引擎疯狂回溯。Java 正则没有自动的回溯上限,就 CPU 打满了。
第三步:止血与修复
- 临时止血:限流该接口,CPU 慢慢回落,没有重启服务
- 修复:正则改成
1[3-9]\d{9}精确匹配,并且在入口处先校验长度不超过 11 位 - 复盘:给所有接收用户输入的正则加了长度前置校验。正则这东西,写的时候只想着"匹配什么",很少想"不匹配什么的时候要算多久"
非 Java 的场景同理:Python 可以用 py-spy dump,Go 用 pprof,C/C++ 上 perf top。套路是一样的——先进程、再线程、再栈帧,三步之内必见真相。

Comments 3 条评论
正则回溯失控,这个真的隐蔽
jstack 连抓三次栈找同一位置,思路好
还得想不匹配时要算多久,精辟