我有一个关于Google数据库云数据存储的设计问题。让我用一个例子解释一下:
我有类似“文章”的实体,其中包含以下属性:
SumOfScore应该是所有相关“得分”实体的总和,它们具有 属性如:
在伪SQL中:
sumOfScore =从得分中选择总和(得分)得分.articleId = article.id
我看到了两种设计方法(使用Google的数据存储API):
1。)没有物品sumOfScore的文章;但总是查询:
这意味着:每次阅读文章时,我都需要对此特定文章进行查询以计算sumOfScore。 想象一下向用户显示的100篇文章的列表。这需要对数据库进行额外的100次查询,只是为了显示每篇文章的分数。
尽管如此:这是使用Relational-DB时的首选方式。没有冗余和良好的规范化。 使用SQL,您只需使用一个join-select来捕获所有数据。 但它对云数据存储感觉不合适。
2。)每当分数实体发生变化时计算sumOfScore:
这意味着:每当添加,删除或更改分数实体时,相关文章 更新sumOfScore属性。
优势:阅读文章时无需其他查询。 sumOfScore在实体本身是多余的。
缺点:每次更改分数时,都会有一个额外的查询和一个额外的写入(更新文章实体)。 sumOfScore可能与实际的Score实体不匹配(例如,通过DB-Console更改了值)
有什么比较有经验的人认为?这种情况是否有共同的最佳做法? 在幕后做JPA或JDO的是什么?
非常感谢
莫斯
答案 0 :(得分:2)
The first thing I recommend you look into the GAE article about sharding counters
这是GAE关于如何处理计数器/总和的最佳实践的文章。这可能有点棘手,因为每次更新元素时都必须使用逻辑来随机选择分片计数器;当你检索你的计数时,你实际上是在取一组实体并对它们进行求和。我已经离开了这条路线但是我不会在这里提供代码,因为我还没有进行过战斗测试。但是如果你只是在整个地方复制/粘贴样本分片代码,你的代码就会匆匆忙忙,所以如果你决定走这条路,那么制作一个抽象或类型的计数器类来重用你的分片逻辑。
另一种选择是使用模糊计数。此方法使用memcache,并以牺牲准确性为代价提供更好的性能。
See the section here labeled "Transient and frequently updated data"
最后一种选择;就是使用SQL。 Its experimental and hot out of the oven (in relation to being used on GAE) but it might be worth looking into.
答案 1 :(得分:1)
Theres第三种可能不妥协的可能性。
您将分数视为文章的孩子,并在文章中保留 sumOfScore 。出于分类目的,该字段将派上用场。由于这两个类来自同一个实体组,因此您可以创建分数并更新事务中的文章。您甚至可以通过查询所有得分来查看父级是否为文章。
这种方法的问题在于,您每秒只能更新一次实体5次。如果您认为自己的活动多于此(请记住,这只是对单个实体的限制而非限制表),您应该查看sharded counter tutorial或查看google io's video解释此问题。
关于同一主题的讨论很充分:How does Google Moderator avoid contention?