我们在Google云数据存储区中有一个包含100M行的表。查找大量密钥(500K-1M)的最有效方法是什么?
对于上下文,一个用例可能是我们有一个大内容数据存储区(想想域中的所有网页)。此数据存储包含每个文档的预爬网内容和元数据。但是,每个文档都可能被许多用户所喜欢。现在,当我们有新用户并且他/她说他/她喜欢文档{a1, a2, ..., an}
时,我们想知道是否已经抓取了所有这些文档ak {k in 1 to n}
。这就是我们想要进行上述查找的原因。如果我们还没有一部分文档,我们会立即开始抓取它们。是的,最终目标是检索所有这些文档内容并使用它们来构建用户配置文件。
我目前的想法是发出一堆batch个查询请求。每个查找请求最多可包含1K的密钥[1]。但是,为了获得一组1M中的每个密钥,我仍然需要发出1000个请求。
另一种方法是使用自定义的中间层来快速查找(例如,可以使用布隆过滤器或类似的东西)来节省多个请求之间的时间。假设我们从不删除密钥,每次插入密钥时,我们都会通过中间层添加密钥。 bloom-filter跟踪我们拥有的密钥(具有可容忍的误报率)。由于这是一个自定义层,我们可以提供一个没有限制的微服务。假设我们可以响应要求存在1M键的请求。但是,这肯定会增加我们的设计/实现复杂性。
有没有更有效的方法呢?也许更好的设计?谢谢!
答案 0 :(得分:3)
我建议以更具可扩展性(且成本更低)的方法来解决问题。
在您提到的用例中,您可以一次处理一个文档,每个文档在数据存储区中都有相应的实体。 网页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,这意味着从创建文档实体(及其启动的爬网作业)开始,直到它出现在查询结果中可能需要一段时间。没什么大不了的,可以通过爬行作业中的一些逻辑来解决,以防止和/或删除文档重复。
我将“喜欢”信息保存为将文档映射到用户的小实体,与文档和用户实体分开,以防止在单个实体中维护可能很长的列表的缺点,请参阅{{ 3}}和Manage nested list of entities within entities in Google Cloud Datastore。
当用户喜欢带有特定网址的网页时,您只需检查匹配的文档实体是否存在:
检查用户是否喜欢某个特定文档会成为一个这样的映射实体的简单查询(因为它最终也是一致的,所以要小心)。
有了这样的方案你不再需要进行那些大规模的查找,你一次只做一次 - 这没关系,用户喜欢文件一次是恕我直言,比提供大量喜欢的文件更自然。