从 Exception 堆栈里读"暗语":我整理的 6 种高频报错与第一反应排查清单

小助手
小助手 版主圣羽星庭 勋望元宿志愿先锋
社区管理
站长杂谈 62 浏览 0 回复

昨晚又翻到去年写的排查笔记,发现大部分 Exception 其实都在重复犯同样的认知错误。不是不会看堆栈,是堆栈信息一多就慌,慌了就乱试。今天把这两年攒下的几种高频场景整理成"第一反应清单",下次再遇到至少知道先盯哪行。

一、Class 'xxx' not found:别急着 composer update,先看加载时机

这报错最坑的是"假不存在"。类文件明明在目录里,命名空间也对,但就是在某个钩子或中间件里抛这个错。我的经验:先区分是编译期找不到还是运行期找不到。

运行期报错,重点看堆栈里报错位置上一帧的调用方。如果是框架核心抛的,大概率是服务注册顺序问题——你的类在容器初始化之后才尝试加载。ThinkPHP 里常见场景:自定义驱动在 `app_init` 之前就被触发了,或者多应用模式下应用目录没识别对。

第一反应不是更新依赖,是打印 `get_included_files()` 确认文件到底有没有被 require 进来。

二、Trying to access array offset on value of type null:链式调用的"断点"在哪

PHP 7.4 之后这报错变严格了。以前能静默返回 null 的地方现在直接炸。最烦人的是这种写法:

$user->profile->avatar['url']

堆栈指向的是最后一行,但真正的 null 出在哪一层?我的习惯是先把链式拆开,每层 isset 或者 null 合并运算符 `??` 兜底。但排查阶段要定位根因,得从堆栈往上数第二帧开始看,那里才是赋值逻辑所在。

如果是 API 接口返回的数据结构变了,这报错往往批量出现。先看最近有没有改过上游接口或者缓存键名。

三、SQLSTATE[HY000] [2002] Connection refused:别只盯着 MySQL 服务

这报错新手第一反应是 `systemctl restart mysql`,但生产环境很多时候服务根本没挂。我踩过的几个真凶:

• 容器环境里 MySQL 容器 IP 变了,配置文件里的 host 还是旧 IP
• 连接数打满,新请求直接被拒,不是服务没开
• 宝塔面板里开了"数据库远程访问"开关,本地 socket 反而连不上了
• 最隐蔽的一次:云厂商维护后换了端口,安全组放行的是旧端口

现在我的排查顺序:telnet 端口通不通 → `show processlist` 看连接数 → 对比配置文件与实际监听地址 → 最后才重启服务。

四、Fatal error: Allowed memory size:不是加内存这么简单

128M 不够就改 256M,256M 不够改 512M——这做法治表不治本。遇到过最离谱的是定时任务里循环查询,每次查询都往数组里塞全量数据,内存是指数级涨的。

看堆栈如果终止在 `array_merge` 或者某个集合操作上,基本是内存里的数据结构失控了。如果终止在图片处理或者 Excel 导出,那是单个大对象的问题。

我的快速判断:报错前有没有循环?循环里有没有累积数据?有的话先改游标查询或者分批处理,而不是调 `memory_limit`。

五、HTTP 500 但日志里没有 Exception:去找 error_log 的"隔壁邻居"

这是最消耗时间的场景。框架日志干干净净,页面就是 500。大概率是 PHP 致命错误在框架捕获之前就已经发生了,或者错误被导向了另一个日志文件。

排查路径:Nginx 的 `error.log` → PHP-FPM 的 `slow.log` 和主错误日志 → 系统 `syslog`。有一次是 OPCache 的内存耗尽,框架层完全没感知,只在 PHP-FPM 日志里有一行 `Unable to allocate memory for pool`。

现在部署新环境时,我会刻意把这几个日志路径记在同一个地方,免得半夜救火时到处找。

六、验证器里 "rule not exist":自定义规则注册时机

ThinkPHP 里自己写了验证规则,配置里也加了,但报错说规则不存在。堆栈指向 `Validate::is` 方法时,说明规则名解析成功了,但对应的类没加载。

问题通常出在:规则类用了自动加载,但类名和规则名映射没对上;或者在命令行模式下运行,某些服务没初始化。我现在的做法是自定义规则一律显式注册到 `Validate` 里,不依赖自动发现,省得环境差异导致玄学问题。

最后

整理这个清单的过程,其实也是逼自己承认一件事:很多"诡异 bug"并不诡异,是排查时没有固定顺序,被焦虑带着乱跑。现在遇到报错,我先深呼吸,然后按"堆栈最后一帧 → 上一帧调用方 → 日志上下文 → 最近变更"这个顺序来,效率比之前高很多。

你们有没有那种"每次遇到都愣一下"的 Exception?欢迎补充,我攒够一波再更新个续集。

评论0
回复 · 0
还没有回复
微信客服 微信客服