Laravel项目php-fpm CPU占用过高求助,已执行多项优化
问题概况
创建了一个从Redis缓存获取数据的测试API,访问domain/api/test时CPU占用最高达15%。VPS配置为2物理核心+2逻辑核心,已执行Laravel优化(php artisan optimize、优化自动加载)、关闭debug模式、设置APP_ENV=production、安装opcache和apcu,但问题仍存在。
测试路由及CPU占用情况
- 测试路由1(CPU占用约10%):
Route::get('test', function(){ return \Cache::remember('test', 6666, function () { return \App\Models\ActivityLog::where('id','<',3355) ->paginate(20, ['*'], 'page', 2) ->toJson(); }); });
- 测试路由2(CPU占用9%):
Route::get('test', function(){ return "string"; });
服务器配置
VPS配置
2 CPU(2物理核心、2逻辑核心)
php-fpm进程状态
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND www 20 0 S 10.7 3.6 0:49.75 php-fpm
php-fpm配置
pm = dynamic pm.status_path = /phpfpm_74_status pm.max_children = 50 pm.start_servers = 5 pm.min_spare_servers = 5 pm.max_spare_servers = 30
问题排查与解决方案
1. 先明确:当前CPU占用是否属于异常?
你的VPS是2核,单php-fpm进程占用10%左右,换算到单核心是20%左右(总CPU算力为200%),这个数值在Laravel框架的请求处理中不算极端异常——毕竟Laravel作为全栈框架,即使是返回字符串的请求,也需要完成框架初始化、路由匹配、中间件执行、响应处理等一系列流程,这些都会消耗CPU资源。
2. 针对性优化建议
(1)优化php-fpm进程数配置
当前pm.max_children=50对于2核VPS来说过高,过多进程会导致CPU上下文切换频繁,反而增加消耗。建议调整为:
pm.max_children = 8 pm.start_servers = 2 pm.min_spare_servers = 2 pm.max_spare_servers = 4
(2核服务器的pm.max_children一般建议设为核心数的2-4倍,避免进程过多抢占资源)
(2)深度优化Opcache配置
确保Opcache真正生效,检查php.ini中的配置:
opcache.enable=1 opcache.enable_cli=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=4000 opcache.revalidate_freq=60 opcache.fast_shutdown=1
执行php -i | grep opcache确认配置生效,同时通过php-fpm状态页查看Opcache命中率,确保接近100%。
(3)简化路由处理逻辑
即使是测试路由,Laravel默认的全局中间件(比如StartSession、VerifyCsrfToken等)依然会执行。如果是纯API场景,可以:
- 把测试路由放到
api路由文件中,API路由组默认已排除Session、Csrf等不必要的中间件 - 手动为测试路由移除不需要的中间件:
Route::get('test', function(){ return "string"; })->withoutMiddleware([ \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Session\Middleware\StartSession::class ]);
(4)排查Redis缓存的实际开销
虽然用了Cache::remember,但首次请求会执行数据库查询并序列化存入Redis,后续请求需要从Redis读取并反序列化数据。可以单独测试:
- 直接返回预生成的JSON字符串(跳过Redis读取),看CPU占用是否下降
- 检查Redis连接延迟,通过
redis-cli ping查看响应时间,确保本地Redis连接稳定
(5)启用PHP的APCu缓存
确保APCu在Laravel中被正确使用,修改config/cache.php:
'array' => [ 'driver' => 'array', ], 'apcu' => [ 'driver' => 'apcu', ],
并将默认缓存驱动临时改为apcu,测试CPU占用是否有变化,排除Redis的额外开销。
3. 性能分析工具定位瓶颈
如果以上优化后仍不满意,可以用工具精准定位:
- 使用
xhprof或tideways分析请求的函数调用耗时,找到CPU消耗最高的环节 - 使用
top -Hp [php-fpm-pid]查看php-fpm进程的线程CPU占用,确认是否有某个线程持续高负载 - 临时开启Laravel的
Query Log,确认数据库查询是否有隐藏开销(即使缓存命中,也要检查是否有意外查询执行)
内容的提问来源于stack exchange,提问作者kennofizet

