百万数据的小系统,搜索标题,不要搞这么复杂

杀鸡用牛刀#

按照小数据量的查询商品标题场景中,比如在一个四百万个商品里找标题,如果是使用 %标题关键词% 会全表扫描 ,一些小伙伴会忍不住上 ES 或者 XunSearch ,我以前遇到的小系统,不少的人是这么干的

其实没有必要,可能百分之 99 的开发者,这辈子都用不上。没有达到一种非常夸张的数据量的时候,几百万个商品标题也是小事一桩。下面由我娓娓道来。
在这里插入图片描述

搜 “一个饼干的包装” 中的 “饼干” 不用 %%#

有没有一种方式,比如把 搜索 “一个饼干的包装” ,直接命中 “饼干” 直接索引读取

思路打开#

我们假设有一种方式,就是可以通过分词工具,比如 用 jieba-php 进行分词。

假设,有两张表
product

product_title

            Jieba::init();
            Finalseg::init();
            $tokens = Jieba::cut($model->title);

我把商品标题表储存在 product 表中, 然后分词的储存在 product_title 中,这样就形成了对应,饼干和包装分两条数据储存在了 product_title 表中,其中对应着 product_id 。 其中 product_title 的 title 加了索引

这种情况就像是论坛里的朋友开发过的优化查询的插件 whereIn ([product_id,product_id]) 差不多的方案。

但是这种方式太 low 了,要是多创建几个表搜索那不是要炸。

有没有现成的?#

那有没有一种 laravel 插件,或者 mysql 插件能直接做呢?

其实 mysql 自带就有,叫 全文索引(FULLTEXT)

CREATE TABLE test (
  id INT AUTO_INCREMENT PRIMARY KEY,
  title TEXT,
  FULLTEXT(title)
);

这种方式的索引和我刚刚举例的的方式有点像,就是先进行了分词,分完词之后会储存到一个地方,读取的

$searchTerm = '关键词';
$results = YourModel::where('title', 'like', "%$searchTerm%")
    ->orWhereRaw("MATCH(title) AGAINST(? IN NATURAL LANGUAGE MODE)", [$searchTerm])
    ->get();

这种方式又会遇到问题,因为 mysql 不知道中文呀,它只知道有空格的英文,中文分词默认的方式不行,需要一种 兼容 中文,日文,韩文这种语言叫做 ngram 分词的倒排索引

 ALTER TABLE  表名 ADD FULLTEXT(title) WITH PARSER ngram;

通过相对比较精确的分词,可以很快的完成我们的目标,搜索 “饼干” 能找到相应的 文档 ID ,直接命中索引

Laravel

加不加 ngram 的区别:

Laravel

还有最后一个问题#

我们搜索场景搜索 “饼干包装” ,如果直接搜索能行吗? 不行!
因为没有储存 “饼干包装” 的文档库,如何实现呢?

还能再优化下吗?#

从应用层面实现:

拿到用户的 keyword 之后,用分词工具分开来, 再通过 implode('+',$cuts) 的方式塞到查询中

SELECT * FROM test_fulltext WHERE MATCH(title) AGAINST('饼干+包装' IN BOOLEAN MODE);

还有就是索引保存在磁盘,意味着你不要买大内存的服务器部署 ,运行在内存中的中间件 ,可以调节 mysql 的索引缓冲来优化查询

#

关于并发,实在不行,docker+ 主 从 + 从 + 从 + 从 + 从 + 从 + 从 + 从 + 从 + 从 + 从 + 从 + 从 …. 就能解决

再说了你用 es 并发大了,也要考虑分布式

以后小伙伴们,业务中有类似的需求的时候,可以试试这种方案哈,别一个小小的系统还搞这些中间件哈,百分之 99 的开发碰不上,真的,我 men 只是配角!

Laravel

百万数据的小系统,搜索标题,不要搞这么复杂

好啦,到此结束!

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 2个月前 自动加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 17

mysql 的 fulltext 要注意 ft_length_min 和 ngr_min , 这两个参数要改下

3个月前 评论
fatrbaby

其实就是自己维护倒排索引吧。ElasticSearch 固然复杂,但我觉得这种方式同样不简单。第一是因为这样搜索不好定制排序,而且扩展一个业务点就得去做一些简单的处理,三是运维数据库我觉得麻烦且难度很大。是我的话,数据量小可以选择 TNTSearch,数据量多可以选择 meilishearch,单机版避免了分布式的麻烦,而且比运维数据库更简单一点。

3个月前 评论
liaosp (楼主) 3个月前
MustangZhong 3个月前
Rytia

:thumbsup:

3个月前 评论

数据库自带全文检索依赖分词效果

3个月前 评论

meilisearch 现在怎么样了,刚开始时候用了下,感觉效果不理想.

3个月前 评论

些的很好,我选择 es

3个月前 评论

服务器资源有限只能 MySQL ngram 了, 不差服务器钱还是得 ES

3个月前 评论
liaosp (楼主) 3个月前
molong

目前我用的另外一个方法进行,不知道大家怎么看,就拿楼主的例子来讲,我在存储标题 “一个饼干的包装” 的时候再前面加个空格,即 “ 一个饼干的包装”,然后在查询的时候使用 like “ % 饼干 %” 进行查询,就是在第一个 % 号前加个空格,当然这个字段加索引,用这个方法同样可以用到索引

3个月前 评论
犯二青年 3个月前
liaosp (楼主) 3个月前
molong (作者) 3个月前
molong (作者) 3个月前
liaosp (楼主) 3个月前

未填写
文章
38
粉丝
16
喜欢
144
收藏
134
排名:319
访问:3.0 万
私信
所有博文
社区赞助商