首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Redis按范围过滤,排序优先返回10

问Redis按范围过滤,排序优先返回10
EN

Stack Overflow用户
提问于 2012-04-18 16:27:57
回答 2查看 3.3K关注 0票数 3

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

代码语言:javascript
复制
id
rating
salary

我想得到10个最高评级和工资与指定范围(50-100)的用户,即在mysql中将是

代码语言:javascript
复制
SELECT id from user WHERE salary>50 and salary<100 ORDER by rating limit 0, 10

这在100K用户表上运行了20ms。

假设我在redis中有相同的内容: Zlist rating (rating=>user_id) Zlist salary (salary=>user_id)

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

代码语言:javascript
复制
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的效率相当?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-04-19 00:12:37

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

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

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

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

  1. 创建工资范围-0-5,000,5,000-10000,10000-15000等
  2. Create sets like users_with_salary:10000-15000。此集合将包含在给定用户创建集中具有薪水的用户in,如‘user _with_rating:1-2’。此集合将包含具有给定范围内评级的用户in
  3. 现在,请运行以下伪代码

代码语言:javascript
复制
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服务器上运行此脚本。

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

票数 3
EN

Stack Overflow用户

发布于 2012-05-10 14:22:20

使用脚本,您可以使用"ZRANGEBYSCORE could 50 100“来获取salary在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,这样就可以快速返回范围内的其他用户,但在这种情况下,存储一个临时zset用户,这些用户的薪水按等级排列在50到100之间可能是有意义的。

我认为我描述的排序方法实际上在算法上是SQL数据库所能实现的最好的。一旦使用索引对一个字段的范围进行筛选,我就不知道如何使用另一个字段的索引来提高对小结果集进行排序的效率。我相信SQL数据库会简单地使用部分快速排序或等效物来只对返回的结果进行排序。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10205635

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档