like '%keyword%' 在mysql中慢是因为无法使用b+树索引,导致全表扫描;fulltext索引仅适用于自然语言文本搜索,需正确创建、配置最小词长并用match() against()语法调用。

为什么LIKE '%keyword%' 在MySQL里慢得像卡住
因为这种全模糊查询无法使用普通B+树索引——LIKE左边带%时,MySQL只能全表扫描。即使字段上有索引,优化器也会直接放弃,执行计划里看到type: ALL就是明证。
常见错误现象包括:查询耗时从毫秒级跳到几秒甚至几十秒、并发一高就堆积大量Waiting for table metadata lock、CPU持续跑满。
全文索引FULLTEXT不是“加个索引就快”,它只对自然语言文本有效,且有硬性限制:MyISAM和InnoDB都支持,但InnoDB要求MySQL ≥ 5.6,且字段必须是CHAR、VARCHAR或TEXT类型。
FULLTEXT索引怎么建才不白建
建错类型、选错字段、忽略最小词长,都会让FULLTEXT形同虚设。
- 必须用
ALTER TABLE ... ADD FULLTEXT (column_name),不能靠CREATE INDEX补 - 单列全文索引最稳妥;多列组合索引(如
FULLTEXT(title, content))后,MATCH()必须包含所有列,否则索引失效 -
innodb_ft_min_token_size默认是3(InnoDB),意味着"a"、"to"、"is"这类停用词或短词直接被过滤——查"php"可以,查"go"就命中不了 - 建完索引后要等
INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE里出现数据,才真正可用(可查SELECT COUNT(*) FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE WHERE TABLE_NAME = 'your_table')
MATCH() AGAINST()怎么写才走索引
写成WHERE column LIKE '%xxx%',哪怕有FULLTEXT也完全不触发;必须用MATCH(col) AGAINST('xxx' IN NATURAL LANGUAGE MODE)语法。
常见误用:
- 用
LIKE混在MATCH()条件里:WHERE MATCH(title) AGAINST('abc') AND content LIKE '%def%'→ 后半段又拖回全表扫 - 搜索词含特殊字符(如
+、-、>)却没切到布尔模式:AGAINST('+mysql -oracle' IN BOOLEAN MODE)才生效,自然语言模式会直接报错或忽略符号 - 搜索词太短(低于
ft_min_word_len)或全是停用词(如'the'、'and'),返回空结果却不报错,容易误判为“没数据”
简单示例:
SELECT id, title FROM articles
WHERE MATCH(title) AGAINST('database optimization' IN NATURAL LANGUAGE MODE);
比FULLTEXT更现实的替代方案有哪些
如果业务要支持单字搜索、拼音搜索、或实时性极高(比如用户边打字边查),FULLTEXT基本扛不住——它不支持前缀模糊(LIKE 'abc%'可以用B+树)、不支持拼音/分词、重建索引还锁表。
更落地的选择:
- 用
LIKE 'keyword%'+ 普通索引:把搜索逻辑改成“开头匹配”,加INDEX(column)就能走range扫描 - 引入Elasticsearch或Meilisearch:适合复杂检索场景,但增加运维成本
- 生成冗余字段预处理:比如把
name转成小写+去空格+拼音,存为name_normalized,再建普通索引,查WHERE name_normalized LIKE 'zhang%' - MySQL 8.0+ 的
REGEXP_LIKE()配合函数索引(仅限确定模式):如CREATE INDEX idx_email_domain ON users ((SUBSTRING_INDEX(email, '@', -1)))
全文索引不是银弹。它解决的是“一段文本里是否含有某些语义相关词”,而不是“任意子串匹配”。真要查'%user_id%'这种结构化字段里的片段,别硬套FULLTEXT——该加前缀索引就加,该改查询方式就改。











