MySQL 全文索引实战:搜索功能的正确打开方式
开场白
做搜索功能的时候,很多人第一反应是 LIKE ‘%关键词%’,数据量小的时候没问题,数据一大直接全表扫描。我之前有个项目,商品表的 LIKE 搜索在 50 万条数据时就要 3 秒以上,根本没法用。后来上了全文索引,查询时间降到毫秒级。不过全文索引也有自己的坑——中文分词、停用词、最小搜索长度,这些搞不明白一样用不好。
全文索引基础
创建全文索引
-- 建表时创建
CREATE TABLE articles (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(200),
content TEXT,
FULLTEXT INDEX ft_title_content (title, content)
);
-- 已有表添加
ALTER TABLE articles ADD FULLTEXT INDEX ft_title_content (title, content);
-- 单列全文索引
ALTER TABLE articles ADD FULLTEXT INDEX ft_content (content);
基本查询
-- 自然语言模式(默认)
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('数据库优化');
-- 布尔模式
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('+MySQL -Oracle' IN BOOLEAN MODE);
-- 查询扩展模式
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('数据库' WITH QUERY EXPANSION);
MATCH 里的列必须和全文索引定义的列完全一致,顺序也要一样。如果索引是 (title, content),查询必须写 MATCH(title, content),不能写 MATCH(content, title)。
三种搜索模式
自然语言模式
默认模式,按相关性排序:
SELECT *, MATCH(title, content) AGAINST('数据库优化') AS score
FROM articles
WHERE MATCH(title, content) AGAINST('数据库优化')
ORDER BY score DESC;
相关性分数基于词频(TF)和逆文档频率(IDF),MySQL 内部计算,不需要我们操心。
搜索的是"数据库"和"优化"两个词的 OR 组合——包含任意一个词的记录都会返回。
布尔模式
精确控制搜索逻辑:
-- 必须包含 MySQL,不能包含 Oracle
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('+MySQL -Oracle' IN BOOLEAN MODE);
-- 必须同时包含 MySQL 和 优化
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('+MySQL +优化' IN BOOLEAN MODE);
-- 包含 MySQL 或 Oracle
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('MySQL Oracle' IN BOOLEAN MODE);
-- 包含以数据开头的词
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('数据*' IN BOOLEAN MODE);
-- 必须包含 MySQL,且优化权重更高
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('+MySQL >优化' IN BOOLEAN MODE);
布尔模式的操作符:
| 操作符 | 含义 |
|---|---|
| + | 必须包含 |
| - | 不能包含 |
| 无 | 可选,包含则加分 |
| * | 通配符(只能后缀) |
| “” | 短语匹配 |
| > < | 增减权重 |
| () | 分组 |
| ~ | 取反(降低相关性) |
短语匹配要特别注意:
-- 精确匹配"数据库优化"这个短语
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('"数据库优化"' IN BOOLEAN MODE);
查询扩展模式
两步搜索:先搜一次,用结果中的词再搜一次。适合用户搜索词太少、结果不够的情况。
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('数据库' WITH QUERY EXPANSION);
但这个模式容易返回不相关的结果,慎用。
中文分词问题
这是全文索引最大的坑。MySQL 默认的分词器按空格和标点分词,中文没有空格,整段中文会被当成一个词:
-- content = "MySQL数据库优化实战"
-- 默认分词器会把"MySQL数据库优化实战"当成一个整体词
-- 搜"数据库"搜不到!
SELECT * FROM articles WHERE MATCH(content) AGAINST('数据库');
解决方案一:ngram 分词器(MySQL 5.7.6+)
-- 建表时指定 ngram 分词器
CREATE TABLE articles (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(200),
content TEXT,
FULLTEXT INDEX ft_title_content (title, content) WITH PARSER ngram
);
-- 添加索引时指定
ALTER TABLE articles ADD FULLTEXT INDEX ft_content (content) WITH PARSER ngram;
ngram 分词器按固定长度把文本切成片段,默认 n=2(二元分词):
"数据库优化" → "数据", "据库", "库优", "优化"
ngram 的 n 值通过 ngram_token_size 参数配置,范围 1-10,默认 2。改这个参数需要重启 MySQL:
# my.cnf
[mysqld]
ngram_token_size = 2
n=2 意味着搜索词至少 2 个字符才能命中。搜单个字(比如"数")搜不到。
解决方案二:手动分词
在插入数据前,用应用层分词器(比如 IK、jieba)把中文文本切成词,用空格拼接后存储:
-- 原文:"MySQL数据库优化实战"
-- 分词后存储:"MySQL 数据库 优化 实战"
INSERT INTO articles (title, content) VALUES (
'MySQL数据库优化实战',
'MySQL 数据库 优化 实战'
);
```
这种方式灵活,分词质量高,但增加了应用层复杂度。
## 全文索引的配置参数
### ft_min_word_len / innodb_ft_min_token_size
最小索引词长度,默认 InnoDB 是 3,MyISAM 是 4。长度小于这个值的词不会被索引。
```ini
# my.cnf
[mysqld]
innodb_ft_min_token_size = 1 # InnoDB
ft_min_word_len = 1 # MyISAM
改成 1 之后需要重建全文索引:
ALTER TABLE articles DROP INDEX ft_content;
ALTER TABLE articles ADD FULLTEXT INDEX ft_content (content) WITH PARSER ngram;
innodb_ft_cache_size
全文索引缓存大小,默认 32MB。批量插入大量数据时,增大这个值可以提升索引构建速度。
ft_query_expansion_limit
查询扩展模式的最大扩展词数,默认 20。
全文索引 vs LIKE
| 维度 | 全文索引 | LIKE |
|---|---|---|
| 索引利用 | 走全文索引 | 前缀匹配走索引,%xx%全表扫描 |
| 搜索精度 | 分词匹配,支持相关性排序 | 模糊匹配,无排序 |
| 中文支持 | 需要 ngram 或手动分词 | 直接支持 |
| 写入性能 | 需要维护索引,写入较慢 | 无额外开销 |
| 存储开销 | 索引占额外空间 | 无额外空间 |
简单说:精确匹配用 LIKE,搜索功能用全文索引。
实战建议
- 搜索场景用全文索引,匹配场景用 LIKE——别拿全文索引当万能的
-
- 中文必须用 ngram 分词器——否则搜不到东西
-
- 布尔模式的 + 和 - 组合最实用——精确控制搜索结果
-
- 注意最小搜索长度——ngram_token_size=2 时搜单个字搜不到
-
- 全文索引会降低写入性能——不适合频繁更新的表
-
- 数据量特别大时考虑 Elasticsearch——MySQL 全文索引有上限,亿级数据还是上 ES 吧
小结
全文索引是 MySQL 内置的搜索方案,适合中小规模的搜索需求。关键点就三条:中文用 ngram 分词器,查询用布尔模式精确控制,搜索长度注意最小值限制。如果你的搜索需求很复杂(拼音搜索、同义词、高亮等),还是得上 Elasticsearch,MySQL 全文索引搞不定。
相关阅读
更多推荐


所有评论(0)