NDB 层次结构和实体组的 GAE 含义

Posted

技术标签:

【中文标题】NDB 层次结构和实体组的 GAE 含义【英文标题】:GAE Implications of NDB hierarchy and entity groups 【发布时间】:2013-12-29 18:47:04 【问题描述】:

我试图更好地理解GAE NDB docs 中描述的深层层次结构的含义

“例如,“属于”所有者的消息的修订版可能有一个看起来像“的键”

rev_key = ndb.Key('Account', 'Sandy', 'Message', 'greeting', 'Revision', '2')

我将此解释为,如果我执行Revision(parent=rev_key).put(),那么我将有一个 Revision=2 级别的实体组,这意味着 ancestor=rev_key 将具有强一致性的祖先查询并写入 parent=rev_key 将限制为 1 /秒。

但是,在层级结构中更进一步的含义是什么?

比如说我有

rev_key_B = ndb.Key('Account', 'Sandy', 'Message', 'greeting', 'Revision', '3')

写入速度在rev_key_B 级别限制为 1/秒,或者,由于它们共享父级的父级,即ndb.Key('Account', 'Sandy', 'Message', 'greeting'),写入速度在祖先路径的更高级别以及最终限制到整个实体组一直到ndb.Key('Account', 'Sandy')

同样的问题:强一致性。 Revision.query(ancestor=ndb.Key('Account', 'Sandy', 'Message', 'greeting')) 会有强一致性吗?

【问题讨论】:

我发誓曾经有文字说明“实体组”始终是根实体加上所有后代(这意味着 1/秒的限制适用于每个根实体和所有后代,无论您的祖先查询是否在层次结构中更远)。不过,我现在找不到任何参考资料,所以也许我误解了,或者它已经改变了。 @Daniel Roseman 对this post 的评论声称实体组是“根实体下的所有内容”,但我也没有在文档中找到它。我对答案的评论是专门针对这个的。 【参考方案1】:

让我们看看

rev_key = ndb.Key('Account', 'Sandy', 'Message', 'greeting', 'Revision', '2')

表示每个实体都遵循实体路径具有强一致性。所以你是对的。

让我们看看它的实际效果:创建实体

account_sandy = Account.get_or_insert('Sandy')
sandy_message = Message.get_or_insert('greeting', parent=account_sandy.key)
sandy_message_rev = Revision.get_or_insert('2', parent=sandy_message.key)

这将为您提供强大的一致性,并授予您查询事务中所有上述实体的能力。

我正在使用get_or_insert,如果提供的密钥不存在,它会在事务中有效地创建实体。这要求 key 或 id 是唯一的。所以这样你就不能有GreetingSandy as parent的2条消息。

键的工作方式就像二叉树。

S = 桑迪,M=消息,R=修订

    Sandy
   / |   \
  M1 M2   M3 
 / |  \   | \
R1 R2  R1 R1 R2

每条到达终点或更短的路径都可以在事务中运行并提供强一致性*。

在评论中回复:

由于这个例子不足以显示 GAE 和 NDB 的效率,所以下面可能会。

假设您有一个自动点唱机,每个房间都有队列。人们正在将歌曲排队到每个点唱机的每个队列中。

J=点唱机,Q=队列,S=歌曲

   Jukebox       
   / |   \        
  Q1 Q2   Q3     
 / |  \   | \
S1 S2  S3 S4 S5

在这个例子中,使用路径很方便,因此用户的每次操作,在知道 wich jukebox,queue 的情况下,可以 CUD 歌曲实体与 jukebox,queue 和歌曲一致。

*顺便说一句,您也可以锁定不是从根目录开始的路径

还要记住Queries inside transactions must include ancestor filters

【讨论】:

也许这只是 GAE 为展示功能而设计的一个示例,但我不认为有这样的层次结构有什么意义。如果在根级别强制执行一致性,即共享 account_sandy.key 祖先的所有内容,那么拥有消息和修订层会获得什么?为什么不让revision 成为Message 的属性,并让所有消息直接成为account_sandy.key 的子级? @diddleboo 您也可以按照您在消息修订示例中的描述拥有它。但是,每次您想编辑消息时,您也会锁定所有修订。拥有路径的意义在于以一种不会阻塞其余流程的方式隔离一致性。 我想这仍然是我的困惑。给定你制作的 Jukebox 树,我更新了一首歌曲,比如 S1,究竟什么是不是锁定的? @diddleboo 随便你,只要它在路径中。例如,您可以锁定队列和特定歌曲,以更新播放计数或将歌曲存档到另一个队列。所以这真的取决于。在大规模情况下,您甚至可以将所有数据锁定在根实体中。此外,路径不需要从根开始,但可能知道密钥是。 @diddleboo 1) 是 2) 是 3) 1/秒我对此一无所知。关于规模总是取决于操作。例如,如果您正在谈论不经常发生的写入,那么是的,这很好。如果您谈论的是每分钟数千次或每分钟读取/写入的次数,例如计数器,那么应​​用引擎 不适合您。应用引擎需要针对其中最少的部分进行优化。写入中的每个操作大约需要 5 次写入,因此 1000 次将是 4000 次,路径中的每个节点将花费 1 次,依此类推。以具有太多读取或写入的长路径结束。简化您的观点...

以上是关于NDB 层次结构和实体组的 GAE 含义的主要内容,如果未能解决你的问题,请参考以下文章

GAE ndb 存储大型一对多关系的最佳实践

多对多关系 GAE NDB 需要父母还是子女?

GAE,删除 NDB 命名空间

如何通过键从 GAE 数据存储中删除多个实体

GAE数据存储区查询ConjunctionNode错误

给定 NDB 游标,获取上一页结果的正确方法是啥?