PHP超时与Laravel生命周期超时不同,盲目调大max_execution_time治标不治本;应聚焦框架层耗时监控、慢查询优化、配置/路由缓存及FPM网关超时协同调整。

PHP 脚本执行超时(max_execution_time)和 Laravel 请求生命周期超时是两回事,直接改 php.ini 的 max_execution_time 通常治标不治本,甚至掩盖真实瓶颈。真正该调的,是框架层可观察、可干预、可定位的执行耗时点。
PHP 层级超时设置不能乱动
修改 max_execution_time 或用 set_time_limit() 强行延长脚本运行时间,只会让慢请求更“稳”地拖垮服务器。尤其在 FPM 模式下,它还受 request_terminate_timeout(php-fpm.conf)和 request_slowlog_timeout 双重约束。强行设为 0(不限制)极易引发进程堆积、内存泄漏未被及时发现等问题。
- 线上环境建议保留默认 30 秒或设为 60 秒,仅对明确需要长时处理的队列任务或导出接口单独调整
- 不要在控制器里写
set_time_limit(0)—— 这会绕过 FPM 的超时控制,导致 worker 卡死 - 若必须延长,优先在 Nginx 的
fastcgi_read_timeout和 php-fpm 的request_terminate_timeout中同步调整,否则 PHP 层还没超时,网关已断连
Laravel 中间件里加执行时间监控比改超时更有用
与其等超时发生,不如提前知道哪段代码快撑不住了。用 SebastianBergmann\Timer\Timer 在中间件中埋点,能精准捕获真实耗时,且不影响业务逻辑。
- 安装:
composer require phpunit/php-timer - 在
app/Http/Middleware/PerformanceMonitor.php中写:$timer = new SebastianBergmann\Timer\Timer(); $timer->start(); $response = $next($request); $duration = $timer->stop(); Log::warning('Slow request detected', [ 'path' => $request->getPathInfo(), 'method' => $request->method(), 'ms' => $duration->asMilliseconds(), 'threshold' => 2000, // 超过 2s 记 warning ]); - 配合日志告警(如 Sentry、Laravel Horizon 日志监听),比改超时阈值更能驱动优化动作
数据库查询慢才是 Laravel 超时的真凶
90% 的“超时”实际是某条 SQL 执行太久,比如没加索引的 WHERE、没预加载的 N+1、或 LIKE '%xxx%' 全表扫描。这类问题改 PHP 超时毫无意义。
- 开启慢查询日志:
DB::enableQueryLog()+dd(DB::getQueryLog())(仅开发环境) - 生产环境用
DB::listen()记录耗时超 500ms 的查询到日志 - 检查是否用了
with()预加载关联模型,而不是在循环里反复查$post->user - 避免在 Eloquent 查询中使用
whereRaw()或复杂闭包,容易绕过查询优化器
配置缓存和路由缓存失效会导致“假性超时”
没跑 php artisan config:cache 或 php artisan route:cache 的 Laravel 应用,每次请求都要重新读取几十个 PHP 配置文件、解析全部路由定义 —— 这本身就会吃掉 50–150ms,高并发下积少成多,看起来像“随机超时”。
- 生产环境必须执行:
php artisan config:cache && php artisan route:cache - 修改了
config/下任意文件后,要立刻php artisan config:clear并重缓存,否则新配置不生效还可能引发隐性错误 - 路由缓存不支持闭包路由,如果用了
Route::get('/test', function () { ... }),缓存会静默失败,退回到低效解析模式
超时不是参数,是信号。它背后大概率是没被发现的慢查询、重复加载的配置、或失控的中间件链。盯住 X-Laravel-Execution-Time 响应头、DB::getQueryLog() 输出、以及 bootstrap/cache/ 下的缓存文件是否存在,比调大 max_execution_time 实在得多。











