应用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