我想就在Google App Engine中进行强大一致性读/写的最佳方式提出一些建议。
我的数据存储在这样的类中。
class UserGroupData(ndb.Model):
users_in_group = ndb.StringProperty(repeated=True)
data = ndb.StringProperty(repeated=True)
我想为这些数据编写一个安全的更新方法。据我所知,我需要避免最终一致的读取,因为它们会冒数据丢失的风险。例如,以下代码是不安全的,因为它使用最终一致的vanilla查询:
def update_data(user_id, additional_data):
entity = UserGroupData.query(UserGroupData.users_in_group==user_id).get()
entity.data.append(additional_data)
entity.put()
如果查询返回的实体是陈旧的,则数据会丢失。
为了实现强大的一致性,似乎我有几个不同的选择。我想知道哪个选项最好:
选项1:
使用始终非常一致的get_by_id()
。但是,在这里做这件事似乎并不是一种巧妙的方式。没有直接从UserGroupData
派生user_id
密钥的简洁方法,因为这种关系是多对一的。要求我的外部客户端存储和发送UserGroupData的密钥似乎有点脆弱和危险。
选项2: 将我的实体放在祖先组中,并执行祖先查询。类似的东西:
def update_data(user_id, additional_data):
entity = UserGroupData.query(UserGroupData.users_in_group==user_id,
ancestor=ancestor_for_all_ugd_entities()).get()
entity.data.append(additional_data)
entity.put()
我认为这应该有效,但将所有UserGroupData
个实体放入一个祖先组似乎是一件极端的事情。它导致写入限制在~1 /秒。这似乎是错误的方法,因为每个UserGroupData
实际上在逻辑上是独立的。
我真正想要做的是对根实体执行强一致的查询。有办法做到这一点吗?我注意到一个建议in another answer基本上会对祖先群体进行分片。这是最好的吗?
选项3:
第三个选项是执行keys_only
后跟get_by_id()
的查询,如下所示:
def update_data(user_id, additional_data):
entity_key = UserGroupData.query(UserGroupData.users_in_group==user_id,
).get(keys_only=True)
entity = entity_key.get()
entity.data.append(additional_data)
entity.put()
据我所知,这种方法可以避免数据丢失,因为我的密钥没有变化,get()
给出了非常一致的结果。但是,我还没有看到过这种方法。这是合理的事吗?它有什么缺点我需要理解吗?
答案 0 :(得分:3)
我认为您还将不一致查询的问题与数据的安全更新混为一谈。
如果user_id在组中,那么像示例UserGroupData.query(UserGroupData.users_in_group==user_id).get()
中的查询将始终只返回一个实体。
如果刚刚添加了索引并且索引不是最新的,那么您将无法获得记录,因此您不会更新记录。
任何更新,无论获取实体的方法如何,都应在事务内部执行,以确保更新的一致性。
对于提高查询一致性的祖先,如果您计划拥有多个UserGroupData实体,那么这一点并不明显。在哪种情况下你为什么要做一个get()。
所以选项3,可能是你最好的选择,只做密钥查询,然后在一个事务内做Key.get()并更新。请记住,跨群组交易仅限于5个实体组。
如果查询所基于的索引已经过时,则采用这种方法,则可能会发生以下三种情况中的一种,
然后,您的代码可以决定采取何种行动。
查询特定用户所需的所有UserGroupData实体需要更新的用例是什么?