qps翻倍靠优化五个关键点:with()字段限制、select()精简、config:cache和route:cache及时重跑、模板避免$loop->last。这五处改完多数项目qps直接翻倍。

QPS 翻倍不是靠加服务器或升 PHP 版本,而是盯死五个关键点:with() 字段没限制、select() 漏写、config:cache 没跑、route:cache 没跑、模板里用了 $loop->last。这五处改完,多数项目 QPS 直接翻倍。
with() 预加载必须带字段限制,否则等于没写
写了 with('user') 不代表解决 N+1,它默认查 SELECT * FROM users。如果 User 表有 bio(TEXT)、avatar(长路径)、settings(JSON),而你只用 $user->name,那每次预加载都在浪费带宽和内存。
- 关联字段必须显式指定:
with(['user' => function ($q) { $q->select('id', 'name', 'email'); }]) - 主表也得精简:
Post::select('id', 'title', 'user_id')->with(...)->get(),否则SELECT *会拖垮 MySQL 缓冲池和网络传输 - 嵌套三层以上(如
posts.tags.users)极易触发笛卡尔积,改用两段式:先pluck('id'),再whereIn()批量查关联
模板里慎用 $loop->last,它会让分页失效
$loop->last 和 $loop->remaining 要求 Laravel 把整个 Collection 加载进内存后才能计算。对 paginate(20) 来说没问题,但对 cursorPaginate(50) 或 chunkById() 场景,它根本不知道“最后”在哪——强行用会把游标分页退化成全量拉取。
-
$loop->index和$loop->iteration是安全的,不依赖集合长度,可放心用 -
$items->count()在游标分页下不准,应改用$items->hasMorePages()配合前端逻辑判断 - 列表末尾加分割线?用 CSS 的
:last-child更轻量,不触发 PHP 层全量加载
config:cache 和 route:cache 不是“部署完再跑”,是“改完立刻重跑”
生产环境每多一次未缓存的请求,就多 8–12ms 启动开销。这不是理论值,是 php artisan tinker 里 microtime(true) 实测出来的差距。
-
config:cache读的是当前.env+config/文件,改了.env必须重跑,否则新变量不生效 -
route:cache不支持闭包路由;API 中用了Route::middleware()->group()且中间件里含闭包,会静默失败——用php artisan route:list对比缓存前后数量 - CI/CD 流水线里,部署脚本末尾必须加
php artisan config:cache && php artisan route:cache,不能只在本地跑
Session 和缓存驱动选错,QPS 直接腰斩
本地开发用 file 驱动没问题,但上线后若还用 file 或 database 存 session,每个请求都要磁盘 I/O 或 DB 连接,QPS 直接腰斩。
- 生产必须切到内存型驱动:
redis是首选,但要注意session和cache共享同一 Redis 实例时,设置不同prefix,否则cache:clear可能清掉 session -
memcached也行,但不支持原子性 TTL 更新,session 过期逻辑略弱于 Redis - 检查
config/cache.php中default和stores.redis.connection是否指向正确 Redis DB(比如 DB 0 存缓存,DB 1 存 session)
真正卡 QPS 的,从来不是 for 循环语法本身,而是循环里触发的隐式查询、模板中未察觉的 Collection 全量加载、以及配置层反复 require 的开销。这些点不盯死,加服务器、升 PHP 版本都只是给慢车换轮胎。











