【问题标题】:Redis PFADD to check a exists-in-set queryRedis PFADD 检查存在于集合中的查询
【发布时间】:2016-07-02 00:41:56
【问题描述】:

我需要处理队列中的多条记录。但由于一些外部问题,这些项目可能会偶尔出现多次。 我只需要处理一次物品

我计划使用 PFADD 到 redis 每条记录(作为 md5sum),然后查看是否返回成功。如果这显示没有增量,则记录是重复的,否则处理记录。

这看起来很简单,但我在使用 PFADD 时误报太多了

有没有更好的方法来做到这一点?

【问题讨论】:

    标签: redis


    【解决方案1】:

    作为概率数据结构,Redis 的 HyperLogLog 表现出 0.81% 的标准误差。您可以通过使用多个 HLL 来减少(但永远不会消除)误报的可能性,每个 HLL 都会计算您记录中不同哈希函数的值。

    另请注意,如果您使用的是单个 HLL,则无需对记录进行哈希处理 - 只需 PFADD 原样即可。

    或者,使用 Redis 集来保留所有标识符/哈希/记录,并使用 SISMEMBER 进行 100% 准确的成员资格测试。当您存储每个已处理的元素时,这种方法需要更多 (RAM) 资源,但除非您的队列真的很大,否则对于适度的 Redis 实例来说应该不是问题。为了控制内存消耗,请根据日期在 Set 之间切换并在 Set 键上设置到期时间(另一种方法是使用单个 Sorted Set 并通过将旧项目保留在分数中来手动从中删除旧项目)。

    【讨论】:

    • SISMEMBER会在内存中存储所有的“成员”吗,我每天可以有1000万条md5记录,我需要存储一周
    • @Ram Redis 基本上是一个内存数据库,使用集合的危险在于它们不能很好地与 Redis 集群配合使用(如果您有大量数据,您可能希望在某个时候使用它) )。总的来说,我会推荐一个简单的哈希(set,hset)。
    • @Ram - 您需要使用 SADD 将成员添加到集合中,SISMEMBER 只是一个成员测试。是的,所有值都将保存在 RAM 中 - 7000 万个 md5 值应该是大约 2GB 的原始数据。
    • @nha “集合的危险在于它们不能很好地与 Redis 集群配合使用”——这不是真的。无论您是使用 Redis Cluster 还是独立的,Set 都能很好地工作。从这个意义上说,集合与哈希没有任何不同。
    • @ItamarHaber Redis 集群实现了 Redis 的一个子集 (redis.io/topics/cluster-spec#implemented-subset)。具体参见这句话“执行复杂的多键操作的命令,如 Set 类型联合或交集,只要键都属于同一个节点,就可以实现。”,所以我认为值得注意的是 Redis 中集合的行为和 Redis 集群不一样。从这个意义上说,它们与哈希不同(尽管只要您远离联合/交叉点,它们是相同的,并且您的答案完全有效)。
    【解决方案2】:

    通常在分布式系统中,您必须在处理项目之间进行选择:

    • 最多一次
    • 至少一次

    只处理一次会很方便,但这通常是不可能的。

    话虽如此,对于您的特定用例可能存在可接受的解决方法,并且正如您建议的那样,存储已处理的项目可能是可接受的解决方案。

    请注意,尽管PFADD 使用 HyperLogLog,它速度很快,scales but is approximate 关于项目的数量,所以在这种情况下,我认为这不是你想要的。 但是,如果您认为错误概率很小,那么这里最合适的数据结构是Bloom filter(如here for Redis 所述),它可以以非常节省内存的方式实现。

    一个简单、高效且 recommended 的解决方案是使用一个简单的 redis 键(例如哈希)来存储类似布尔值(“0”、“1”或“true”、“false”)例如使用HSETSET with the NX 选项指令。如果您愿意,也可以将其放在namespace 下。它还具有能够使密钥过期的额外好处。

    它会避免你使用一个集合(不是SET 命令,而是SINTERSUNION 命令),如果你想扩展到更多,它不一定适用于Redis cluster不止一个节点。 SISMEMBER 仍然很好(但缺少一些哈希函数,例如生存时间)。

    如果您使用散列,我还建议您选择collisions than md5 机会较少的散列函数(冲突意味着两个不同的对象最终具有相同的散列)。

    哈希的另一种方法是在将每个项目放入队列时为其分配一个 uuid(如果您想获得一些时间信息,则为 squuid)。

    【讨论】:

    • 我认为 HSET 是一个很好的解决方案,带有 NX 选项的普通 SET 也是一个不错的选择,如果队列中的项目在一段时间后没有重复,您可以添加过期时间
    • @PerroVerd 完全正确(我以为我写过它但分心了)。基本的 Redis 操作通常已经足够好了(唯一的事情是避免扫描整个数据库,并保持键之间的关系简单)。
    • @nha 使用“布尔”哈希而不是 Set 不仅尴尬而且更浪费,因为您建议也存储 fasle/0 条目(有趣的是,Redis 的 Set 在内部使用哈希实现)。集合是成员资格测试的自然选择,您对答案的最后编辑只会降低它的质量 - 关于哈希冲突的部分实际上非常正确,但现在完全丢失了不准确性。最后,Set 是完全集群安全的,它是多键跨槽操作,无论数据结构如何,都不支持。
    • @ItamarHaber 1 - 感谢您解释反对票(假设是你) 2 - “无论数据结构如何,它都是不支持的多键跨槽操作”是的,这是在我上次编辑 3 中解释这一点的意图 - 我不建议存储错误值,也许我需要重新措辞。 4 - 我认为我没有提到任何关于“集群安全”的内容,只是警告了 Redis 和 Redis 集群之间的行为差​​异。否则我同意 Sets 对于 Redis 的成员资格测试是很自然的(仍然不太确定 Redis 集群)。
    猜你喜欢
    • 1970-01-01
    • 2020-11-17
    • 1970-01-01
    • 2017-12-23
    • 2012-11-03
    • 2019-01-21
    • 1970-01-01
    • 1970-01-01
    • 2023-02-09
    相关资源
    最近更新 更多