谷歌应用引擎NDB数据存储的简单解释。

17

我正在创建一个Google App Engine应用程序(Python),并正在学习一般框架。我一直在研究NDB数据存储的教程和文档,但是在理解概念方面遇到了一些困难。我具有SQL数据库的丰富背景,并且从未使用过任何其他类型的数据存储系统,因此我认为这就是我遇到麻烦的地方。

我当前的理解是:NDB数据存储是实体集合(类似于DB记录),它们具有属性(类似于DB字段/列)。实体使用模型创建(类似于DB模式)。每个实体都有一个键,在存储时为其生成。这就是我遇到困难的地方,因为这些键似乎没有与SQL DB概念中的任何事物类比。它们似乎类似于表的主键,但那些更紧密地绑定到记录,并且实际上本身就是字段。这些NDB键不是实体的属性,而是与实体分开考虑的对象。如果将实体存储在数据存储中,则可以使用其键检索该实体。

我的一个重要问题是:您从哪里获取这些键?我看到的一些文档显示了简单地创建键的示例。我不明白这一点。当实体被存储时,似乎put()方法返回一个可以稍后使用的键。那么如果原始键是由数据存储生成的,您如何只创建键并定义ID呢?

我似乎还在挣扎与键的祖先概念。您可以定义所需的任何类型的父键。有预定义的模式吗?例如,如果我有一个名为“Person”的模型子类,并创建了一个类型为“Person”的键,我是否可以将该键用作任何其他类型的父键?例如,如果我想让'Shoe'键成为'Shoe'键的子级,我是否也可以声明'Car'键为同一个'Person'键的子级?或者添加“Shoe”键后会无法再添加“Car”键?

对于来自主要SQL背景的人,我真的只想简单介绍NDB数据存储及其API。


2
当考虑数据存储时,请忘记您所知道的有关 SQL 的一切。数据存储通过键存储整个实体,包括属性名称。它单独创建与某些标准匹配的索引 - 其中一些是自动定义的,一些是手动定义的。请阅读以下内容 - https://developers.google.com/appengine/docs/python/datastore/entities - Tim Hoffman
2
个人认为,包含GQL是一个巨大的错误,因为来自SQL背景的人看到“select * from MyEntity”就会立即开始用SQL术语思考。这真的是一个导致应用程序性能不佳的配方。但这只是我的观点。 - Tim Hoffman
2个回答

13

我认为你在心里过于复杂。当你创建一个实体时,你可以选择给它一个自定义的命名键,或者省略它并让数据存储选择一个数字ID。无论哪种方式,当你调用put时,数据存储将返回键,键以[<entity_kind>, <id_or_name>]的形式存储(实际上还包括应用程序ID和任何名称空间,但出于清晰起见,我会省略它们)。

您可以通过指定祖先来将实体成为实体组的成员。虽然通常情况下,该祖先不必引用现有实体。祖先所做的一切就是将实体的键包括祖先的键:所以它现在看起来像 [<parent_entity_kind>, <parent_id_or_name>, <entity_kind>, <id_or_name>]。现在,只能通过包含其父键来获取实体。因此,在您的示例中,Shoe实体可以是Person的子项,无论该Person是否已被创建:是子项知道祖先,而不是反过来。

(请注意,祖先路径可以任意扩展:子实体本身可以是祖先等等。在这种情况下,组由树顶部的实体确定。)

作为组的一部分保存实体在一致性方面具有优势,因为实体组内的查询始终保证是完全一致的,而在外部查询仅最终一致。但是,它也有劣势,即实体组的写入速率受限于整个组每秒1次。


好的,我现在明白了。在实体存储之前,我如何为实体分配一个键?如果这不太清楚,我问这个问题的原因是我想知道如何为实体的键分配自己的ID,如果我不能在存储实体之前检索到该键。 - jchitel
我刚才浏览了一下API,看起来我会这样做:entity = Entity(properties=..., id='<my_id>')。然后调用put()返回的键将带有此ID。这正确吗? - jchitel
1
没错,您也可以将完整的ndb.Key对象作为key参数传入。 - Daniel Roseman

7

数据存储类似于内部SQL行标识符,但当然不完全相同。在Appengine中,标识符有点像SQL主键。为了支持许多应用程序实例在服务器云中的分散并发创建新键,AppEngine内部生成键以保证唯一性。您的应用程序定义参数(应用程序标识符、可选命名空间、种类和可选实体标识符),AppEngine使用这些参数来初始化其键生成器。如果您没有提供标识符,AppEngine将生成唯一的数字标识符您可以读取

最终一致性需要时间,因此有时批量请求多个新键更加高效。AppEngine会为您生成一系列数字实体标识符。您可以从键中读取它们的值作为KeyProperty元数据

祖先用于将所有相关实体的写操作组合在一起,以便进行事务隔离。这方面没有预定义的模式,但每个子代只能有一个父代。

在您的示例中,一个特定的鞋子可能有一个特定的人作为父级。另一个特定的鞋子可能有一匹马作为父级。另一个鞋子可能没有父级。许多实体都可以有相同的父级,因此几个汽车实体也可以有该初始人作为父级。数据存储是无模式的,因此由您的应用程序允许或禁止汽车具有马作为父级。
请注意,子代知道其父代,但父代不知道其子代,因为实现这一点将影响可扩展性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接