开启 laravel 慢 sql 日志需在 appserviceprovider::boot() 中使用 db::listen() 监听,判断 $query->time > 500(毫秒),记录超时查询的 sql、绑定参数和耗时,仅限开发环境使用。

怎么开启 Laravel 查询日志看慢 SQL
默认 Laravel 不记录查询详情,得手动打开。只在开发环境开,生产环境绝对别开,会拖垮性能。
最直接的方式是在 AppServiceProvider::boot() 里加监听:
DB::listen(function ($query) {
if ($query->time > 500) { // 超过 500ms 认为是慢查询
\Log::warning('Slow query', [
'sql' => $query->sql,
'bindings' => $query->bindings,
'time' => $query->time,
]);
}
});
-
$query->time单位是毫秒,别误以为是秒 - 绑定参数
$query->bindings是原始值,不带引号,但能帮你还原真实执行的 SQL - 别用
dd()或dump()打印,页面会卡死,尤其批量操作时 - 这个监听对所有数据库连接生效,如果用了多个连接(比如读写分离),要确认监听挂载在哪个连接上
Laravel Scout + Algolia 搜索时查得慢怎么办
Scout 默认每次搜索都走全量同步索引,但真正卡顿往往不是 Algolia 响应慢,而是本地模型关联没预加载,导致 N+1 在搜索后渲染时爆发。
典型场景:搜索出 20 篇文章,再逐个查作者、分类、标签——这时候 with() 比调优 Algolia 更管用。
- 确保
toSearchableArray()返回的数据尽量精简,别塞进大字段(如content、html) - 搜索结果用
Model::hydrate($ids)或Model::whereIn('id', $ids)->with(...)->get()预加载关联,而不是在 Blade 里循环查 - Algolia 的
search()本身很快,但Model::search(...)->get()底层会查 DB,这步才是瓶颈 - 如果只是列表展示,考虑用
Model::search(...)->raw()直接拿 Algolia 原始响应,自己映射 ID 再批量查,更可控
用 when() 动态加 where 条件反而变慢?
看起来很优雅的链式条件,实际可能让 Eloquent 生成冗余 SQL 或干扰查询优化器判断,尤其嵌套多层 when() 加子查询时。
常见错误是把本该提前判断的逻辑塞进 when(),比如空字符串、null 值没过滤就传进去,导致生成 WHERE column = '' 这种无意义条件,还可能命中全表扫描。
- 先做 PHP 层校验:
if (! blank($request->input('status'))) { ... },比全塞进when()更清晰也更快 -
when()里避免调用 DB 查询或复杂函数,它会在每次构建 Query Builder 时执行闭包,重复计算浪费 CPU - 注意
when()和tap()的区别:前者是条件分支,后者是无条件介入,别用错 - 如果条件太多,考虑拆成不同方法,比如
scopeActive()、scopeByCategory(),可读性和可测性更好
为什么加了 select() 还是查了所有字段
因为 Eloquent 默认会用主键和时间戳字段补全,即使你写了 select('id', 'title'),只要调用的是 Model::get(),它仍会尝试 hydrate 完整模型实例,触发访问器、强制加载 created_at 等字段——除非你明确告诉它“别当模型用”。
- 真只需要几个字段,用
DB::table('posts')->select('id', 'title')->get(),返回的是stdClass对象,零模型开销 - 如果必须用模型但只取部分字段,加
->toBase()->get()绕过 Eloquent hydration,但会丢失访问器和属性转换 -
select()不影响with()关联查询的字段,关联表还是全查,得单独给关联加select() - MySQL 8.0+ 开启
optimizer_switch='derived_merge=off'可能缓解某些子查询场景下的字段膨胀,但属于数据库层,和 Laravel 无关
慢查询真正的麻烦点不在工具开关,而在“你以为在优化的地方,其实根本没生效”。比如开了日志却只盯着时间,忽略了绑定参数里的类型不匹配;或者信了 explain 的输出,却没注意 Laravel 自动生成的隐式 join 把 left join 变成了 inner join。











