6

这是在 appengine交易文档中...

注意:如果您的应用程序在提交事务时收到异常,并不总是意味着事务失败。在事务已提交并最终将成功应用的情况下,您可能会收到 Timeout、TransactionFailedError 或 InternalError 异常...

考虑以下场景

  1. A我在事务中更新实体。
  2. 事务操作导致上述特殊“异常”,其中事务已提交并最终将被应用
  3. db.get(entity_a_key_goes_here)在第 2 步之后或几乎与第 2 步同时在另一个事务中运行。
  4. 如果上面的第 3 步返回,我通过将密钥设置为和它None来创建该实体(第 3 步和此步骤在同一事务中运行)。entity_a_key_goes_heredb.put()

我的问题:

上述步骤 3 中的事务 操作是否有可能db.get()返回过时的值(或者不是步骤 1 中设置的更新值)?db.get()即使“奇怪的”事务异常发生在它之前,事务操作是否保证返回最新的结果?

4

3 回答 3

2

get 并不是真正的“事务性”;如果一个事务只包含读取,那么提交事务实际上并没有做任何事情。在事务中执行读取只保证一件事:如果在事务中应用任何写入时读取返回的值不再是最新值,则事务将中止并且不会发生任何写入。

因此,允许发生以下事件序列:

  1. 开始事务 1。
  2. 在事务 1 中,您读取实体 A 并返回状态 A1。
  3. 在事务 1 中,您将实体 A 从状态 A1 更新到状态 A2。
  4. 提交事务 1 时,它会因上述异常之一而失败。
  5. 开始事务 2。
  6. 在事务 2 中,您读取实体 A 并返回状态 A1。
  7. 事务 1 在后台应用于数据存储,将 A 从状态 A1 更改为状态 A2。
  8. 结束事务 2(没有提交,因为没有写入)。

但是,这一系列事件是不同的:

  1. 开始事务 1。
  2. 在事务 1 中,您读取实体 A 并返回状态 A1。
  3. 在事务 1 中,您将实体 A 从状态 A1 更新到状态 A2。
  4. 提交事务 1 时,它会因上述异常之一而失败。
  5. 开始事务 2。
  6. 在事务 2 中,您读取实体 A 并返回状态 A1。
  7. 在事务 2 中,您将实体 A 从状态 A1 更新到状态 A3。
  8. 事务 1 在后台应用于数据存储,将 A 从状态 A1 更改为状态 A2。
  9. 事务 2 现在不会成功提交,因为它即将应用的写入是基于实体 A 的过时版本。它将失败并且 A 将留在状态 A2。

因此,将您的第 4 步添加到问题中,您就处于第二个事件序列中。即使实体确实存在,步骤 3 中的 get 也可能返回 None,但在这种情况下后续写入不可能成功:事务已过期,因此无法提交。该事务将被重试,第二次,get 将返回在步骤 1 中编写的对象,这就是您想要的。

所以非常简短的回答是:是的,它可以返回一个陈旧的值,但可以保证,如果结果是陈旧的,那么在同一事务中对该实体的后续写入将失败,因此这实际上不会导致问题。

于 2013-06-13T14:54:23.673 回答
1

我认为在这种情况下第 3 步的结果将是一个陈旧的值。在这种情况下,“最新鲜”的结果可能不是第 1 步中的实体。

我的建议是在检索值时使用 memcache(db.get()当 memcache 未命中时回退到调用),并在更新实体时更新 memcache。使用实体键作为内存缓存键。事实上,这就是 ndb 自动工作的方式。

于 2013-06-11T04:57:27.707 回答
0

从我在“ndb”上阅读的文档中了解到,如果您在“事务”( )中运行您的get()和两者,那么您将不会获得陈旧的数据。'ndb' 将提供更新的数据或两者都失败。put()@ndb.transactional

交易要么失败,要么成功。与其他 dbms 一样,ndb 也维护“日志”。

希望这可以帮助。

于 2013-06-11T17:18:49.440 回答