Google App Engine中的非规范化?

背景::::

我正在使用Java的Google App Engine(GAE)。我正在努力设计一个数据模型,以发挥大表的优势和劣势,这是之前的两篇相关文章:

我已初步决定使用完全规范化的骨干,并将非规范化的属性添加到实体中,以便大多数客户端请求只需一个查询即可处理。

我推断,一个完全规范化的骨干会:

  • 如果我在非规范化中编码错误,则帮助维护数据完整性
  • 从客户端的角度在一个操作中启用写入
  • 允许对数据进行任何类型的意外查询(前提是愿意等待)

虽然非规范化数据将:

  • 使大多数客户端请求能够非常快速地得到处理

基本非规范化技术:::

我观看了一个应用引擎视频,描述了一种称为“扇出”的技术。我们的想法是快速写入规范化数据,然后使用任务队列在后台完成非规范化,而无需客户端等待。我在这里提供了视频以供参考,但它长达一个小时,没有必要观看它来理解这个问题:http://code.google.com/events/io/2010/sessions/high-throughput-data-pipelines-appengine.html

如果我使用这种“扇出”技术,则每次客户端修改某些数据时,应用程序都会在一次快速写入中更新规范化模型,然后将非规范化指令触发到任务队列,这样客户端就不必等待它们完成。

问题:::

使用任务队列更新数据的非规范化版本的问题是,客户端可以在任务队列完成对该数据的非规范化之前,对它们刚刚修改的数据发出读取请求。这将为客户端提供过时的数据,这些数据与其最近的请求不一致,使客户端混淆并使应用程序看起来有问题。

作为补救措施,我建议通过 URLFetch 通过异步调用应用程序中的其他 URL 来并行扇除非规范化操作:http://code.google.com/appengine/docs/java/urlfetch/ 应用程序将等到所有异步调用完成后再响应客户端请求。

例如,如果我有一个“约会”实体和一个“客户”实体。每个约会都将包括其计划对象的客户信息的非规范化副本。如果客户更改了他们的名字,应用程序将进行30次异步调用;每个受影响的约会资源一个,以便更改每个约会中客户名字的副本。

从理论上讲,这一切都可以并行完成。所有这些信息都可以在大约对数据存储进行 1 次或 2 次写入所需的时间内进行更新。在非规范化完成后,可以及时响应客户端,从而消除了客户端暴露于不一致数据的可能性。

我看到的最大潜在问题是,应用程序在任何时候都不能有超过10个异步请求调用(记录在这里):http://code.google.com/appengine/docs/java/urlfetch/overview.html)。

建议的非规范化技术(递归异步扇出)::

我建议的补救措施是将非规范化指令发送到另一个资源,该资源以递归方式将指令拆分为大小相等的较小块,以较小的块作为参数调用自身,直到每个块中的指令数量足够小以直接执行。例如,如果具有 30 个关联约会的客户更改了其名字的拼写。我会调用非规范化资源,其中包含更新所有 30 个约会的说明。然后,它将这些指令拆分为 10 组 3 条指令,并使用每组 3 条指令向自己的 URL 发出 10 个异步请求。一旦指令集小于 10,资源就会根据每条指令直接发出异步请求。

我对这种方法的担忧是:

  • 它可以被解释为试图规避应用程序引擎的规则,这会导致问题。(它甚至不允许URL调用自己,所以我实际上必须有两个URL资源来处理相互调用的递归)
  • 它很复杂,有多个潜在故障点。

我真的很感激对这种方法的一些意见。


答案 1

这听起来非常复杂,设计越复杂,编码和维护就越困难。

假设您需要对数据进行非规范化,我建议只使用基本的非规范化技术,但要跟踪正在更新的对象。如果客户端请求正在更新的对象,您知道您需要查询数据库以获取更新的数据;如果没有,您可以依赖非规范化数据。任务队列完成后,它可以从“正在更新”列表中删除对象,并且一切都可以依赖于非规范化的数据。

复杂的版本甚至可以跟踪每个对象的编辑时间,因此给定对象将知道它是否已被任务队列更新。


答案 2

这听起来像是你正在重新实现实例化视图 http://en.wikipedia.org/wiki/Materialized_view


推荐