2014-09-04 121 views
0

我一直在使用App Engine几年,似乎一次又一次与HDR一起弹出的问题是,当您在一个屏幕上更新数据时,您将有时如果只读请求是在更新的一两秒内完成的,则会在该屏幕的只读版本上检索旧数据。我知道这已被讨论herehere。我知道问题是什么,但我想知道适当的解决方案可能是什么?这是一个我可以在哪里陈旧数据的例子。谷歌App Engine保存并避免获取旧数据

假设您构建购物清单应用程序。

Entity 1: ShoppingList 
Entity 2: ShoppingListItem 

ShoppingList实体上有一个字段,用于保存列表中的总量项目。第二个实体是您的ShoppingListItem。当您添加或删除ShoppingListItem时,您需要更新ShoppingList上的总数。你有两种方法来更新总数。

  1. 查询数据库并统计ShoppingListItem表中的项目数。
  2. 自动递增缓存的总字段+/- 1,而不需要统计数据库。

有了这两种解决方案,您最终会得到过时的数据。 #1可能已过时,因为添加/删除的原始保存可能尚未传播。因此查询会错过新记录。当您快速连续添加或移除多个项目时,#2将会有过时的数据。总数更新为保存1但保存2,保存1可能仍在传播,对于保存2的ShoppingList总计查询仍将反映保存前的原始状态1.

所以我的问题是,什么是正确的方法来解决这个问题对我来说,似乎你有两种选择:

  1. 使用缓存总数时总是使用memcached实体。保存不会清除内存缓存,而是直接将更新后的实体注入到内存中。这使memcache保持最新状态。这当然意味着不会触及memcache的查询(例如获取所有ShoppingList实体的列表)可能仍会返回陈旧的数据。但至少当所有的保存完成后,数据库中的缓存总数将是正确的。
  2. 处理您将要陈旧的数据,然后找出将来某个时间清理它的方法。可以安排一个TaskQueue重新查询ShoppingList并在10-30秒内更新总数或者计划一个每30秒运行一次的Cron Job并查找所有已更新的ShoppingList实体。

我倾向于解决方案#1。思考?

回答

0

我认为在大多数情况下,@ david-w-smith的回答是正确的。但是,我在他的回答中提到了,使用实体组存在很大的局限性。该限制是实体组每秒只能处理一次写入。对于只有少数用户的应用程序而言,这可能是正确的,但对于社交网络来说这是不可接受的。

正如Google文档(https://developers.google.com/appengine/docs/java/datastore/structuring_for_strong_consistency)也提出的那样,我使用memcache实现了一个相当极端的缓存实现。所有的购物清单查询结果都存储在缓存中,当列表被更新,添加或删除时,我也会更新缓存中查询结果的状态。我担心维护两个数据副本(数据库和memcache)的自然复杂性,但这是UnitTests的用处。对?

4

选项3是给https://developers.google.com/appengine/docs/java/datastore/structuring_for_strong_consistencyhttps://developers.google.com/appengine/docs/java/datastore/structuring_for_strong_consistency仔细阅读。你遇到的是“最终一致性”的典型表现。要特别注意实体组和祖先查询。

+0

感谢您的建议。不幸的是我不能使用实体组,因为每秒写入限制为1。从文档:“这种方法通过写入每个留言簿的单个实体组来实现强大的一致性,但它也将留言簿的更改限制为每秒不超过1次写入(支持的实体组限制)” – dirkoneill 2014-11-12 00:25:08