GQL 查询优化和表架构

Posted

技术标签:

【中文标题】GQL 查询优化和表架构【英文标题】:GQL Query Optimization and Table Architecture 【发布时间】:2010-08-03 19:42:07 【问题描述】:

我一直在使用 Google App Engine,但我的一些数据查询遇到了一些性能缓慢的问题。我读过设计 App Engine 数据存储区与使用 SQL 数据库的思维方式不同,我不确定我这样做是不是最好的方式。我有两个问题要尝试走上正轨:

具体来说:

我有一个Foo 类型和一个UserFoo 类型。每个UserFoo 都是对应Foo 的“实例”,并保存特定于该实例的数据。我的Foo 类型有一个fooCode 属性,它是一个唯一标识符,我使用它们的fooCode 属性将每个UserFoo 映射到每个Foo。然后我使用如下代码对每个 Foo 进行操作:

foos = Foo.all().filter('bar =', bar)
for foo in foos:
    userFoo = UserFoo.all().filter('userKey =', user).filter('fooCode =', foo.fooCode)

注意:我在引用键上使用fooCode,以便我们可以轻松删除并重新添加新的Foos,而不必重新映射所有相应的UserFoos。

一般:

设计 GAE 数据存储表的典型方法以及使用它们的最佳实践是什么?

【问题讨论】:

Foo和UserFoo对象之间是否存在一一对应关系? 请注意,您所显示的实际上并不是 GQL 查询。 1 Foo 到许多 UserFoos。此外,我最初将调用作为 GqlQuery 进行了更改,但因为我读到它们速度较慢而更改了它们。 是的,GqlQuery 比较慢,因为必须解析查询。 【参考方案1】:

这是staircase of gets 反模式。解决办法是ReferenceProperty pre-fetching。

问题是您决定不使用 ReferenceProperty。我建议你重新考虑这个选择。

注意:我使用 fooCode 而不是 参考键,这样我们就可以轻松 删除并重新添加新的 Foos 而不是 然后必须重新映射所有 对应的 UserFoos。

请记住,实体键只是其路径的编码表示:实体及其任何祖先的种类和名称或 ID。如果您删除然后重新创建了 Foo,则只有在赋予不同的名称或 ID 时,它才会有不同的密钥。如果您有某种方式为新旧实体提供相同的fooCode,您可以轻松地使用fooCode 作为键名,这将允许删除然后重新添加Foo 以保留其原始键。

【讨论】:

阅读这些链接很有意义。我现在正在修改我的数据存储。感谢所有指出这一点的人!【参考方案2】:

一般:

尽可能去规范化。

通过其密钥引用实体 只要有可能;这是最快的 从数据存储中获取数据的方法。

具体来说,如果您使用 ReferenceProperty 而不是进入过滤器的代码来建立关系,您的性能可能会显着提高。我猜想查询 UserFoo 的 Foo 比删除和重新映射 Foo 更频繁,是吗?在这种情况下,务实和明智的做法是使用引用属性。

此外,如果 Foo-UserFoo 关系可以非规范化为单个实体,则您完全不需要整个系列的查询。

【讨论】:

【参考方案3】:

我建议进行以下更改:

    在 UserFoo 中使用 ReferenceProperty 来引用其 Foo,或在适当的情况下将其设为子实体。我不明白您关于重新映射现有实体的评论 - 这绝不是必要的。 为每个 UserFoo 添加 'bar' 属性的副本 对 UserFoo.all().filter('bar =', bar).order('userKey') 执行单个查询。结果将按栏过滤并按用户分组,并且只需要一个查询,而不是每个用户一个。 通过对查询调用 .fetch() 立即获取结果,而不是遍历它们。这样效率更高。 如果需要,请使用 ReferenceProperty prefetching 检索每个 UserFoo 的 Foo 对象。

【讨论】:

以上是关于GQL 查询优化和表架构的主要内容,如果未能解决你的问题,请参考以下文章

SQL为王:oracle标量子查询和表连接改写

MySQL(十六)索引优化:索引失效的情况分析

优化 MySQL 查询

优化关联查询

三高Mysql - Mysql索引和查询优化(偏实战部分)

MySQL的索引优化,查询优化