【问题标题】:Filtering Redis Hash Entries过滤 Redis 哈希条目
【发布时间】:2011-08-06 05:15:44
【问题描述】:

我正在使用 redis 存储每个散列约 10 万条记录的散列。我想在给定的哈希中实现过滤(分面)记录。请注意,一个哈希条目可以属于 n 个过滤器。

在阅读了thisthis 之后,看起来我应该:

  1. 为每个过滤器实现一个排序的 SET。 SET 中的值对应于 HASH 中的键。
  2. 从给定的过滤器 SET 中检索 HASH 键。
  3. 一旦我从 SET 中获得了 HASH 键,就从 HASH 中获取相应的条目。这应该会给我所有属于过滤器的条目。

首先,上述方法在高层次上是否正确?

假设方法没问题,我缺少的一点是检索 HASH 条目的最有效实现是什么?我的想法是否正确,一旦我有了 HASH 键,我应该使用 PIPELINE 将多个 HGETALL 命令排队通过每个 HASH 键?有更好的方法吗?

我对使用 PIPELINE 的担忧是,我相信它会在为命令提供服务时阻止所有其他客户端。我将对过滤后的结果进行分页,每页有 500 个结果。由于多个基于浏览器的客户端执行过滤,更不用说填充 SET 和 HASH 的后端进程,如果 PIPELINE 确实阻塞,听起来可能会出现很多争用。有人可以对此发表看法吗?

如果有帮助,我使用的是 2.2.4 redis,web 客户端使用 predis,后端使用 servicestack。

谢谢, 保罗

【问题讨论】:

  • 我正在尝试做类似的过滤器,但我有大量的数据集(100 万条记录)要过滤。你有没有找到更好的方法在 redis 中进行过滤?

标签: hash filter redis facet


【解决方案1】:

个别操作确实会阻塞,但这并不重要,因为它们不应该长时间运行。听起来您检索的信息比实际需要的多 - HGETALL 将返回 100,000 项,而您只需要 500 项。

发送 500 个 HGET 操作可能有效(假设集合同时存储哈希和密钥),但使用哈希可能是过早优化的一种情况 - 使用常规密钥和 MGET 可能会更好。

【讨论】:

  • 感谢汤姆的回复。你是对的,我误解了 HGETALL 的目的。虽然您的回答很有用,但我不会接受它,因为我觉得它并没有真正让我更接近原始问题。我听到您所说的过早优化,但似乎排序集是实现过滤的公认方式,而散列是存储“对象”的最佳方式。我觉得我只是在遵循最佳实践,而不是做任何不寻常的事情。
【解决方案2】:

Redis 是一个无锁非阻塞异步服务器,因此在使用流水线时不会添加 争用。 Redis 在收到每个操作后就会愉快地处理它们,因此在实践中可以处理多个流水线操作。本质上,redis-server 并不关心操作是否流水线化,它只是在接收到每个操作时处理它们。

流水线的好处是减少客户端延迟,而不是在发送下一个操作之前等待来自 redis-server 的每个操作的响应,客户端可以在一次写入中一次泵送所有操作,然后读回所有操作一次阅读中的响应。

这方面的一个例子是在我的Redis mini StackOverflow clone 中,每次点击都会调用ToQuestionResults(),因为操作是流水线的,所以会在 1 个 Socket 写入调用上发送所有操作,并在 1 个 Socket 阻塞读取中读取结果,这更多高效而不是每次调用阻塞读取:

https://github.com/ServiceStack/ServiceStack.Examples/blob/master/src/RedisStackOverflow/RedisStackOverflow.ServiceInterface/IRepository.cs#L180

我对使用 PIPELINE 的担忧是 我相信它会阻止所有其他 服务命令时的客户端。

这不是一个有效的问题,我不会过多地考虑 Redis 是如何在这里工作的,假设它在流水线不阻塞处理其他客户端命令的情况下最有效地工作。从概念上讲,您可以认为 redis-server 以 FIFO 顺序处理每个命令(流水线或非流水线)(即不会浪费时间等待/读取整个管道)。

您正在描述更接近 MULTI/EXEC(即 Redis 事务)的东西,其中所有操作在 Redis 服务器读取 EXEC(即 EOF 事务)后立即完成。这也不是问题,redis-server 仍然不会浪费任何时间等待接收您的整个事务,它只是将部分命令集排队在一个临时队列中,直到它收到最终的 EXEC,然后立即处理所有这些。

这就是redis如何通过处理每个命令来实现原子性,一次一个,一旦收到它们。由于没有其他线程,因此没有线程上下文切换,没有锁,也没有多线程问题。它基本上通过非常快速地处理每个命令来实现并发。

所以在这种情况下,我会使用流水线,因为它总是一个胜利,所以你流水线的命令越多(因为你减少了阻塞读取计数)。

【讨论】:

    【解决方案3】:

    我认为您误解了流水线的作用。在发送所有命令时它不会阻塞。它所做的只是缓冲命令,然后在最后一次执行它们,因此它们被执行就好像它们是一个单一的命令一样。任何时候都不会发生阻塞。 redismulti/exec也是如此。在 redis 中最接近阻塞/锁定的方法是使用 watch 进行乐观锁定,如果自调用 watch 以来已写入 redis 密钥,这将导致 exec 失败。

    比在管道块中调用hget 500 次更有效的是只调用hmget('hash-key',*keys),其中keys 是您正在查找的500 个哈希键的数组。这将导致对 redis 的一次调用,这与它是流水线的一样,但执行起来应该更快,因为你没有在 ruby​​ 中循环。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-29
      • 2012-07-02
      • 1970-01-01
      • 2015-09-18
      • 2021-06-01
      • 1970-01-01
      • 2018-08-26
      • 2019-05-21
      相关资源
      最近更新 更多