explain是判断索引是否生效的唯一可靠方式;必须为where、join、order by中的高频字段(如user_id、deleted_at、status、created_at)加索引,复合索引需遵循等值前置、范围后置原则,并用explain验证type、key、rows三字段。

EXPLAIN 是判断索引是否生效的唯一可靠方式,不是“加了就快”,而是“用对才快”。Laravel 本身不自动建索引,所有性能收益都依赖你对查询模式和执行计划的理解。
哪些字段必须加索引?看 WHERE、JOIN、ORDER BY 出现场景
高频出现在查询条件里的字段,不加索引等于默认开启全表扫描。重点包括:
-
user_id、post_id这类外键字段,几乎总是需要单独索引 -
deleted_at(软删除字段),只要用了withTrashed()或onlyTrashed(),就必须加索引,否则IS NOT NULL条件直接触发type: ALL - 复合查询组合,比如
WHERE status = ? AND created_at > ?,单列索引效果极差,得建['status', 'created_at']复合索引 - 字符串字段如
email超过 191 字节(utf8mb4 下VARCHAR(255)),必须显式指定前缀长度:$table->index('email(191)')
为什么加了索引还是慢?检查三个关键字段
执行 EXPLAIN SELECT ... 后,只盯这三列:
-
type:至少要是range或ref;ALL表示全表扫,索引完全没用上 -
key:显示实际命中索引名;NULL就是没走索引,别信“我加了” -
rows:预估扫描行数;如果接近总行数(比如 100 万行里扫 95 万),说明索引选择性太差或顺序不对
常见翻车点:whereDate('created_at', '2026-05-20') 会让索引失效,应改写为 whereBetween('created_at', [$start, $end]);LIKE '%abc' 左模糊也无法使用 B-tree 索引。
复合索引字段顺序怎么排?按「等值 → 范围」原则
MySQL 只能高效使用索引的最左前缀。设计时优先把等值条件字段放前面,范围条件放后面:
- 正确:
['deleted_at', 'created_at']—— 因为deleted_at IS NOT NULL是等值判断,created_at > ?是范围 - 错误:
['created_at', 'deleted_at']—— 范围字段在前,deleted_at就无法利用索引 - 如果还有
status(低区分度字段),不要放在最前;它适合放在等值段末尾,比如['deleted_at', 'status', 'created_at']
生产环境加索引要注意什么?锁表风险真实存在
大表加索引不是执行一条命令就完事,MySQL 5.6+ 虽支持在线 DDL,但仍会阻塞写入操作:
- 务必在业务低峰期操作,避免用户提交失败
- 迁移中用
Schema::table()加索引时,Laravel 默认包裹事务,可能加剧锁等待;可考虑拆出原生语句:DB::unprepared('CREATE INDEX CONCURRENTLY ...')(PostgreSQL)或 MySQL 的ALGORITHM=INPLACE选项 - 加完立刻验证:
EXPLAIN对应查询,再查SHOW INDEX FROM table_name确认索引已生效且名称一致
最易被忽略的是:索引不是一劳永逸的,数据分布变化(比如 status 字段从 90% 为 1 变成 50/50)、统计信息陈旧、或查询条件微调,都可能导致优化器弃用已有索引——定期重看 EXPLAIN 比盲目加索引重要得多。











