资讯专栏INFORMATION COLUMN

使用 Redis 实现自动补全功能

PrototypeZ / 2347人阅读

摘要:自动补全也可以称为自动提示,类似于在百度搜索的输入框中输入一个字符,下面会提示多个关键词供参考。使用倒是一个很不错的方案。虽然通过的通配符方式也可以实现自动提示,但是这种方法在中数据量较大时也存在性能问题。

自动补全也可以称为自动提示,类似于在百度搜索的输入框中输入一个字符,下面会提示多个关键词供参考。

假设:

输入 a,会自动提示 apple、application、acfun、adobe;
输入 ap,提示 apple、application;
输入 ac,提示 acfun;
输入 ad,提示 adobe。

看到这个功能需求,很简单的方式就是在数据库中直接使用 SQL LIKE 操作符来匹配,但是性能肯定是不行的。使用 Redis 倒是一个很不错的方案。

Redis 是 KV 型数据库,查询都是基于 key 的,key 值必须具有唯一性。

虽然通过 key 的通配符方式也可以实现自动提示,但是这种方法在 Redis 中数据量较大时也存在性能问题。

回到上面的话题,本质就是我希望通过 a 获取 apple、application、acfun、adobe,通过 ap 获取 apple、application,以此类推。那么,需要查询的值就应该作为 key,查询结果应该作为 value。遵循这个思路,可以利用 Redis 的集合存储这些值。为什么不用列表,而使用集合?因为我希望查询出来的词不会重复,而集合的特性就是元素唯一,性质决定用途,所以就使用集合。一般情况下,查询出来的词也会按照热度频率什么的排序,需要排序的话,就要使用 Redis 的有序集合。

以 word: 为前缀,方便管理和区别(保证唯一性),后面跟上要查询的值,例如:word:a(其实这样还是无法确保唯一性,就假设这个 key 在 Redis 中是唯一的)。
向集合中添加 关键词:

bashZADD word:a 0 apple 0 application 0 acfun 0 adobe
ZADD word:ap 0 apple 0 application
ZADD word:app 0 apple 0 application
ZADD word:appl 0 apple 0 application
ZADD word:apple 0 apple
ZADD word:appli 0 application
……

上面还没有添加完成,完整添加的代码如下:

phpredis = $redis;
    }


    public function add($word) {
        $len = mb_strlen($word, "UTF-8");
        for ($i = 1; $i <= $len; $i++) {
            $sub = mb_substr($word, 0, $i, "UTF-8");
            $this->redis->zAdd(self::PREFIX . $sub, 0, $word);
        }
    }

}

看以上代码,应该发现我给有序集合每个元素的 score 为 0,这个意义何在?不应该给每个词不同的 score,以便给搜索结果排序吗?确实如此,但是这样会存在一个问题:某一个词会出现在多个集合中,如果该词的热点要增加,那么就需要同时更新多个集合中该词的 score。这显然是不合理的。

那么到底应该怎么办哩?再创建一个有序集合,专门用于存放这些词和它们的 score。最后,将查询结合和这个记录热度的集合做交集,就能得出按热度排列后的结果。

设置这些词的热度:

bashZADD word_scores 100 apple 80 adobe 70 application 60 acfun

交集:

bashZINTERSTORE word_result 2 word_scores word:a WEIGHTS 1 1
ZRANGE word_result 0 -1 withscores

具体 PHP 实现代码如下,仅供参考:

phpredis = $redis;
    }


    public function add($word) {
        $len = mb_strlen($word, "UTF-8");
        for ($i = 1; $i <= $len; $i++) {
            $sub = mb_substr($word, 0, $i, "UTF-8");
            $this->redis->zAdd(self::PREFIX . $sub, 0, $word);
        }
    }


    public function incScore($word, $score = 1) {
        return $this->redis->zIncrBy(self::WORDS_PREFIX, $score, $word);
    }


    public function search($keyword, $stop = 5) {
        $this->redis->zInter(self::RESULT_PREFIX, array(self::PREFIX . $keyword, self::WORDS_PREFIX), array(1, 1));
        return $this->redis->zRevRange(self::RESULT_PREFIX, 0, $stop, true);
    }

}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/38716.html

相关文章

  • 使用 Redis 实现自动补全功能

    摘要:自动补全也可以称为自动提示,类似于在百度搜索的输入框中输入一个字符,下面会提示多个关键词供参考。使用倒是一个很不错的方案。虽然通过的通配符方式也可以实现自动提示,但是这种方法在中数据量较大时也存在性能问题。 自动补全也可以称为自动提示,类似于在百度搜索的输入框中输入一个字符,下面会提示多个关键词供参考。 假设: 输入 a,会自动提示 apple、application、acfun、...

    or0fun 评论0 收藏0
  • 使用 Redis 实现自动补全功能

    摘要:自动补全也可以称为自动提示,类似于在百度搜索的输入框中输入一个字符,下面会提示多个关键词供参考。使用倒是一个很不错的方案。虽然通过的通配符方式也可以实现自动提示,但是这种方法在中数据量较大时也存在性能问题。 自动补全也可以称为自动提示,类似于在百度搜索的输入框中输入一个字符,下面会提示多个关键词供参考。 假设: 输入 a,会自动提示 apple、application、acfun、...

    mumumu 评论0 收藏0
  • 【数据库】Redis进阶篇

    摘要:子进程负责把进程内的数据分批写入文件,这个过程属于密集操作,通常子进程对单核利用率接近。如果部署多个实例,尽量保证同一时刻只有一个子进程执行重写工作。避免在大量写入时做子进程重写操作,这样将导致父进程维护大量页副本,造成内存消耗。 欢迎关注公众号:【爱编码】如果有需要后台回复2019赠送1T的学习资料哦!! 继续接着上一篇【数据库】Redis基础篇 事务 为了保证多条命令组合的原子性,...

    ssshooter 评论0 收藏0
  • 使用php+phantomjs构建一只简单爬虫的开发思路

    摘要:唠叨最近频繁的给客户做采集功能既然代码无法公布所以一直想写个思路类的文章既然是简单的爬虫那么一切自然以简单为原则能少用的都少用比如和这两个神兵利器就被我省略了打造这只虫子就像是樵夫砍柴一般该磨刀还是要磨刀远观拜拜山头对象站点国内知名电商平台 唠叨: 最近频繁的给客户做采集功能,既然代码无法公布,所以一直想写个思路类的文章.既然是简单的爬虫,那么一切自然以简单为原则,能少用的都少用,比如...

    liangdas 评论0 收藏0
  • java | Spring Boot 与 Redis 实现 Cache 以及 Session 共享

    摘要:完成状态编写中已完成维护中原文是一个使用编写的开源支持网络基于内存可选持久性的键值对存储数据库维基百科是目前业界使用广泛的基于内存的数据库。 完成状态 [ ] 编写中 [ ] 已完成 [x] 维护中 原文 Redis Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库 ------ 维基百科 Redis 是目前业界使用广泛的基于内存的...

    ssshooter 评论0 收藏0

发表评论

0条评论

PrototypeZ

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<