Mysql Redis按范围筛选,排序并首先返回10

Mysql Redis按范围筛选,排序并首先返回10,mysql,database,search,redis,Mysql,Database,Search,Redis,假设我们有一个简单的mysql表(用户),其中包含以下字段: id rating salary 我想得到10个具有最高评级和指定范围(50-100)工资的用户,即在mysql中 SELECT id from user WHERE salary>50 and salary<100 ORDER by rating limit 0, 10 这是绝对缓慢的(为什么复制100k元素以删除其中大部分?) 是否有任何方法可以实现这一点,至少与redis的效率相当?您描述的用例无法在NoSQL解

假设我们有一个简单的mysql表(用户),其中包含以下字段:

id
rating
salary
我想得到10个具有最高评级和指定范围(50-100)工资的用户,即在mysql中

SELECT id from user WHERE salary>50 and salary<100 ORDER by rating limit 0, 10
这是绝对缓慢的(为什么复制100k元素以删除其中大部分?)


是否有任何方法可以实现这一点,至少与redis的效率相当?

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

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

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

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

  • 创建薪资范围-0-5000、5000-10000、10000-15000等等
  • 创建薪资为10000-15000的用户集。此集合将包含薪资在给定范围内的用户ID
  • 类似地,创建“users\u with_rating:1-2”这样的集合。该集合将包含在给定范围内具有评级的用户ID
  • 现在,运行以下伪代码
  • 
    字符串用户标识[];
    对于(额定值=10;额定值>0;额定值--){
    对于(工资=最低工资;工资<最高工资;工资+=5000){
    字符串salary_key=“users_with_salary:”+salary+“-”+(salary+5000);
    String rating_key=“users_,带有评级:“+评级+”-“+(评级+1);
    append(redis.sinter(salary_key,rating_key));
    如果(userids.length>10){
    打破
    }
    }
    }
    
    使用Redis2.6和lua脚本,您甚至可以在lua服务器上运行它


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

    通过脚本编写,您可以使用“ZRANGEBYSCORE salary 50 100”获取工资介于50和100之间的用户,并将结果存储到tmp集中。假设您将用户的评级存储在哈希键“user:[id]处”,然后您可以执行“按用户排序tmp:->评级限制0 10”

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

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

    我认为我描述的排序方法实际上在算法上和SQL数据库一样好。一旦您使用索引按一个字段上的范围进行筛选,我就知道无法使用另一个字段上的索引来提高对小结果集进行排序的效率。我相信SQL数据库只会使用部分快速排序或等效方法来对返回的结果进行排序

    zinterstore 1 search salary
    zremrange search -inf 50
    zremrange search 100 +inf
    zinterstore 2 search rating weights 0 1
    zrange search 0 10
    
    
    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;
          }
       }
    }