10

Google App Engine 文档包含以下段落:

注意:如果您的应用程序在提交事务时收到异常,并不总是意味着事务失败。在事务已提交并最终将成功应用的情况下,您可能会收到 DatastoreTimeoutException、ConcurrentModificationException 或 DatastoreFailureException 异常。尽可能使您的数据存储事务具有幂等性,这样如果您重复事务,最终结果将是相同的。

等等,什么?似乎有一类非常重要的事务根本无法做到幂等,因为它们依赖于当前的数据存储状态。例如,一个简单的计数器,就像在一个赞按钮中一样。事务需要读取当前计数,增加它,然后再次写出计数。如果交易看起来“失败”但并没有真正失败,并且我无法在客户端告诉我,那么我需要再试一次,这将导致一键生成两个“喜欢”。肯定有一些方法可以通过 GAE 来防止这种情况发生吗?

编辑:

似乎这是分布式系统中固有的问题,除了 Guido van Rossum - 请参阅此链接:

应用引擎数据存储事务异常

因此,如果您想要高度的可靠性,看起来设计幂等事务几乎是必须的。

我想知道是否有可能在整个应用程序中实现一个全局系统以确保幂等性。关键是在数据存储中维护事务日志。客户端将生成一个 GUID,然后将该 GUID 包含在请求中(相同的 GUID 将在重试相同的请求时重新发送)。在服务器上,在每个事务开始时,它会在数据存储中查找具有该 ID 的事务实体组中的记录。如果它找到它,那么这是一个重复的事务,所以它会返回而不做任何事情。

当然,这需要启用跨组事务,或者将单独的事务日志作为每个实体组的子项。如果失败的实体键查找速度很慢,也会对性能造成影响,因为几乎每个事务都会包含失败的查找,因为大多数 GUID 都是新的。

就额外数据存储交互的额外成本而言,这可能仍然低于我必须使每个事务幂等的情况,因为这需要大量检查每个级别的数据存储中的内容。

4

3 回答 3

7

丹·威尔克森、西蒙·戈德史密斯等人。在应用引擎的本地(每个实体组)交易之上设计了一个全面的全球交易系统。在高层次上,它使用类似于您描述的 GUID 的技术。dan 处理“潜艇写入”,即您描述的报告失败但后来显示为成功的事务,以及数据存储的许多其他理论和实践细节。erick armbrust 在tapioca-orm中实现了 dan 的设计。

我不一定建议您实施他的设计或使用tapioca-orm,但您肯定会对这项研究感兴趣。

回答您的问题:很多人实施 GAE 应用程序,这些应用程序在没有幂等性的情况下使用数据存储。仅当您需要具有某些类型的保证(例如您所描述的保证)时才重要。了解何时确实需要它们绝对很重要,但您通常不需要。

数据存储是在 Megastore 之上实现的,本文对此进行了深入描述。简而言之,它在每个实体组内使用多版本并发控制,并使用Paxos进行跨数据中心的复制,这两者都有助于海底写入。我不知道数据存储中是否有关于潜艇写入频率的公众号,但如果有,使用这些术语和数据存储邮件列表中的搜索应该可以找到它们。

亚马逊的 S3 并不是真正的可比系统;它更像是一个 CDN,而不是一个分布式数据库。亚马逊的 SimpleDB 具有可比性。它最初只提供最终一致性,并最终添加了一种非常有限的事务,他们称之为条件写入,但它没有真正的事务。其他 NoSQL 数据库(redis、mongo、couchdb 等)在事务和一致性方面有不同的变化。

基本上,分布式数据库总是在规模、事务广度和一致性保证强度之间进行权衡。这以 eric brewer 的CAP 定理最为人所知,该定理表示权衡的三个轴是一致性、可用性和分区容差。

于 2013-07-22T01:27:04.977 回答
1

我想出使计数器幂等的最佳方法是使用集合而不是整数来计数。因此,当一个人“喜欢”某物时,我不会增加计数器,而是将类似添加到这样的东西中:

class Thing {
Set<User> likes = ....

public void like (User u) {
  likes.add(u);
}
public Integer getLikeCount() {
  return likes.size();
}
}

这是在 java 中,但我希望你明白我的意思,即使你使用的是 python。

这种方法是幂等的,你可以添加一个用户,你喜欢多少次,它只会被计算一次。当然,它的代价是存储一个巨大的集合而不是一个简单的计数器。但是,嘿,你不需要跟踪喜欢吗?如果您不想让 Thing 对象膨胀,请创建另一个对象 ThingLikes,并在 Thing 对象上缓存喜欢计数。

于 2014-05-01T21:05:31.557 回答
0

另一个值得研究的选项是应用引擎内置的跨组事务支持,它允许您在单个数据存储事务中对多达五个实体组进行操作。

如果您更喜欢阅读堆栈溢出,这个 SO question有更多详细信息。

于 2014-02-26T20:50:54.107 回答