13

背景::::

我正在使用适用于 Java 的谷歌应用引擎 (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 资源来处理相互调用的递归)
  • 它很复杂,有多个潜在故障点。

我非常感谢您对这种方法的一些意见。

4

3 回答 3

4

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

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

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

于 2010-07-05T17:06:44.827 回答
3

听起来您正在重新实现物化视图http://en.wikipedia.org/wiki/Materialized_view

于 2010-07-07T20:02:53.827 回答
0

我建议您使用 Memcache 的简单解决方案。从您的客户端更新后,您可以在 Memcache 中保存一个实体,该实体存储状态为“正在更新”的更新实体的密钥。当您完成任务时,它将删除 Memcached 状态。然后您将在读取之前检查状态,如果实体仍然“锁定”,则可以正确通知用户。

于 2013-02-19T07:38:46.287 回答