like '%xxx%' 无法使用b+树索引导致全表扫描,引发高cpu、mdl锁等待及并发下降;fulltext索引需满足引擎、字段类型、中文分词器、最小词长等硬性条件,并严格使用match() against()语法才生效。

LIKE '%keyword%' 不会锁行,但会引发大量“Waiting for table metadata lock”和高CPU,本质是全表扫描拖垮并发能力。
为什么 LIKE '%xxx%' 会让查询变慢还卡住其他操作
MySQL 对 LIKE '%xxx%' 完全无法使用 B+ 树索引,优化器直接放弃索引,执行计划里 type: ALL 就是明证。它得逐行读取整张表、对每条记录做字符串匹配——数据量一过几万,单次查询就几百毫秒;并发上来后,连接堆积、磁盘 I/O 拉满、CPU 持续 90%+,其他简单查询(比如 SELECT id FROM t WHERE id = ?)也会被卡在 Waiting for table metadata lock 状态,因为全表扫描长期持有 MDL(metadata lock)。
常见错误现象包括:
- 原本 5ms 的查询突然变成 3s+,且波动极大
- 慢查询日志里反复出现
SELECT ... WHERE column LIKE '%xxx%' - SHOW PROCESSLIST 看到一堆
State: Sending data或Waiting for table metadata lock
FULLTEXT 索引不是加了就能快,必须满足这四个硬条件
建错类型、配错参数、写错语法,FULLTEXT 就是摆设。关键限制如下:
- 存储引擎必须是
InnoDB(MySQL ≥ 5.6)或MyISAM;MEMORY或CSV不支持 - 字段类型只能是
CHAR、VARCHAR或TEXT;INT或JSON上建不了 - 中文必须显式指定
WITH PARSER ngram,否则默认按字节切分,搜“李”“AI”全失效 - 最小词长由
innodb_ft_min_token_size控制(InnoDB 默认为 3),搜“go”要改配置并重建索引
建索引必须用:
ALTER TABLE articles ADD FULLTEXT (title, content) WITH PARSER ngram;不能用
CREATE INDEX 补。
MATCH() AGAINST() 写法错一个字符,全文索引就白建
哪怕表上有 FULLTEXT 索引,只要 SQL 里还带 LIKE 或 =,就完全不走索引。必须严格用 MATCH() AGAINST() 语法:
- 自然语言模式(默认):
WHERE MATCH(title) AGAINST('数据库优化' IN NATURAL LANGUAGE MODE) - 布尔模式(支持 +、-、*):
WHERE MATCH(title) AGAINST('+MySQL -Oracle' IN BOOLEAN MODE) - 搜单字且已调小
ft_min_word_len:AGAINST('李' IN NATURAL LANGUAGE MODE)
典型误用:
-
WHERE title LIKE '%MySQL%' AND MATCH(content) AGAINST(...)→ 后半段索引失效 -
AGAINST('MySQL*')在自然语言模式下等价于搜 “MySQL*”,不是通配 - 搜索词含停用词(如“的”“了”),结果为空却不报错,容易误判为“没数据”
建完 FULLTEXT 索引后,别急着查,先确认索引真生效了
索引创建语句执行完不代表马上可用。InnoDB 全文索引有异步合并机制,需等数据刷入内部倒排索引表:
- 查
INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE是否有记录:SELECT COUNT(*) FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE WHERE TABLE_NAME = 'articles';
- 刚插入的数据不会立刻出现在全文索引中,需等事务提交 + 后台线程合并(默认 30 秒内)
- 如果查不到数据,先
OPTIMIZE TABLE articles强制刷新索引缓存
真正容易被忽略的是:全文索引只加速文本相关性排序,ORDER BY MATCH() AGAINST() 才能体现优势;若只是想“有没有”,用 EXISTS (SELECT 1 FROM ... WHERE MATCH() AGAINST()) 更轻量。另外,MATCH() 只能用于 WHERE 子句,不能出现在 SELECT 或 JOIN 条件里。











