mysql原生不支持中文语义分词,fulltext索引仅按空格/标点切分,ngram仅支持固定长度字符切分且无法识别词汇边界;自定义函数可预处理关键词为带通配符的布尔查询字符串,但不能改变match底层匹配逻辑。

MySQL 原生不支持中文分词,MATCH 只能配合 FULLTEXT 索引对英文单词切分
直接在中文字段上建 FULLTEXT 索引并用 MATCH ... AGAINST,基本搜不到结果——因为 MySQL 默认按空格、标点切词,而中文没有天然分隔符。你看到的“能搜到”,大概率是凑巧匹配了连续字串(比如搜索“数据库”刚好字段里有这仨字连在一起),不是真正意义上的分词检索。
所以别指望靠改 ft_min_word_len 或换 ngram parser 就一劳永逸。MySQL 5.7+ 的 ngram 确实能按字符切,但它只支持固定长度(如 n=2 就切“数 据 库”→“数 据”“据 库”),无法识别“数据库”是一个词,也不能跳过停用词或做同义扩展。
自定义函数不能绕过 FULLTEXT 的底层限制,但可以预处理查询关键词
MySQL 允许写 FUNCTION,但注意:它不能动态修改 MATCH 的匹配行为,也不能把一个中文字符串自动拆成“数据库”“mysql”“索引”这样的语义词。你能做的,是用函数把用户输入的搜索词做简单切分(比如按字、按常见分隔符、或查词典表映射),再拼成 AGAINST('词1* 词2* 词3*' IN BOOLEAN MODE) 这样的表达式。
例如写一个函数 split_chinese_keywords,输入“MySQL数据库优化”,返回字符串 'MySQL* 数据*库* 优*化*'(注意带星号通配符):
DELIMITER $$
CREATE FUNCTION split_chinese_keywords(s TEXT)
RETURNS TEXT
READS SQL DATA
DETERMINISTIC
BEGIN
DECLARE result TEXT DEFAULT '';
DECLARE i INT DEFAULT 1;
WHILE i <p>然后这样用:</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill2334" title="MySQL"><img
src="https://img.php.cn/upload/skill/000/000/081/178900927846657.jpg" alt="MySQL" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill2334" title="MySQL" class="overflowclass">MySQL</a>
<p class="overflowclass">编写正确的MySQL查询,避免字符集、索引和锁方面的常见陷阱。</p>
</div>
<a rel="nofollow" href="/xiazai/skill2334" title="MySQL" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><p><code>SELECT * FROM docs WHERE MATCH(title) AGAINST (split_chinese_keywords('数据库优化') IN BOOLEAN MODE);</code></p><p>⚠️ 注意:这个函数只是机械切字,没语义;且 <code>MATCH</code> 要求字段已建 <code>FULLTEXT</code> 索引,否则报错 <code>ERROR 1191 (HY000): Can't find FULLTEXT index</code>。</p><h3>真正可用的分词搜索,得靠外部工具或应用层协作</h3><p>如果业务真需要准确分词(比如搜“苹果手机”不命中“苹果汁”),MySQL 不是合适载体。更现实的做法是:</p>
- 用
Elasticsearch或MeiliSearch做专门检索,MySQL 只存主数据;应用层把用户搜索词发给 ES,拿到 ID 列表后再回查 MySQL - 用
MySQL 8.0+的JSON字段 + 应用层分词(如jieba、ansj),把分好的词存进 JSON 数组,再用JSON_CONTAINS查;缺点是无法高效全文索引,适合低频、小数据量 - 如果必须死守 MySQL,可建一张
doc_keywords关联表,每行存一个文档 ID + 一个分好词的 keyword(由应用或定时任务生成),然后走普通IN或LIKE查询;加keyword字段的 B-tree 索引能提速,但不支持模糊/近义/权重
MATCH 和自定义函数混用时最容易踩的三个坑
很多人试了几次就放弃,其实卡在几个具体细节上:
-
MATCH只接受常量或列名,不能直接传函数调用结果(除非该函数是DETERMINISTIC且返回字符串——上面例子中已满足,但很多自定义函数漏写DETERMINISTIC,会报错FUNCTION is not allowed) -
FULLTEXT索引对中文字段效果差,但如果你强行建了,又忘了在AGAINST里加IN BOOLEAN MODE,默认是自然语言模式,会忽略所有单字词(因ft_min_word_len=4),导致空结果 - 函数返回的字符串里如果有特殊字符(如
+、-、>),没转义就直接拼进AGAINST,会导致语法错误或逻辑反转(比如+变成“必须包含”)
最麻烦的其实是维护成本:分词逻辑一旦升级(比如加新词典、改停用词),你要同步改 MySQL 函数、重建索引、刷缓存……这些事在 ES 里改个配置就能完成,在 MySQL 里就得动生产代码和 DBA 流程。










