真正拖慢页面的常是高频重复执行的SQL,如循环查头像,单次20ms但累计占800ms;需用DB::listen()记录并统计频次,而非只盯单条耗时。
怎么看 Laravel 的慢查询日志里哪条 SQL 真正拖慢了页面
laravel 默认不记录慢查询,得先配好 db::listen() 或用 slowlog 驱动(如 mysql 的 slow_query_log)。真实瓶颈往往藏在“单次执行不慢、但高频重复执行”的 sql 里,比如循环中查用户头像——日志里可能每条只耗 20ms,但加起来占了整页 800ms。
实操建议:
- 在
AppServiceProvider::boot()中用DB::listen()记录执行时间 + SQL + 绑定参数,写入文件或日志通道,别只看query_time > 1000这种硬阈值 - 用
grep -A 2 -B 2 "SELECT.*from users" storage/logs/laravel.log定位同一 SQL 出现频次,比单纯看耗时更准 - 注意日志里带问号的 SQL(如
"select * from posts where user_id = ?"),它代表预处理语句,真实执行时参数不同可能导致索引失效,不能直接拿去 phpMyAdmin 测试
在 phpMyAdmin 里怎么模拟 Laravel 查询并验证索引是否生效
phpMyAdmin 不是黑盒测试工具,它显示的 EXPLAIN 结果必须和 Laravel 实际执行的 SQL 完全一致,否则结论无效。常见错误是直接复制日志里的带问号语句去执行,结果 EXPLAIN 显示 type=ALL,但线上其实走了索引——因为没传真实参数,MySQL 优化器判断失准。
实操建议:
- 从日志里提取完整 SQL,把
?替换成实际值(如user_id = 123),再粘贴进 phpMyAdmin 的 SQL 标签页执行EXPLAIN SELECT ... - 重点看
key列是否非NULL、rows是否远小于表总行数、Extra里有没有Using filesort或Using temporary - 如果
WHERE有多个字段(如status = ? AND created_at > ?),联合索引顺序必须匹配最左前缀,INDEX(status, created_at)有效,反过来就无效
Laravel 迁移里加索引的写法和容易被忽略的坑
加索引不是加了就完事。Laravel 的 Schema::table() 默认用 addIndex(),但 MySQL 对大表会锁表,线上直接跑迁移可能让页面卡死几分钟。另外,Eloquent 的 withCount()、whereHas() 等隐式关联查询,常被忽略索引需求。
立即学习“PHP免费学习笔记(深入)”;
实操建议:
- 大表加索引务必加
ALGORITHM=INPLACE(MySQL 5.6+):在迁移里用DB::statement("ALTER TABLE users ADD INDEX idx_user_status_created (status, created_at) ALGORITHM=INPLACE") - 外键字段(如
user_id)必须手动加索引,Laravel 不自动加,否则belongsTo关联查询会全表扫描 - 软删除字段
deleted_at如果经常出现在WHERE(如->onlyTrashed()),单独建索引意义不大,要和其它条件组成联合索引,比如INDEX(deleted_at, status)
为什么加了索引页面还是卡——检查 Laravel 自身的 N+1 和缓存穿透
索引只解决数据库层扫描问题。如果 Laravel 每次请求都执行 50 次 User::find(),哪怕每次只要 1ms,也比一次 User::with('posts')->get() 慢 10 倍。更隐蔽的是缓存穿透:缓存没命中就直查 DB,而缓存 key 设计不合理(比如含动态 ID 却没做存在性校验),导致大量无效查询压垮数据库。
实操建议:
- 用 Laravel Debugbar 或
DB::enableQueryLog()+dd(DB::getQueryLog())数清当前请求发了多少条 SQL,超过 5 条就要警惕 N+1 - 对高频访问但低更新的关联数据(如分类列表),用
Cache::rememberForever()缓存整个集合,而不是缓存单个模型 - 软删除 + 缓存场景下,
find($id)可能返回 null,但缓存里没存这个 “不存在” 状态,下次还查,要用Cache::remember('user_'.$id, 3600, function () use ($id) { return User::withTrashed()->find($id); })主动存 null
索引只是性能优化链条上的一环,真正卡顿往往出在 SQL 和应用逻辑的交界处——比如一个 whereHas 查 1000 个用户是否有某权限,即使加了索引,也该改成用中间表聚合统计后缓存。


















