GAE 中的分片祖先实体
Posted
技术标签:
【中文标题】GAE 中的分片祖先实体【英文标题】:Sharded ancestor entities in GAE 【发布时间】:2014-03-18 16:13:48 【问题描述】:我正在从事一个基于 GAE 的项目,该项目涉及大量用户群(可能有数百万用户)。我们使用 Datastore 来实现持久性。用户将通过用户名和电子邮件地址来识别,因此这两个属性在所有此类实体中应该是唯一的。因为 Datastore 不支持 ID 以外的唯一字段,所以我们需要事务来确保新用户注册时这些字段的唯一性。为了进行交易,用户实体需要包含在实体组中。
正如here 指出的那样,不建议使用大型实体组。因此,考虑到可能存储的大量用户,我正在考虑将它们放入多个较小的实体组中。每个组将有一个共同的父级,其 ID 由两个唯一字段(例如 MD5 总和的一部分)生成。插入新用户可能如下所示(在 Python 中):
@ndb.transactional
def register_new_user(login, email, full_name) :
# validation code omitted
user = User(login = login, email = email, full_name = full_name)
group_id = a_simple_hash(login, email)
group_key = ndb.Key('UserGroup', group_id)
query = User.query(ancestor = group_key).filter(ndb.OR(User.login = login, User.email = email))
if not query.get() :
user.put()
我看到此解决方案的一个问题是,仅通过 ID 获取用户是不可能的。我们必须使用完整的实体键。
这种方法还有其他缺点吗?有人试过类似的吗?
编辑
正如我在 cmets 中指出的那样,上述哈希值无法正常工作,因为它只会阻止注册具有非唯一电子邮件的用户以及与这些电子邮件匹配的非唯一用户名。如果哈希是基于单个字段计算的,它将起作用。
不过,我发现这种分片的概念本身很有趣,也许值得讨论。
【问题讨论】:
你听说过get_or_insert吗:developers.google.com/appengine/docs/python/ndb/… 是的,但是——据我所知——这种方法只能确保键名的唯一性。我不想使用用户名或电子邮件地址作为实体标识符。 您的代码只会阻止具有相同登录名和相同电子邮件的用户对。任何一个都可以重复! @Greg 是的,同时我也想到了。我将编辑问题并指出这一点。 最终一致性并不意味着您“有机会在查询时找不到用户”。这意味着您可能无法在写入后几秒钟内检索您的实体。这里不需要交易。看我的回答 【参考方案1】:电子邮件地址归用户所有,并且是唯一的。所以有一个很小的变化,有人会(尝试)使用相同的电子邮件地址。
所以我的方法是:get_or_insert 一个新的登录名,这样可以很容易地登录(通过密钥),然后验证电子邮件地址是否唯一。
如果它不是唯一的,您可以丢弃或.....做其他事情
实体组对事务有意义。我对您计划的交易感兴趣,因为我不了解您的实体组密钥哈希。哪些实体将成为实体组的一部分,为什么?
具有相同登录名的用户将成为另一个实体组的一部分,如果我确实了解您的哈希? 看起来您的实体组拥有一个实体。
【讨论】:
我真的很想拥有不容易猜到的用户 ID。如果没有办法与唯一的用户名/电子邮件一起实现这一点,我会按照您的建议(用户名作为键)或完全删除用户名字段来执行此操作。分片的想法是作为解决唯一字段和事务问题的可能解决方案出现的。用户本来是按哈希分组的,这样当新用户注册时,我们可以找到可能发生潜在用户名/电子邮件冲突的组。不过,我误解了哈希概念,它不会像预期的那样在多个字段中起作用。 使用正则表达式,您可以请求包含 8 个字符的用户 ID 和 ...。但您也可以使用 Openid,如 ***、新的 Openid Connect 或 Facebook connect。 不可猜测的用户 ID 也很难让人记住。【参考方案2】:在我看来,您在这里想多了:两个用户同时使用相同的用户名注册的概率是多少? 很苗条。对于这种情况,最终一致性已经足够好了,因为你没有纳秒精度...... 除非您计划拥有比 facebook 更多的用户,每秒都有人注册。
不同的用户几乎不可能使用同一电子邮件注册,因为电子邮件提供商已经为您完成了检查! 只有用户可以尝试使用相同的电子邮件地址开设两个帐户。最终的一致性对于这个查询也足够了。
您的每个用户实体都属于自己的实体组。
实际上,在大多数用例中,您的用户是最明显的根实体:人们使用数据存储是因为他们需要可扩展性,而且大多数时候,面向用户的应用程序需要巨大的规模。 p>
【讨论】:
以上是关于GAE 中的分片祖先实体的主要内容,如果未能解决你的问题,请参考以下文章
获取属性错误:类型对象“Level_2_Headings”没有属性“祖先”(GAE 数据存储)