全文索引不会被优化器自动选用,必须显式使用match...against语法触发;中文需指定ngram分词器,否则可能无结果;不支持order by/group by加速;性能瓶颈常在后续过滤条件,需配合其他索引优化。

全文索引不会被优化器“自动选中”
MySQL 优化器在生成执行计划时,EXPLAIN 中的 key 列永远为 NULL,哪怕表上建了 FULLTEXT 索引。这不是失效,而是设计如此:全文检索必须显式使用 MATCH ... AGAINST 语法触发,优化器不把它当作普通索引参与成本估算。
常见误解是把全文查询写成 WHERE content LIKE '%关键词%' 或 WHERE content = 'xxx',这类写法完全绕过全文索引,只能走全表扫描或普通 B+ 树索引(如果存在且匹配)。
- 必须用
MATCH(content) AGAINST('关键词')才能激活全文索引 -
AGAINST的第二个参数决定模式:IN NATURAL LANGUAGE MODE(默认)、IN BOOLEAN MODE、WITH QUERY EXPANSION - 即使列上有
FULLTEXT索引,ORDER BY content或GROUP BY content也不会利用它——全文索引不支持排序或分组加速
全文索引的词法分析由分词器控制,不是 SQL 层逻辑
InnoDB 全文索引的分词行为发生在存储引擎层,与 SQL 解析器分离。默认使用 ngram 分词器(适用于中文/日文),但需在建表或添加索引时显式指定:
ALTER TABLE articles ADD FULLTEXT INDEX ft_title_content (title, content) WITH PARSER ngram;
没加 WITH PARSER ngram,对中文字段执行 MATCH ... AGAINST 可能返回空结果——因为默认分词器按空格和标点切分,中文无空格,整个字段被当做一个“单词”,几乎无法命中。
-
ngram_token_size参数控制中文分词粒度,默认为 2(即按双字切分),可设为 1(单字)或 3(三字),但值越小索引体积越大 - 分词结果存于内部 FTS auxiliary tables(如
FTS_0000000000000123_0000000000000124_INDEX_1),不可直接查,也不参与EXPLAIN - MyISAM 的全文索引用不同分词机制,且不支持
ngram,跨引擎迁移时行为会突变
全文查询性能瓶颈常不在索引本身,而在匹配后过滤
MATCH ... AGAINST 返回的是文档 ID 和相关性评分,但 MySQL 默认不缓存中间结果。如果后续跟了复杂 WHERE 条件或 JOIN,引擎可能先取出全部匹配文档 ID,再回主表逐行过滤——这会导致大量随机 I/O。
例如:
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('数据库优化' IN NATURAL LANGUAGE MODE)
AND status = 'published'
AND create_time > '2025-01-01';
这里 status 和 create_time 若无联合索引覆盖,就可能让全文检索的优势被抵消。
- 优先把高频过滤条件挪到
MATCH外部,并确保这些列有独立索引或与全文字段组成组合索引(注意:InnoDB 不支持全文索引与其他类型索引混合) -
IN BOOLEAN MODE支持+、-、*等操作符,能提前排除大量无效文档,比自然语言模式更可控 - 相关性评分(
RELEVANCE)无法用索引排序,ORDER BY MATCH(...) AGAINST(...)会触发文件排序(Using filesort)
全文索引重建与碎片问题容易被忽略
InnoDB 全文索引更新是异步的:INSERT/UPDATE 后,变更先写入 FTS insert buffer,后台线程再批量合并进倒排索引。DELETE 不立即清理倒排项,而是记入 DELETED 辅助表。长时间运行后,索引会膨胀、查询变慢。
没有类似 OPTIMIZE TABLE 对全文索引的“一键整理”命令。有效手段只有:
- 执行
OPTIMIZE TABLE articles—— 这会重建整张表,包括全文索引,但会锁表,且耗时长 - 导出数据 → 删除全文索引 → 重建表 → 导入 → 重新加
FULLTEXT索引(适合低峰期维护) - 监控
INFORMATION_SCHEMA.INNODB_FT_CONFIG表中的optimize_count,值持续增长说明合并滞后
真正麻烦的是:全文索引一旦创建,就不能修改分词器类型或字段列表,只能删掉重建——而重建期间新写入的数据可能丢失分词,导致搜索不一致。











