钥匙清单或单独的型号?

时间:2012-03-27 21:29:20

标签: google-app-engine

我正在构建一个包含用户及其活动的应用。现在我正在考虑设置数据存储模型的最佳方法。哪一个最快/首选,为什么?

A

class User(db.Model):
    activities = db.ListProperty(db.Key)
    ...
class Activity(db.Model):
    ...

activities = db.get(user.activities)

class User(db.Model):
    ...
class Activity(db.Model):
    owner = db.ReferenceProperty(reference_class=User)
    ...

activities = Activity.filter('owner =', user)

5 个答案:

答案 0 :(得分:2)

如果给定的活动只能拥有一个所有者,请务必使用ReferenceProperty

  • 这是ReferenceProperty
  • 设计的内容
  • 它会自动为您设置反向引用,这可以很方便,因为它可以为您提供双向链接(与ListProperty不同,这是一个单向链接)
  • 强制要链接的东西是正确的类型/类
  • 它强制只有一个用户链接到给定的活动
  • 如果您愿意,它可以让您自动获取链接对象,而无需编写显式查询

答案 1 :(得分:0)

我猜测差异将是微不足道的,可能更多地取决于您的应用,而不是基于您的模型的读/写时间的具体差异。

如果您要使用每次获取用户时用户所做的每项活动的信息,我会说使用第一个选项。换句话说,如果用户在您的应用程序上执行的几乎所有操作都与其活动的大部分内容相吻合,那么始终可以使活动可用。

如果您不是一直需要这些活动,请使用选项B.这将在您需要使用活动时在数据存储上产生单独的请求,但它也会使请求变小。发出额外请求可能会增加开销,而不是提出更大的请求。

所有这一切,如果你在这两种方法之间有明显的区别,我会感到惊讶。通过使用memcache,您将获得更明显的性能改进的区域。

答案 2 :(得分:0)

我不知道性能差异,我怀疑它会有类似的。说到性能,GAE数据存储很难控制。如果您的所有查询都碰到同一个平板电脑(bigtable服务器),那么可能会限制您的性能而不是查询本身。

最大的区别是A比B便宜。既然你有一个你想要的活动列表,你就不需要为你写的每个活动对象写一个索引。如果活动写得很多,那么你的积蓄就会增加。

由于您拥有活动密钥,因此您还可以执行高度一致的get()而不是最终一致的过滤器()

另一方面,您将无法进行向后引用,例如查找给定活动的所有者。您的ListProperty还可以使您达到最大实体大小 - 最终将对每个用户的活动数量进行严格限制。如果你选择B,你可以为每个用户提供大量的活动。

编辑:我忘了,如果你索引ListProperty,你可以有向后引用,但那样,编写你的User对象会变得昂贵,并且索引属性数量的限制会限制你的清单大小。因此,即使有可能,如果您需要向后引用,B仍然是可取的。

答案 3 :(得分:0)

A会更快,因为它纯粹使用键。仅使用键查找对象会直接进入BigTable中的数据节点,而B需要首先查找索引,这样会更慢(并且成本会随着Activity实体的数量而增加)。

如果您永远不需要测试所有权,则可以修改A以不为密钥列表编制索引。这绝对是最便宜,最有效的路线。但是,据我了解,如果您以后需要索引它们app引擎无法追溯更新密钥列表上的索引。因此,如果您确定自己永远不需要,请仅禁用索引。

答案 4 :(得分:0)

C:如何将Activity的父级设置为用户密钥?这样您就可以使用Activity.query(ancestor = user.key)获取用户的活动。

这样您就不需要额外的键/属性+将实体分组为HR数据存储区的好方法。