当前位置:首页 > 问答 > 正文

用Redis搞倒排索引,速度快到飞起,搜索体验直接爽翻天

综合自个人项目实践、早期技术社区分享及Redis官方文档的核心思想提炼)

想做个搜索,比如在自己网站里找文章,最傻的办法就是用户输入一个词,苹果”,然后你的程序就去数据库里,一篇篇文章地翻,看标题、看内容里有没有“苹果”这两个字,文章少的时候还行,要是有了几万篇、几十万篇文章,这么搞每次搜索都得等好几秒,甚至十几秒,用户早就跑了,体验稀烂。

那咋办?用Redis搞个倒排索引,效果立马不一样,倒排索引是个啥?说白了就是换个方向找东西,你不是顺着文章找词,而是提前把每个词对应哪些文章都给记下来,你有三篇文章:是“好吃的红苹果”是“苹果手机真贵”是“香蕉和苹果”

正常顺序(正排)是:文章1 -> 好吃的红苹果;文章2 -> 苹果手机真贵;文章3 -> 香蕉和苹果。 倒排索引是:苹果 -> [文章1, 文章2, 文章3];好吃 -> [文章1];手机 -> [文章2];香蕉 -> [文章3]。

看出来了吧?有了这个“词典”,当用户搜索“苹果”时,你根本不用去扫所有文章,直接在这个“词典”里查“苹果”这个关键词,它立马就告诉你关联了文章1、2、3,这个过程快得离谱,因为就是一次简单的键值查询。

Redis为啥特别适合干这个?因为它就是个放在内存里的超级快键值对数据库,内存读写速度比硬盘快几个数量级,而且Redis本身就是为了这种高速访问设计的,用它来存这个“词典”,查询起来就是毫秒级甚至微秒级的响应,那感觉可不是“爽翻天”么。

具体怎么用Redis实现呢?超级简单,主要用它的集合(Set)或者有序集合(Sorted Set)数据结构。

基础玩法:用Set存索引 对于“苹果”这个关键词,你在Redis里设置一个键(Key),名字可以叫 word:苹果,它的值(Value)是一个集合(Set),这个集合里存放所有包含了“苹果”这个词的文章ID。 命令大概长这样:

SADD word:苹果 1 2 3

这样就把文章ID为1、2、3的三篇文章和“苹果”关联起来了,同样,给“手机”建一个:SADD word:手机 2。 搜索的时候,用户搜“苹果”,你就执行 SMEMBERS word:苹果,Redis瞬间就把文章ID [1,2,3] 返回给你了,如果你要实现“苹果 手机”这样的多词搜索(AND操作),Redis还能直接求交集:SINTER word:苹果 word:手机,结果瞬间出来,就是同时包含“苹果”和“手机”的文章ID [2],这速度,跟你直接从内存里找个数字差不多快,根本不是去数据库里“大海捞针”能比的。

进阶玩法:用Sorted Set搞排名 光找到还不够,我们通常希望最相关的结果排前面,标题里出现“苹果”的比内容里出现的更相关,或者点赞多的文章更靠前,这时候Set就不够用了,得请出有序集合(Sorted Set)。 这次,键还是那个键,word:苹果,但值不再是简单的文章ID集合,而是每个文章ID都附带一个分数(Score),这个分数可以根据你的 ranking 规则来定,标题出现一次加10分,内容出现一次加1分,最后把所有分数加起来。 添加索引的命令变成:

ZADD word:苹果 10 1   # 文章1,假设标题有“苹果”,计10分
ZADD word:苹果 1 2    # 文章2,假设内容有“苹果”,计1分
ZADD word:苹果 8 3    # 文章3,假设标题有“苹果”(但可能不是完全匹配),计8分

搜索的时候,你不再只是简单取回所有ID,而是按分数从高到低取回:ZREVRANGE word:苹果 0 -1,结果就是 [1, 3, 2],这样,最相关的文章自然就排在最前面了,搜索体验又上了一个台阶。

一些实际的细节和好处

  1. 索引构建:这步是“苦活”,一般在文章发布、修改的时候,你就得自动把文章分词(就是把句子拆成一个个关键词),然后更新到Redis里对应的各个关键词的集合中去,这叫“写时建索引”,虽然写的时候稍微慢一点,但换来了读的时候 lightning fast 的速度。
  2. 处理同义词:你想搜“手机”,可能也希望看到“电话”的结果,很简单,在建索引的时候,除了给“手机”这个词加文章ID,也给“电话”这个同义词加上同一个文章ID,或者搜索时,把“手机”和“电话”两个集合合并(SUNION)一下再返回。
  3. 自动补全:Redis还能用有序集合轻松实现搜索框的自动补全提示(Auto Complete),比如你输入“苹”,它就能提示“苹果”、“苹果手机”等,这又是另一个爽点,用的数据结构叫ZSET按字典序排列。
  4. 性能恐怖如斯:因为数据全在内存,而且Redis是单线程模型避免了锁的竞争,处理这种简单的数据结构和命令效率极高,理论上,只要你的Redis服务器内存足够大,能装下所有的索引,那么无论你的文章库有多大,每次搜索的响应时间都能稳定在极低的水平,几乎无感延迟,这才是“速度快到飞起”的根本原因。

天下没有免费的午餐,用Redis搞倒排索引,最大的代价就是耗内存,你的关键词越多,文章越多,这个索引占用的内存就越大,而且你得保证Redis不能随便挂掉,需要做高可用方案,但考虑到现在内存已经不那么贵了,以及Redis集群方案的成熟,用内存换来的这种极致搜索体验,对于很多追求响应速度的应用来说,是完全值得的,特别是那些对搜索实时性要求高、并发量大的场景,比如新闻站、电商平台、社交媒体的即时搜索,这套方案简直就是大杀器。

当你受够了数据库模糊查询(LIKE)的慢速,亲手用Redis搭起一个倒排索引,然后看着搜索响应时间从秒级降到毫秒级以下的时候,那种爽快感,真的只有试过的人才知道,它不是什么高深莫测的技术,就是一种“思想转变+合适工具”带来的巨大性能提升。

用Redis搞倒排索引,速度快到飞起,搜索体验直接爽翻天