索引需按查询实际使用场景设计,避免因sql写法(如左模糊、函数操作、联合索引顺序错误)导致失效;应优先为高频等值+范围+排序组合建联合索引,并用getlastsql()+explain验证执行计划。

索引不是加得越多越好,而是要让每一条查询真正用得上。很多 ThinkPHP 项目查得慢,根本原因不是没建索引,而是索引建了但 SQL 写法让 MySQL 直接弃用——你得先让查询“能走索引”,再谈“走得好不好”。
ThinkPHP 的 where() 哪些写法会让索引失效
框架链式调用看着干净,但一写错,EXPLAIN 就显示 type: ALL、key: NULL,等于白建索引。
-
where('name', 'like', '%abc')—— 左模糊,B+ 树没法从头匹配,必须改成where('name', 'like', 'abc%')或用全文索引 -
whereRaw('DATE(create_time) = "2024-01-01"')—— 对字段做函数操作,索引失效;应改用whereBetween('create_time', [$start, $end]) -
where('status', 1)->where('score', '>', 80)—— 若建的是(status, score)联合索引,status是等值,score是范围,能用上;但反过来建(score, status),score是范围,后面status就失效 -
where('user_id', $uid)->order('id desc')—— 若只有user_id索引,id没索引,排序会触发Using filesort;要提速就得建(user_id, id)
联合索引字段顺序怎么排才对
MySQL 遵循最左前缀原则,顺序错了,索引就形同虚设。别凭感觉排,按实际查询模式来。
- 高频等值 + 范围组合:比如常查
where('category_id', 5)->where('status', 1)->where('create_time', '>=', $t),索引应为(category_id, status, create_time);create_time放最后,它才能参与范围筛选 - 带排序的查询:如
where('user_id', $uid)->order('created_at desc'),索引必须是(user_id, created_at);若写成(created_at, user_id),user_id等值条件就无法利用索引有序性 - 避免冗余:已有
(a, b),就别再单独建(a);MySQL 5.7+ 会自动跳过单列索引
怎么验证 ThinkPHP 查询到底有没有走索引
buildSql() 只输出 SQL 字符串,不告诉你执行计划;getLastSql() 才是关键——它返回参数已替换的真实 SQL,可直接丢进 MySQL 执行 EXPLAIN。
- 先开日志:
'sql_explain' => true加到config/database.php,TP 会自动对每个SELECT打印EXPLAIN结果 - 手动验证时,用
$query->getLastSql()拿完整 SQL,复制到 MySQL 客户端执行EXPLAIN SELECT ... - 重点看三列:
type(ref/range合理,ALL危险)、key(是否命中你建的索引名)、rows(扫描行数是否接近结果集) - 注意软删除字段:
delete_time IS NULL这种条件,如果delete_time有大量 NULL,MySQL 可能认为选择性差而放弃索引
哪些字段值得优先加索引
别一上来就给所有 where 字段建单列索引。索引有维护成本,优先保障高频、高筛选性的组合场景。
- 主键和外键:ThinkPHP 默认主键
id自带聚簇索引;user_id、category_id这类外键字段,只要出现在JOIN或WHERE中,基本都要加 - 高频唯一查询字段:如
mobile(登录)、order_sn(订单详情页)、token(会话校验),单列索引即可,效果立竿见影 - 状态 + 时间组合:如订单表的
status和create_time,几乎总是成对出现,建联合索引比两个单列更省空间、更高效 - 避开大字段:别给
TEXT、JSON字段建索引,哪怕只WHERE一下,也可能导致索引膨胀、查询变慢
最常被忽略的一点:索引优化不是一次性动作。业务逻辑一变,原来高效的查询可能立刻退化;每次新增 or 修改 where 条件、order by 字段、join 表,都得重新跑一遍 EXPLAIN 看执行计划。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











