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

杀鸡用牛刀

按照小数据量的查询商品标题场景中,比如在一个四百万个商品里找标题,如果是使用 %标题关键词% 会全表扫描 ,一些小伙伴会忍不住上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 协议》,转载必须注明作者和本文链接
本帖由系统于 1年前 自动加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 17

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

1年前 评论
fatrbaby

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

1年前 评论
Image liaosp (楼主) 1年前
Image MustangZhong 1年前
Rytia

:+1:

1年前 评论

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

1年前 评论

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

1年前 评论

些的很好,我选择es

1年前 评论

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

1年前 评论
Image liaosp (楼主) 1年前
molong

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

1年前 评论
Image 犯二青年 1年前
Image liaosp (楼主) 1年前
Image molong (作者) 1年前
Image molong (作者) 1年前
Image liaosp (楼主) 1年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
Image
未填写
文章
39
粉丝
16
喜欢
146
收藏
135
排名:312
访问:3.0 万
私信
所有博文