【问题标题】:Redis filter by range, sort and return 10 firstRedis按范围过滤,排序后先返回10
【发布时间】:2012-04-29 15:25:46
【问题描述】:

假设我们有一个带有字段的简单 mysql 表(用户):

id
rating
salary

我想在指定范围(50-100)内获得 10 个评分和薪水最高的用户,即在 mysql 中会是

SELECT id from user WHERE salary>50 and salary<100 ORDER by rating limit 0, 10

这在 100K 个用户表上运行 20 毫秒。

假设我在 redis 中有相同的: Zlist 评分 (rating=>user_id) Zlist 工资(salary=>user_id)

我在 redis 中看到的所有解决方案都包括复制 100k 工资 Zlist、删除不需要的条目以及与 100k 评级列表合并,例如

zinterstore 1 search salary
zremrange search -inf 50
zremrange search 100 +inf
zinterstore 2 search rating weights 0 1
zrange search 0 10

这绝对是慢的(为什么要复制 100k 元素来删除大部分?)。

有没有什么方法可以实现这一点,至少与 redis 相当有效?

【问题讨论】:

    标签: mysql database search redis


    【解决方案1】:

    您描述的用例无法在 NoSQL 解决方案中优雅地建模。这不是 Redis 的限制。

    让我再解释一下。您正在一个字段上运行范围查询,并在另一个字段上进行排序。这不是 NoSQL 解决方案所擅长的。例如,Google App Engine 禁止此类查询。查看GAE Query Restrictions 并阅读“必须在其他排序顺序之前对不等式过滤器中的属性进行排序”部分

    要获得与不等式过滤器匹配的所有结果,查询会扫描 第一个匹配行的索引表,然后返回所有连续的 结果,直到找到不匹配的行。对于连续 行来表示完整的结果集,行必须按 其他排序顺序之前的不等式过滤器。

    话虽如此,您仍然可以高效地运行查询,但解决方案并不优雅。

    1. 创建薪资范围 - 0-5000、5000-10000、10000-15000 等
    2. 创建像users_with_salary:10000-15000 这样的集合。该集合将包含具有给定范围内薪水的用户 ID。
    3. 类似地,创建“users_with_rating:1-2”之类的集合。该集合将包含在给定范围内具有评级的用户 ID
    4. 现在,运行以下伪代码
    
    String userids[];
    for(rating = 10; rating > 0; rating--) {
      for(salary = min_salary; salary < max_salary; salary += 5000) {
          String salary_key = "users_with_salary:" + salary + "-" + (salary+5000);
          String rating_key = "users_with_rating:" + rating + "-" + (rating+1);
    
          userids.append(redis.sinter(salary_key, rating_key));
    
          if(userids.length > 10) {
             break;
          }
       }
    }
    
    

    使用 redis 2.6 和 lua 脚本,您甚至可以在 lua 服务器上运行它。

    总之,如果您想对数据运行复杂的查询,最好在关系数据库中对其进行建模。

    【讨论】:

      【解决方案2】:

      使用脚本,您可以使用“ZRANGEBYSCORE 工资 50 100”来获取工资在 50 到 100 之间的用户,并将结果存储到 tmp 集中。假设您将用户的评分存储在键“user:[id]”的哈希中,那么您可以执行“SORT tmp BY user:*->rating LIMIT 0 10”。

      不幸的是,您目前无法按与 zset 中的条目关联的分数排序,因此您需要仅或另外将评分值存储在单独的哈希中才能使用此方法。

      当然,您也可以使用“ZINTERSTORE tmp2 2 rating tmp WEIGHTS 1 0”,然后使用“ZRANGE tmp2 0 10”,但这比使用 SORT 效率低得多,因为它需要对所有 tmp2 进行排序的开销(因为它正在被创建)而 SORT with LIMIT 使用部分快速排序算法,该算法有效地仅对实际返回的 10 个结果进行排序。您可能希望保持在 tmp2 左右,以便您可以快速返回该范围内的其他用户,但在这种情况下,存储按评级排序的薪水在 50 到 100 之间的临时用户 zset 可能是有意义的。

      我认为我描述的 SORT 方法实际上在算法上与 SQL 数据库所能达到的一样好。一旦您使用索引按一个字段上的范围进行过滤,我就不知道可以使用另一个字段上的索引来提高对该小结果集进行排序的效率。我相信 SQL 数据库会简单地使用部分快速排序或等效方法来仅对返回的结果进行排序。

      【讨论】:

        猜你喜欢
        • 2017-04-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多