使用appengine避免爆炸指数和实体组写入速率限制

时间:2011-03-02 16:43:39

标签: java google-app-engine indexing

我的应用程序中有Course s,TopicTag s。每个Topic可以在多个Course中,并且有许多Tag个。我想查找具有特定Topic x并且在特定Tag y中的每个Course

  1. 天真地,我为每个标准提供了Course ID和Tag ID的列表,因此我可以select * from Topic where tagIds = x && courseIds = y。我认为这个查询需要一个爆炸性的索引:有30个课程和30个标签,我们正在查看~900个索引条目,对吧?在50 x 20时,我远远超过了5000个限制。

  2. 我可以select * from Topic where tagIds = x,然后使用for循环浏览结果,只选择Topic的{​​{1}}。这会返回比我感兴趣的结果更多的结果,并花费大量时间反序列化这些结果,但索引仍然很小。

  3. 我可以courseIds.contain(y)select __KEY__ from Topic where tagIds = x在我的应用程序代码中查找交集。如果集很小,这可能不合理。

  4. 我可以使用select __KEY__ from Topic where courseIds = yTopicTagLookup字段创建一种联接表tagId。这些实体的父键将指向相关的courseId。然后我需要为courseId x tagId x相关主题id的每个组合制作其中一个Topic实体。这实际上就像创建自己的索引一样。它仍然会爆炸,但没有5000入口限制。但是,现在我需要将5000个实体写入同一个实体组,这将违背实体组写入速率限制!

  5. 我可以预先计算每个查询。 TopicTagLookup实体会包含TopicTagQueryCachetagIdcourseId。然后查询看起来像List<TopicId>,获取主题ID列表,然后在列表中使用select * from TopicTagQueryCache where tagId=x && courseId = y调用。与#3类似,但每个courseId x tagId只有一个实体。不需要实体组,但现在我有这个可能很大的列表来维护交易。

  6. Appengine似乎非常适合您可以预先计算的查询。我只是没有找到一种方法来有效地预先计算这个查询。问题基本归结为:

    组织数据的最佳方式是什么,以便我们可以执行设置操作,例如在getAllByIdTopic的交集中查找Course

3 个答案:

答案 0 :(得分:2)

您对选项的评估是正确的。但是,如果您不需要任何排序条件,则App Engine数据存储区已使用合并连接策略或多或少地为您完成了选项3。只需在选项1中详细查询,不需要任何排序或不等式过滤器,App Engine将在数据存储区内部进行合并连接,并仅返回相关结果。

选项4和5类似于this talk中记录的关系索引模式。

答案 1 :(得分:1)

我喜欢#5 - 你实际上是在创建自己的(爆炸式)索引。查询速度很快。

唯一的缺点是您必须手动维护它(下一段),并且检索Topic实体将需要额外的查询(首先您查询TopicTagQueryCache以获取主题ID然后您需要实际检索主题。)

更新您建议的TopicTagQueryCache也不应该是个问题。我不担心会以交易方式进行 - 当你更新Topic时,这个“索引”只会在短时间内失效(最糟糕的是,你的Topic会暂时显示在结果中应该不再出现,并且可能需要花一点时间才显示出它应该出现的新结果 - 这似乎并不那么糟糕)。您甚至可以在任务队列上执行此更新(以确保这些潜在的大量数据库写入都成功,以便您可以快速完成请求,以便您的用户不会等待。)

答案 2 :(得分:0)

正如你自己所说,你应该安排你的数据,以方便你的应用程序的扩展,因此在组织数据的最佳方式是什么,这样我们就可以进行设置操作,如在交叉点找到主题课程和标签?

您可以通过创建CourseRef和TopicRef的对象来保存这些集合的索引,这些对象仅包含Key,ID部分是相应实体的实际Key。这些“Ref”实体将位于特定标记下,因此没有实际的Key重复。所以给定标签的结构是: Tag \ CourseRef ... \ TopicRef ...

通过这种方式给定标签和课程,您可以构建Key Tag \ CourseRef并执行ancestor Query,它可以获取一组您可以获取的密钥。这非常快,因为它实际上是一个直接访问,这应该处理大型课程或主题列表而不会出现List属性的问题。

此方法将要求您在某种程度上使用DataStore API。 正如您所看到的,这给出了一个特定问题的答案,而且该模型对其他类型的Set操作没有任何好处。