thinkphp查询慢主因是sql未走索引、字段冗余或缓存失效;应通过getlastsql()+explain验证索引使用,避免函数操作和左模糊,建联合索引遵循最左匹配,并启用字段缓存与配置优化。

ThinkPHP 查询慢,八成不是框架问题,而是 SQL 没走索引、字段选多了、缓存没开对——改这几点,响应时间常能从 800ms 掉到 80ms。
怎么确认查询到底有没有走索引
别信 buildSql() 输出的 SQL 看着“干净”就以为没问题。它只拼字符串,不执行,更不告诉你用没用上索引。buildSql() 返回的是带问号占位符的语句(如 WHERE name = ?),得先用 getLastSql() 拿到真实值替换后的 SQL;把 getLastSql() 结果复制进 MySQL 客户端,前面加 EXPLAIN 执行,重点看:
-
type:非ALL才算走了索引(const/ref/range合理) -
key:非NULL,且是你建的索引名 -
rows:越小越好,接近实际返回行数才健康
哪些 ThinkPHP 写法会悄悄让索引失效
ORM 链式调用掩盖了底层 SQL 的危险操作,一写错,索引直接作废:
-
where('name', 'like', '%abc')—— 左模糊,B+ 树没法前缀匹配,key必为NULL -
whereRaw('DATE(create_time) = "2024-01-01"')—— 对字段套函数,索引失效;应改写为whereBetween('create_time', ['2024-01-01 00:00:00', '2024-01-01 23:59:59']) -
where('user_id', $uid)->order('id desc')->limit(10)—— 若id无索引,Extra里会出现Using filesort,排序变全表扫描 -
where('category_id', 5)->where('status', 1)->where('score', '>', 70)—— 复合索引必须按顺序覆盖:(category_id, status, score)可用;(category_id, score)则status后面的score就断了
联合索引字段顺序怎么排才对
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+ 会自动跳过单列索引,纯属冗余 -
TEXT/JSON字段禁止建索引,哪怕只WHERE json_extract(xxx)一次,也容易触发索引膨胀甚至全表扫描
缓存和配置动哪里见效最快
上线前不调这几个开关,等于开着空调跑马拉松——白耗:
- 开启查询日志:
'sql_explain' => true加到config/database.php,TP 会自动对每个SELECT打印EXPLAIN结果 - 禁用分页自动
COUNT(*):用paginate(15, false, ['query' => request()->param()])关闭,自己用覆盖索引查总数(如SELECT COUNT(user_id) FROM user WHERE status = 1)或 Redis 缓存近似值 - 关联查询慎用
with():100 条用户数据 +with('profile', 'orders')可能触发 200+ 次额外查询;优先用join或显式field()控制字段 - 主键
id不用管,但检查迁移脚本是否误删了它——没主键的表,InnoDB 会自建隐藏聚簇索引,性能更差
真正卡住的地方,往往不是“要不要建索引”,而是“建了但没用上”——验证必须落到 EXPLAIN 的 type 和 key 上,而不是 getLastSql() 看着顺眼就收工。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











