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
,以便我们可以轻松删除并重新添加新的Foo
s,而不必重新映射所有相应的UserFoo
s。
一般:
设计 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 查询优化和表架构的主要内容,如果未能解决你的问题,请参考以下文章