PHP 8.1 查询慢主因是SQL设计或Eloquent误用,应聚焦N+1查询、索引命中、字段精简三方面:开启查询日志定位重复SQL,补全with链避免深层访问,WHERE/JOIN字段建索引,禁用函数导致索引失效,改whereRaw配合复合索引,select指定字段,优先with预加载,大数据用cursorPaginate。

PHP 8.1 数据库查询慢,90% 不是 PHP 版本问题,而是 SQL 层设计或 Eloquent 使用不当导致的。定位要快、修复要准,重点盯住“查了几次”“查了什么字段”“有没有走索引”这三件事。
一、快速定位 N+1 查询
别靠猜,直接在开发环境控制器里加两行代码:
- 调用 DB::enableQueryLog() 开启日志
- 执行查询后用 dd(DB::getQueryLog()) 查看实际发出的 SQL
如果看到大量重复的 select * from users where id = ?,说明 N+1 已发生;理想情况应只有两条 SQL(主表 + 预加载关联表)。常见失效点包括:
– 模板中用了 $post->user->profile->avatar,但只写了 with('user'),必须显式写成 with('user.profile')
– 循环中调 $post->comments->count(),应改用 withCount('comments')
二、检查 WHERE/JOIN 字段是否命中索引
全表扫描是慢查询头号原因。重点检查:
立即学习“PHP免费学习笔记(深入)”;
- 所有 WHERE 条件字段 是否单独或组合建了索引
-
JOIN 关联字段(如
posts.user_id)是否加了索引 - 避免在字段上套函数,例如
WHERE DATE(created_at) = '2024-01-01'会跳过索引,应改写为WHERE created_at >= '2024-01-01' AND created_at
复合索引要注意字段顺序:高频等值查询字段放前面,范围查询字段放后面(如 (status, created_at) 比 (created_at, status) 更有效)。
三、用 whereRaw + 参数绑定优化复杂条件
当 Eloquent 自动拼接的逻辑导致执行计划变差时,whereRaw 是安全有效的绕过手段,但必须满足两个前提:
- 使用数组传参绑定,绝不能字符串拼接(防 SQL 注入)
- 对应字段已建好索引,否则加速无效
例如把嵌套 or 的写法:
→ 原始低效写法(可能无法用索引)$query->where('status','!=','cancelled')<br> ->where(function($q) {<br> $q->where('created_at','>', now()->subDays(30))<br> ->orWhere('priority','high');<br> });→ 改为 whereRaw + 复合索引(如 (status, created_at, priority))
$query->whereRaw(<br> '(status != ? AND (created_at > ? OR priority = ?))',<br> ['cancelled', now()->subDays(30), 'high']<br>);
四、精简字段与预加载控制
少查就是快查:
- 用 select(['id', 'title', 'user_id']) 替代
select('*'),尤其避免大文本字段(如 content、description)被默认拉取 - 慎用
load()动态加载,它不解决 N+1,只延迟触发;优先用with()在主查询中一次性预载 - 对大数据集分页,考虑用 cursorPaginate() 替代
paginate(),减少 count 查询开销
不复杂但容易忽略。



















