应用java实现相似度打分改进sql语句的like功能

一、sql语句的痛点

s.sku_name LIKE CONCAT('%', #{name}, '%')
OR g.goods_name LIKE CONCAT('%', #{name}, '%')
...

本质是连续子串匹配,所以:

场景数据库存用户输入LIKE 能否命中原因
有空格红富士苹果红富士 苹果红富士 苹果不是连续子串
中间掉字农夫山泉矿泉水农夫矿泉水中间缺"山泉",非连续
顺序颠倒苹果12手机手机苹果顺序不对

方式 1:MySQL 原生 ngram 全文索引(最省事,DB 层搞定)

MySQL 内置了 CJK 的ngram分词器:

ALTER TABLE wms_goods_sku ADD FULLTEXT INDEX ft_sku_name (sku_name) WITH PARSER ngram;
-- 查询
WHERE MATCH(sku_name) AGAINST('农夫矿泉水' IN BOOLEAN MODE)

数据库自动按字符 n-gram 建索引,你一行 Java 分词都不用写,还有索引加速。适合做粗召回。

我目前用的是这样的:

WHERE s.del_flag = '0'
AND ( s.sku_name LIKE '%农夫%' OR g.goods_name LIKE '%农夫%' OR ...
OR s.sku_name LIKE '%夫山%' OR ... ) -- 每个 bigram 一组 OR

(前导通配)用不上普通 B-Tree 索引,本质是全表扫描。所以:

  • 多个 bigram 的 OR→ 每行都要跑一遍所有 LIKE,比原来单个 LIKE 更重。
  • 数据量小时(单租户几百~几千 SKU)→ 毫秒级,完全无感,不需要额外索引。
  • 数据量大时(单租户几万~几十万 SKU)→ 会明显变慢。

是否需要加分词索引?分规模看

单租户 SKU 量级当前 LIKE-OR 方案建议
< 5 千毫秒级不用加,保持现状
5 千 ~ 5 万几十~几百 ms建议上 ngram 全文索引
> 5 万可能 1s+必须上 ngram 或独立搜索引擎

如果要优化:MySQL ngram 全文索引

MySQL 内置 CJK 的ngram分词器,可以把召回从"全表扫描"变成"索引命中":

ALTER TABLE wms_goods_sku ADD FULLTEXT INDEX ft_sku_name (sku_name) WITH PARSER ngram;
ALTER TABLE wms_goods ADD FULLTEXT INDEX ft_goods_name (goods_name) WITH PARSER ngram;

召回 SQL 改成:

WHERE MATCH(s.sku_name) AGAINST('农夫矿泉水' IN NATURAL LANGUAGE MODE)

核心算法换成了成熟类库

委托Hutool 5.8.25 的cn.hutool.core.text.TextSimilarity进行相似度打分

可在苹果应用市场搜索“羽辽进销存”免登录体验相似度打分实践应用。
PC端:https://zhicun.site