google-app-engine - 使用 appengine 数据存储祖先路径进行高效搜索

标签 google-app-engine indexing google-cloud-datastore hierarchy objectify

我们有一个 mulch-tenancy,我需要根据索引属性范围和客户端 ID 在一个巨大的应用引擎数据存储中搜索和获取。是否使用 Ancestor Paths使其高效?或者,同样可以使用额外的过滤器来完成

例如通过 objectify 获得前 100 名的薪水

Key<Clients> clientIdKey = Key.create(Clients.class, 500)
ofy().load().type(Salaries.class).ancestor(clientIdKey).order("-salary").limit(100).list()

或者只是

ofy().load().type(Salaries.class).filter("clientId = ", 500 ).order("-salary").limit(100).list()

我的假设是,在第一种情况下,属于任何其他客户端的所有实体都将被忽略,但在以后的情况下,它将进行全面扫描,这将更加昂贵。这个假设是否正确?

此外,索引“salary”是全局存储还是根据祖先进行分区以便索引更新仅发生在同一祖先内?这将减少更新索引所花费的时间,并且当我们永远不会跨不同客户端查询时,这将是一个很好的解决方案。

最佳答案

我需要指出的第一件事是数据存储不进行表扫描。除了几个异常(exception)(最明显的之字形合并),GAE 查询仅遵循索引 - 因此通常这些类型的问题归结为“哪个索引维护起来更有效?”

让我们从第二种情况开始(请注意,我将 Salary 单数化,我认为这是您的意图):

ofy().load().type(Salary.class).filter("clientId = ", 500 ).order("-salary").limit(100).list()

这需要一个关于Salary { clientId, salary } DESC 的多属性索引。 GAE 会将索引导航到 Salary/clientId/500 的开头,然后一次读取每个索引记录。它将在任意数据中心的索引表上执行此操作 - 由于这些索引表是异步复制的,因此您将获得最终一致的结果。

为了让实体参与多属性索引,每个单独的属性都必须自己编入索引。如果 Salary 没有其他索引属性,编写单个 Salary 将花费:

  • 1 写实体操作
  • 2 为 clientId 索引(asc 和 desc)写入
  • 2 为 salary 索引(asc 和 desc)编写
  • 1 为多属性索引编写 { clientId, salary } DESC

现在我们来看第一种情况:

ofy().load().type(Salary.class).ancestor(clientIdKey).order("-salary").limit(100).list()

这需要在您的 datastore-indexes.xml 中使用不同的多属性索引。这次您需要一个关于 Salary { ancestor, salary } DESC 的索引。此外,GAE 的默认行为是从数据中心的法定人数中读取,以使其成为强一致性操作。这应该比其他方法慢一些(虽然不贵),但是,您可以显式指定最终一致性以获得相同的“任何数据中心”行为:ofy().consistency(Consistency.EVENTUAL).load( )... 这里的好处是您可以选择强一致性。

祖先方法的另一个好处是您不需要在 clientId 上维护单一属性索引。以下是您编写此薪水时会发生的情况(假设没有其他索引字段):

  • 1 写实体操作
  • 2 为 salary 索引(asc 和 desc)编写
  • 1 为多属性索引编写 { ancestor, salary } DESC

这可以使您的系统便宜得多。多属性索引的最大成本通常是所有(否则不相关)双向单属性索引的成本,您必须将其简单地作为 GAE 的标志进行维护。


关于你的最后一个问题,它可能有助于解释什么是 GAE 索引表。有三个用于索引的 BigTable 表,在所有应用程序之间共享。前两个是单属性索引表(一个升序,一个降序)。它们的内容大致如下所示:

{appId}/{entityKind}/{propertyName}/{propertyValue}/{entityKey}

通过范围扫描(BigTable 的基本操作之一),您可以确定哪些实体与您的查询匹配。这也是为什么仅键查询快速/便宜的原因;您可以立即返回 key ,而无需进行后续查找。

多属性索引表看起来(同样,这并不准确)如下所示:

{appId}/{entityKind}/{prop1name}/{prop1value}/{prop2name}/{prop2value}/.../{entityKey}\

使用 Salary { clientId, salary } DESC 上的多属性索引的一些值可能更容易可视化:

yourapp/Salary/clientId/500/salary/99000/aghzfnZvb3N0MHILCxIFRXZlbnQYAQw
yourapp/Salary/clientId/500/salary/98000/aghttydiisgAJJ3JGS0ij44JFAjasdw

同样,您可以看到 GAE 如何通过执行范围扫描找到与您的查询匹配的实体。

我希望这有助于澄清问题。

关于google-app-engine - 使用 appengine 数据存储祖先路径进行高效搜索,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26338283/

相关文章:

java - 是否可以从元数据_property_解码属性类

java - Android Studio appengine 端点不包括构建器

xpath - MarkLogic:Xpath与搜索

Python 列表问题

python - 从 MultiIndex 中删除单个(子)列

python - 使用ndb和python从GAE中通过URL传递的自动分配的ID中检索 key

python - 祖先查询的写入限制意味着什么?

python - 等效于 App Engine 中的 objects.latest()

google-app-engine - 如果 POST 数据很大,GCP HTTP 负载平衡器返回 502 错误

python - 在 gae 中安装 pytz 会出现 UnknownTimeZoneError