db::listen()是抓取laravel慢查询最轻量可控的入口,需结合执行时间、sql模式(如select * from users)、绑定参数及调用栈综合判断,而非仅依赖time > 1000毫秒阈值。

慢查询不是靠“猜”出来的,而是得先让 Laravel 把它吐出来——DB::listen() 是最轻量、最可控的入口,比 Debugbar 更早介入、不依赖 UI、能进生产灰度环境。
DB::listen() 怎么抓到真·慢查询
别只盯着 time > 1000 这种硬阈值。高频低耗 SQL(比如循环里查用户头像)才是真实瓶颈,单条 15ms,20 次就是 300ms。在 AppServiceProvider::boot() 里注册监听:
DB::listen(function ($query) {
if ($query->time > 50 || str_contains($query->sql, 'SELECT * FROM users')) {
\Log::channel('slowlog')->info('Slow or suspicious query', [
'sql' => $query->sql,
'bindings' => $query->bindings,
'time' => $query->time,
'backtrace' => collect(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS))
->take(3)
->map(fn($f) => $f['file'].':'.$f['line'])
->join(' → ')
]);
}
});
- 用
str_contains()主动盯住高风险表或模式,比纯看耗时更准 -
$query->bindings必须记下来,否则你没法还原真实执行条件 - 日志通道建议单独配
slowlog,避免冲掉业务日志 - 别在监听里做复杂操作(如写 DB 或调 API),会拖慢主线程
日志里带问号的 SQL 怎么 EXPLAIN 才对
日志里看到 "SELECT * FROM posts WHERE user_id = ? AND status = ?",直接复制进 phpMyAdmin 执行 EXPLAIN 是错的——MySQL 优化器没参数就瞎猜,type=ALL 不代表线上也这样。
- 必须把
?替换成真实值,比如user_id = 123 AND status = 'published',再跑EXPLAIN - 重点看
key列是否非 NULL、rows是否远小于表总行数、Extra有没有Using filesort或Using temporary - WHERE 多字段时,联合索引顺序必须匹配最左前缀:INDEX(status, created_at) 有效,INDEX(created_at, status) 对
WHERE status = ?就无效 - 软删除字段
deleted_at单独建索引基本没用,要和常用条件组合,比如 INDEX(deleted_at, status)
Debugbar 的 Queries 面板怎么用才不踩坑
Debugbar 能快速暴露 N+1 和重复查询,但它的 Run EXPLAIN 按钮默认不传绑定参数,点完看到的执行计划可能失真。
- 点击查询旁的 SQL 文本,手动展开 “Bindings” 查看实际参数值
- 复制完整 SQL + 参数,在 phpMyAdmin 或 MySQL CLI 里粘贴后加
EXPLAIN前缀再执行 - 注意面板顶部的 “Duplicates” 计数——出现 47 次的
SELECT * FROM users WHERE id = ?,比一条 800ms 的查询更值得优先优化 - 别在生产环境开 Debugbar,它本身会增加内存和响应时间,开发/预发够用
大表加索引为什么 ALTER TABLE 会锁表
MySQL 5.6+ 默认 DDL 是 blocking 的,ALTER TABLE orders ADD INDEX idx_client_id (client_id) 可能让整张 25 万行的表卡住几秒甚至更久,引发 504。
- 迁移里必须显式加
ALGORITHM=INPLACE:DB::statement("ALTER TABLE orders ADD INDEX idx_client_id (client_id) ALGORITHM=INPLACE") - 外键字段(如
user_id)Laravel 从不自动加索引,belongsTo()关联查不出数据?先检查这个 - 分页场景(
where client_id = ? order by id paginate(20))要建联合索引:INDEX(client_id, id),单列client_id索引无法避免 filesort - 加完索引立刻用
EXPLAIN验证,别假设“加了就生效”
真正难的不是加索引,是判断哪条 SQL 值得优化——它可能每秒执行 200 次,但每次只花 3ms;也可能一天只跑一次,却卡住整个请求链路。盯住频次、绑定值、执行路径,比单纯追着毫秒数跑更接近真相。











