代码之家  ›  专栏  ›  技术社区  ›  greeness user1775765

查找数据存储中是否存在大量密钥(多达100万个)

  •  0
  • greeness user1775765  · 技术社区  · 6 年前

    我们在谷歌云数据存储中有一个100万行的表。查找大量密钥(500K-1M)存在的最有效方法是什么?

    对于上下文,一个用例可能是我们有一个大的内容数据存储(想想域中的所有网页)。此数据存储包含每个文档的预爬网内容和元数据。然而,每个文档都会受到许多用户的喜爱。现在,当我们有一个新用户,他/她说他/她喜欢文档时 {a1, a2, ..., an} ,我们想知道这些文件 ak {k in 1 to n} 已爬网。这就是我们想进行上述查找的原因。如果有一部分文档我们还没有,我们会立即开始抓取它们。是的,最终目标是检索所有这些文档内容并使用它们构建用户配置文件。

    我现在的想法是 batch 查找请求。每个查找请求最多可包含1K个密钥[1]。然而,要获得一组1M中每个密钥的存在性,我仍然需要发出1000个请求。

    另一种方法是使用定制的中间层提供快速查找(例如,可以使用bloom filter或类似的方法),以节省多个请求之间的时间。假设我们从不删除密钥,每次插入密钥时,我们都会通过中间层添加它。bloom过滤器跟踪我们拥有的密钥(假阳性率可以接受)。由于这是一个自定义层,我们可以提供无限制的微服务。假设我们可以响应一个请求,请求存在1M个密钥。然而,这无疑增加了我们的设计/实现复杂性。

    有没有更有效的方法?也许是更好的设计?谢谢

    [1] https://cloud.google.com/datastore/docs/concepts/limits

    1 回复  |  直到 6 年前
        1
  •  3
  •   Dan Cornilescu    6 年前

    我建议用一种更具可扩展性(且成本更低)的方法来解决这个问题。

    在您提到的用例中,您可以一次处理一个文档,每个文档在数据存储中都有相应的实体。 网页URL唯一标识页面,因此您可以使用它为各个实体生成唯一的键/标识符。通过单键查找(强一致性),您可以确定实体是否存在,即是否已考虑对网页进行爬网。如果没有,则会创建一个新实体并为其启动爬网作业。

    实体键的长度可能是一个问题,请参阅 How long (max characters) can a datastore entity key_name be? Is it bad to haver very long key_names? 。为避免此问题,您可以将URL存储为网页实体的属性。然后,您必须通过url属性查询实体,以确定是否已考虑对该网页进行爬网。这只是 eventually consistent ,这意味着从创建文档实体(及其爬网作业启动)到它出现在查询结果中可能需要一段时间。这没什么大不了的,可以通过爬行作业中的一些逻辑来解决,以防止和/或删除文档重复。

    我将“like”信息作为将文档映射到用户的小实体,与文档和用户实体分开,以避免在单个实体中维护可能很长的列表的缺点,请参阅 Manage nested list of entities within entities in Google Cloud Datastore Creating your own activity logging in GAE/P

    当用户喜欢具有特定URL的网页时,只需检查匹配的文档实体是否存在:

    • 如果它只是创建相似的映射实体
    • 如果没有,并且您使用了上述唯一密钥标识符:
      • 创建文档实体并启动其爬网作业
      • 创建like映射实体
    • 否则:
      • 启动爬网作业,该作业将创建负责重复数据消除的文档实体
      • 稍后,当(唯一)文档实体可用时,启动延迟作业以创建映射实体。可能与爬行作业无关。可能需要一些重试逻辑。

    检查用户是否喜欢某个特定文档将成为一个此类映射实体的简单查询(需要谨慎,因为它最终也是一致的)。

    有了这样的方案,您不再需要进行大量的查找,您一次只需进行一次—这没关系,一个用户一次只喜欢一个文档,这比提供一个大的喜欢文档列表更自然。