用 EXPLAIN 查看 Laravel 查询是否走索引最准确,需关注 type、key、rows 和 Extra 字段;常见不走索引原因包括 WHERE 中使用函数、联合索引顺序不匹配、VARCHAR 未设前缀等。

用 EXPLAIN 看懂 Laravel 查询实际走不走索引
直接在 Laravel 中执行 EXPLAIN 是最准的判断方式,而不是靠猜或看模型定义。Laravel 的 DB::select() 或原生查询能拿到 MySQL 的执行计划,关键看 type、key、rows 和 Extra 字段。
常见误判是:字段加了索引,但查询仍 type: ALL(全表扫描),原因通常是:
- WHERE 条件用了函数或表达式,如
WHERE YEAR(created_at) = 2024 - 联合索引顺序不匹配,比如索引是
(user_id, status),但查询只用了status = 'active' - 字符串字段没指定长度,
VARCHAR(255)上建索引却没写前缀,InnoDB 可能拒绝使用
DB::select("EXPLAIN SELECT * FROM orders WHERE user_id = ? AND status = ?", [123, 'shipped']);哪些 Laravel 查询场景必须加索引
不是所有 where 都值得加索引。优先覆盖高频、慢、且过滤性好的查询:
-
where+orderBy组合,如Order::where('status', 'pending')->orderBy('created_at')->get()→ 考虑联合索引(status, created_at) - 外键字段,尤其是
belongsTo关联查询,post.user_id必须有索引,否则with('user')会 N+1 拖垮性能 - 软删除字段
deleted_at,如果常用withTrashed()或onlyTrashed(),单独索引它往往无效,应和其它条件组成联合索引,如(deleted_at, status) - 全文搜索字段(非
FULLTEXT)不要硬套 B-tree 索引,LIKE '%keyword%'基本无法用索引,得换WHERE column LIKE 'keyword%'或上 Elasticsearch
Laravel 迁移中定义索引的几个易错点
用 Schema::table() 加索引时,名字别太长,MySQL 限制 64 字符,超长会自动截断并可能重名冲突;联合索引字段顺序直接影响是否生效。
- 外键字段建索引要显式写,Laravel 不自动创建:
$table->unsignedBigInteger('user_id'); $table->index('user_id'); - 唯一索引和主键不同,
unique()不等于primary(),别把email字段只设unique()就以为能加速登录查询,还得确认是否被 WHERE 实际用到 - 迁移回滚时,
dropIndex()必须用和index()完全一致的名字,否则报错:$table->dropIndex('orders_user_id_status_index');(不是index_orders_on_user_id_status) - 对大表加索引要小心,MySQL 5.6+ 支持
ALGORITHM=INPLACE,但 Laravel 默认迁移不指定,线上执行可能锁表;生产环境建议用DB::unprepared("ALTER TABLE ... ADD INDEX ... ALGORITHM=INPLACE")手动控制
验证索引是否真正生效的三步法
加完索引别信“应该可以了”,必须用真实数据量 + 真实查询路径验证:
- 第一步:清空查询缓存(
RESET QUERY CACHE或重启 MySQL 连接),避免旧计划干扰 - 第二步:用
EXPLAIN FORMAT=JSON查看是否用到索引,重点看used_key_parts是否包含你期望的字段 - 第三步:查
information_schema.STATISTICS确认索引存在且无重复:SELECT * FROM STATISTICS WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'orders' AND INDEX_NAME = 'idx_orders_status_created';
一个常被忽略的细节:MySQL 的索引统计信息可能过期,导致优化器选错执行计划,定期运行 ANALYZE TABLE orders; 有助于保持准确判断。










