3

假设我们有一个带有字段的简单 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 (rating=>user_id) Zlist Salary (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相当有效?

4

2 回答 2

3

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

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

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

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

  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 服务器上运行它。

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

于 2012-04-18T16:12:37.087 回答
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 数据库会简单地使用部分快速排序或等效方法来仅对返回的结果进行排序。

于 2012-05-10T06:22:19.953 回答