LIKE '%关键词' 一定慢因MySQL B+树索引无法匹配前导通配符,导致全表扫描;优先改用前缀匹配'关键词%',否则应建FULLTEXT索引并手写MATCH...AGAINST查询。

ThinkPHP 的 like 查询本身不加速,加速靠的是数据库层的索引策略和查询改写——框架只负责拼 SQL,真正决定快慢的是 MySQL 是否走索引、是否触发全表扫描。
LIKE '%关键词' 为什么一定慢?
MySQL 的 B+ 树索引无法从中间或末尾开始匹配。只要 like 条件带前导通配符(如 '%abc' 或 '%abc%'),优化器会直接放弃该字段上的普通索引,EXPLAIN 中 key 为 NULL、type 为 ALL 就是铁证。
- 业务上能改成前缀匹配(
'abc%')就优先改:比如搜索“苹果”,要求用户输“苹果手机”而非“手机苹果” -
where('title|content', 'like', "%{$kw}%")这种多字段 OR 模糊,每字段都可能全表扫,实际是 N 倍慢 - 即使加了单列索引,
like '%x%'也完全用不上——索引不是万能胶
什么时候该换 FULLTEXT 全文索引?
当必须支持任意位置匹配、且字段内容较长(如文章标题、描述、评论),FULLTEXT 是比 like 更合适的方案,但需主动建、主动查,ThinkPHP 不自动适配。
- 先在数据库执行:
ALTER TABLE article ADD FULLTEXT(title, content); - 查询不能继续用
where(..., 'like', ...),得手写原生 SQL:Db::query("SELECT * FROM article WHERE MATCH(title, content) AGAINST(? IN NATURAL LANGUAGE MODE)", [$kw]); -
MATCH ... AGAINST不支持whereOr链式调用,也不能和like混用,得单独封装方法 - 注意分词:MySQL 内置分词对中文支持弱,纯中文场景建议搭配第三方分词(如
Fenci\Segment)预处理入库
ThinkPHP 里怎么安全地用全文索引而不翻车?
别指望 where() 自动识别全文字段。手动控制 SQL 是唯一可靠方式,同时避开框架对参数绑定和字段名的自动转义干扰。
立即学习“PHP免费学习笔记(深入)”;
- 禁用自动转义:用
Db::raw()包裹MATCH表达式,例如where(Db::raw("MATCH(title) AGAINST('{$kw}' IN NATURAL LANGUAGE MODE)"), 1) - 避免 SQL 注入:
$kw必须过滤空格、+、-、~ 等全文操作符,或改用参数绑定 +AGAINST(? IN BOOLEAN MODE) - 不要给全文字段再加普通索引:
title上既有INDEX又有FULLTEXT是冗余,还可能干扰优化器选索引 - 测试时一定要
EXPLAIN看type是否为fulltext,而不是只看 PHP 层有没有报错或返回数据
真正卡住性能的往往不是 ThinkPHP 写法,而是没意识到 like '%x%' 在数据库层面就是无解的;全文索引不是开关一开就快,它需要字段设计、建索引、改查询三步全到位,漏任何一环都白搭。



















