来源 | http://github.com/jasonGeng88/blog/blob/master/201706/redis-search.md

场景

大家如果是做后端开发的,想必都实现过列表查询的接口,当然有的查询条件很简单,一条 SQL 就搞定了,但有的查询条件极其复杂,再加上库表中设计的各种不合理,导致查询接口特别难写,然后加班什么的就不用说了(不知各位有没有这种感受呢~)。

下面以一个例子开始,这是某购物网站的搜索条件,如果让你实现这样的一个搜索接口,你会如何实现?(当然你说借助搜索引擎,像 Elasticsearch 之类的,你完全可以实现。但我这里想说的是,如果要你自己实现呢?

必备技能!聊聊二维码扫码登录的原理

从上图中可以看出,搜索总共分为6大类,每大类中又分了各个子类。这中间,各大类条件之间是取的交集,各子类中有单选、多选、以及自定义的情况,最终输出符合条件的结果集。

好了,既然需求很明确了,我们就开始来实现。

实现1

率先登场是小A同学,他是写 SQL 方面的“专家”。小A信心满满的说:“不就是一个查询接口吗?看着条件很多,但凭着我丰富的 SQL 经验,这点还是难不倒我的。”

于是乎就写出了下面这段代码(这里以 MYSQL 为例):

select ... from table_1
left join table_2
left join table_3
left join (select ... from table_x where ...) tmp_1
...
where ...
order by ...
limit m,n

代码在测试环境跑了一把,结果好像都匹配上了,于是准备上预发。这一上预发,问题就开始暴露出来。预发为了尽可能的逼真线上环境,所以数据量自然而然要比测试大的多。所以这么一个复杂的 SQL,它的执行效率可想而知。测试同学果断把小A的代码给打了回来。

实现2

总结了小A失败的教训,小B开始对SQL进行了优化,先是通过了explain关键字进行SQL性能分析,对该加索引的地方都加上了索引。同时将一条复杂SQL拆分成了多条SQL,计算结果在程序内存中进行计算。

伪代码如下:

$result_1 = query('select ... from table_1 where ...');
$result_2 = query('select ... from table_2 where ...');
$result_3 = query('select ... from table_3 where ...');
...$result = array_intersect($result_1, $result_2, $result_3, ...);

这种方案从性能上明显比第一种要好很多,可是在功能验收的时候,产品经理还是觉得查询速度不够快。小B自己也知道,每次查询都会向数据库查询多次,而且有些历史原因,部分条件是做不到单表查询的,所以查询等待的时间是避免不了的。

实现3

小C从上面的方案中看到了优化的空间。他发现小B在思路上是没问题的,将复杂条件拆分,计算各个子维度的结果集,最后将所有的子结果集进行一个汇总合并,得到最终想要的结果。

于是他突发奇想,能否事先将各个子维度的结果集给缓存起来,这要查询的时候直接去取想要的子集,而不用每次去查库计算。

这里小C采用 Redis 来存储缓存数据,用它的主要原因是,它提供了多种数据结构,并且在 Redis 中进行集合的交并集操作是一件很容易的事情。

具体方案,如图所示:

这里每个条件都事先将计算好的结果集ID存入对应的key中,选用的数据结构是集合(Set)。查询操作包括:

  • 子类单选:直接根据条件 key,获取对应结果集;

  • 子类多选:根据多个条件 Key,进行并集操作,获取对应结果集;

  • 最终结果:将获取的所有子类结果集进行交集操作,得到最终结果;

*这其实就是所谓的反向索引。*

这里会发现,漏了一个价格的条件。从需求中可知,价格条件是个区间,并且是无穷举的。所以上述的这种穷举条件的 Key-Value 方式是做不到的。这里我们采用 Redis 的另一种数据结构进行实现,有序集合(Sorted Set):

说实话,DataGrip真得牛逼,只是你不会用而已~

将所有商品加入 Key 为价格的有序集合中,值为商品ID,每个值对应的分数为商品价格的数值。这样在 Redis 的有序集合中就可以通过ZRANGEBYSCORE命令,根据分数(价格)区间,获取相应结果集。

至此,方案三的优化已全部结束,将数据的查询与计算通过缓存的手段,进行了分离。在每次查找时,只需要简单的查找 Redis 几次就能得出结果。查询速度上符合了验收的要求。

扩展

分页

这里你或许发现了一个严重的功能缺陷,列表查询怎么能没有分页。是的,我们马上来看 Redis 是如何实现分页的。

分页主要涉及排序,这里简单起见,就以创建时间为例。

如图所示:

!来给IDEA换个酷炫的主题吧!这几个都有点哇塞啊

图中蓝色部分是以创建时间为分值的商品有序集合,蓝色下方的结果集即为条件计算而得的结果,通过ZINTERSTORE命令,赋结果集权重为0,商品时间结果为1,取交集而得的结果集赋予创建时间分值的新有序集合。对新结果集的操作即能得到分页所需的各个数据:

  • 页面总数为:ZCOUNT命令

  • 当前页内容:ZRANGE命令

  • 若以倒序排列:ZREVRANGE命令

数据更新

关于索引数据更新的问题,有两种方式来进行。一种是通过商品数据的修改,来即时触发更新操作,一种是通过定时脚本来进行批量更新。这里要注意的是,关于索引内容的更新,如果暴力的删除 Key,再重新设置 Key。因为 Redis 中两个操作不会是原子性进行的,所以中间可能存在空白间隙,建议采用仅移除集合中失效元素,添加新元素的方式进行。

性能优化

Redis 是内存级操作,所以单次的查询会很快。但是如果我们的实现中会进行多次的 Redis 操作,Redis 的多次连接时间可能是不必要时间消耗。通过使用MULTI命令,开启一个事务,将 Redis 的多次操作放在一个事务中,最后通过EXEC来进行原子性执行(*注意:这里所谓的事务,只是将多个操作在一次连接中执行,如果执行过程中遇到失败,是不会回滚的 *)。

总结

这里只是一个采用 Redis 优化查询搜索的一个简单 Demo,和现有的开源搜索引擎相比,它更轻量,学习成本页相应低些。其次,它的一些思想与开源搜索引擎是类似的,如果再加上词语解析,也可以实现类似全文检索的功能。

最后,未完,待续。。。

往期推荐

必备技能!聊聊二维码扫码登录的原理

来给IDEA换个酷炫的主题吧!这几个都有点哇塞啊!

一二线城市都有哪些互联网公司是大家都想去的?

Kafka 2.8.0发布,与ZooKeeper正式分手!

这是我见过最蛋疼的注册中心与API网关实践!

如果你喜欢本文,欢迎关注我,订阅更多精彩内容

关注我回复「加群」,加入Spring技术交流群

免费领取:SQL基础知识

喜欢的这里报道

↘↘↘

使用 Redis 实现一个轻量级的搜索引擎,牛逼啊 !相关推荐

  1. 使用 Redis 实现一个轻量级的搜索引擎

    今日推荐 后端接口如何提高性能? 16 个写代码的好习惯 为什么不推荐使用BeanUtils属性转换工具 盘点阿里巴巴 34 个牛逼 GitHub 项目 常见代码重构技巧(非常实用) 作者:jason ...

  2. 我用Redis实现了一个轻量级的搜索引擎!

    作者:jasonGeng88 出处:https://github.com/jasonGeng88/blog 大家如果是做后端开发的,想必都实现过列表查询的接口,当然有的查询条件很简单,一条 SQL 就 ...

  3. 牛逼的python代码_牛逼啊!一个随时随地写Python代码的神器

    现在学Python的人越来越多,很多小伙伴都非常有激情,利用碎片时间随时随地学习Python, 大家知道Python是一门编程语言,但是学语言光看不练是没有用的.最好能编程并运行,有没有什么好的神器可 ...

  4. 在 B 站学编程,真滴牛逼!

    互联网让信息更容易触达,但是由于信息过于庞大,筛选出有价值的信息所花费的精力也会很大. 众所周知,B 站是一个学习的网站,很多人在 B 站上大学.考研.学编程,但 B 站的视频资源那更是五花八门,要从 ...

  5. 牛逼!下一代 Docker 镜像构建神器

    点击下方公众号「关注」和「星标」 回复"1024"获取独家整理的学习资料! Docker通过读取Dockerfile中的指令自动构建镜像,Dockerfile是一个文本文件,其中依 ...

  6. 后续:高中毕业的程序员读者到月薪四万的逆袭之路,大写的牛逼!!!

    大家好,我是findyi,前几天分享了一篇文章,是我的一个读者从高中毕业到月薪四万+两套房的逆袭之路,今天是后续- 这个牛逼读者写的上篇在6月5号:一个程序员读者从高中毕业到月薪四万的逆袭之路,牛逼! ...

  7. 牛逼的人很早就开始牛逼了

    作者:拾遗君 来源:拾遗(ID:shiyi201633) "牛逼的人很早就开始牛逼了."这话很有道理.虽然有"大器晚成"一说,但罗马绝对不是一天建成的.能够&q ...

  8. 上海都有哪些牛逼的互联网公司?

    作者:墨菲 来源:https://zhuanlan.zhihu.com/p/191577083 上一篇: 成都都有哪些牛逼的互联网公司? 今天来归纳一下上海都有哪些牛逼的互联网公司? 第一梯队:六巨头 ...

  9. sync神key_告诉你 Redis 是一个牛逼货

    原标题:告诉你 Redis 是一个牛逼货 概述: Redis是一个 Key-Value 存储系统.和 Memcached 类似,它支持存储的 value 类型相对更多,包括 string(字符串). ...

最新文章

  1. php-cgi 重启,自动监测和重启 FastCGI 服务
  2. Qt中禁止调节窗口大小
  3. Spark on K8S 的几种模式
  4. 反思走火入魔 急功近利:一步一步的做 一步一步的走 顺其自然 水到渠成
  5. C语言简单题-找最大的字符串
  6. 科大讯飞2020完整事件抽取系统(bert+数据集)
  7. 太阳能板如何串联_太阳能光伏系统单晶和多晶模组的差异?农村家庭自用如何科学选择...
  8. MySQL Bit Functions and Operators(二进制方法)
  9. C#拾遗系列(5):泛型委托
  10. 侧信道实验实验三 S盒CPA侧信道攻击
  11. mathtype服务器不显示,出现MathType字体无效的情况怎么办
  12. 时间复杂度与空间复杂度-o(1)、o(n)、o(logn)、o(nlogn)、斐波那契
  13. 商迪3D三维虚拟现实VR景区全景漫游展示
  14. Excle超链接图片修改默认打开方式
  15. 淘客用户订单同步思路
  16. java梯形_如何绘制梯形?
  17. 文件如何生成链接插入到微信公众号,如Word、Excel、Pdf等
  18. 小象学院hadoop进阶
  19. 在线副业教程之 02 你学的越多,你赚的越多+你必须开始学习的5个最好的在线副业
  20. 湖北省教育考试院湖北省人事考试网报名入口报名时间软考报名

热门文章

  1. C#读取Adams的Shell文件(*.shl)
  2. Zend Framework 跳转方法(render, forward, redirect)区...
  3. SPOJ 287 Smart Network Administrator
  4. 为何多线程就能提高Java程序的执行效率
  5. csdn文章保存到本地pdf
  6. Android中/system/build.prop文件解读
  7. Android Parcelable和Serializable的区别
  8. KVM虚拟机源代码分析
  9. 5大架构:细数数据平台的组成与扩展
  10. Gps高程拟合matlab代码,几种GPS高程拟合方法分析与比较.doc