如何从数据存储的角度而不是数据库的角度来思考?

185
作为一个例子,Google App Engine使用Google Datastore而不是标准数据库来存储数据。有人有使用Google Datastore代替数据库的提示吗?我的思维已经完全训练成直接映射到表结构的对象关系,现在很难看到其他东西。我可以理解Google Datastore的一些好处(如性能和分布数据的能力),但某些良好的数据库功能被牺牲了(如连接操作)。
有没有与Google Datastore或BigTable合作过的人有任何可以给出的建议?

DataSource是一个旧的API,我们正在逐步删除它 - 它与数据库连接模型非常紧密相关。DataStore是低级API,允许访问基于“原始”流式GIS内容的方法;使用FeatureReaders和FeatureWriter。 - Murali Krishna Pinjala
现在,Google Cloud SQL为Google App Engine提供关系型数据库支持。如果您仍在寻找数据存储的解决方案,可以使用Google Cloud SQL - Chandana
你可能想要查看Mungo Datastore API:http://bit.ly/13eSDpr - quarks
8个回答

150

与“传统”的关系型数据库相比,App Engine数据存储有两个主要的需要适应的方面:

  • 数据存储区别不了插入和更新。当您对实体调用put()时,该实体将使用其唯一键存储到数据存储区,并覆盖具有该键的任何内容。基本上,数据存储区中的每个实体类别都像一个巨大的映射或排序的列表。
  • 查询受到更多限制。首先,没有连接。

要了解的重要事情 - 以及这些差异背后的原因 - 是Bigtable基本上就像一个巨大的有序字典。因此,put操作只是为给定键设置值 - 无论该键的先前值如何,而获取操作仅限于获取单个键或连续范围的键。通过索引可以实现更复杂的查询,它们基本上只是自己的表格,允许您将更复杂的查询实施为对连续范围的扫描。

一旦掌握了这一点,您就有了理解数据存储区能力和限制所需的基本知识。可能看起来是任意限制的限制可能更有意义。

关键在于,尽管这些是相对于关系型数据库可以做的限制,但正是这些限制使其能够实现Bigtable设计的规模。在SQL数据库中,您根本无法执行看起来不错但极其缓慢的查询。

在如何更改数据表示方面,最重要的是预计算。尽可能地在数据存储区中预先计算数据并将其存储。如果要选择随机记录,请生成随机数并将其与每个记录一起存储。这里有一整本这种技巧的食谱和诀窍在此处


43
我一直采用的思维转换方法是完全忘记数据库存在的事实。在关系数据库世界中,您总是需要考虑数据规范化和表结构。抛弃它们,只需布局网页。将它们全部放出来,现在看看它们。你已经完成了三分之二的工作。
如果您忘记了数据库大小很重要,而且数据不应该重复使用的概念,那么您已经完成了四分之三的工作,甚至不需要编写任何代码!让您的视图决定您的模型。您不再需要像关系世界中那样将对象变成二维对象。您现在可以存储形状对象。
是的,这是对这个过程的简化解释,但它帮助我忘记了数据库,只是制作了一个应用程序。到目前为止,我已经使用这种哲学建立了4个App Engine应用程序,并且还会有更多的应用程序。

2
我喜欢“让你的视图决定你的模型”这部分。我认为这是来自关系数据库管理系统的一个限制,但它简化了一切。 - cbednarski

23

每当有人说“这不是关系型数据库”时,我总会笑一笑。我在Django中编写了cellectr,并在下面放了一个模型片段。正如您所看到的,我有由用户管理或执教的联赛。我可以从联赛中获取所有经理,或者从给定的用户那里返回她执教或管理的联赛。

仅因为没有特定的外键支持并不意味着您不能拥有具有关系的数据库模型。

我的两分钱。


class League(BaseModel):
    name = db.StringProperty()    
    managers = db.ListProperty(db.Key) #all the users who can view/edit this league
    coaches = db.ListProperty(db.Key) #all the users who are able to view this league

    def get_managers(self):
        # This returns the models themselves, not just the keys that are stored in teams
        return UserPrefs.get(self.managers)

    def get_coaches(self):
        # This returns the models themselves, not just the keys that are stored in teams
        return UserPrefs.get(self.coaches)      

    def __str__(self):
        return self.name

    # Need to delete all the associated games, teams and players
    def delete(self):
        for player in self.leagues_players:
            player.delete()
        for game in self.leagues_games:
            game.delete()
        for team in self.leagues_teams:
            team.delete()            
        super(League, self).delete()

class UserPrefs(db.Model):
    user = db.UserProperty()
    league_ref = db.ReferenceProperty(reference_class=League,
                            collection_name='users') #league the users are managing

    def __str__(self):
        return self.user.nickname

    # many-to-many relationship, a user can coach many leagues, a league can be
    # coached by many users
    @property
    def managing(self):
        return League.gql('WHERE managers = :1', self.key())

    @property
    def coaching(self):
        return League.gql('WHERE coaches = :1', self.key())

    # remove all references to me when I'm deleted
    def delete(self):
        for manager in self.managing:
            manager.managers.remove(self.key())
            manager.put()
        for coach in self.managing:
            coach.coaches.remove(self.key())
            coaches.put()            
        super(UserPrefs, self).delete()    

12

我来自关系型数据库世界,然后发现了这个Datastore。花了几天时间才摸清楚它的用法。好吧,以下是我的发现。

你一定已经知道Datastore是为了扩展而构建的,这是它与关系型数据库不同之处。为了更好地处理大型数据集,App Engine进行了一些更改(有些意味着很多更改)。

关系型数据库 VS DataStore
结构
在数据库中,我们通常将数据结构化为表格、行,在Datastore中则变成了Kinds和Entities

关系
在关系型数据库中,大多数人都遵循一对一、多对一、多对多的关系。在Datastore中,虽然没有"Join"的概念,但我们仍然可以使用"ReferenceProperty"来实现规范化,例如一对一关系示例

索引
通常在关系型数据库中,我们会创建主键、外键、唯一键和索引键等索引来加快搜索速度和提高数据库性能。而在数据存储中,您必须至少为每个种类创建一个索引(无论您是否喜欢,它都会自动生成),因为数据存储根据这些索引搜索实体,相信我,这是最好的部分。在关系型数据库中,您可以使用非索引字段进行搜索,尽管需要一些时间,但是可以。在数据存储中,您不能使用非索引属性进行搜索。

计数
在关系型数据库中,使用count(*)更容易,但在数据存储中,请不要以常规方式思考(是的,有一个count函数),因为它有1000限制,并且会像实体一样产生小操作的成本,这不好,但我们总有好的选择,我们可以使用分片计数器

唯一约束
在关系型数据库中,我们喜欢这个功能,对吧?但是数据存储有自己的方式。你不能定义一个属性为唯一的:(。

查询
GAE Datastore提供了更好的功能,类似于SQL中的LIKE(哦不!Datastore没有LIKE关键字),即GQL

数据插入/更新/删除/选择
这是我们所有人都感兴趣的地方,就像在关系型数据库中一样,我们需要一个查询来进行插入、更新、删除和选择,Datastore也有put、delete、get(不要太激动),因为Datastore使用写入、读取、小操作(阅读 Datastore调用成本)来表示put或get,这就是数据建模发挥作用的地方。您必须最小化这些操作并保持应用程序运行。为了减少读操作,您可以使用Memcache


6
请查看Objectify文档。页面底部的第一个评论说:
“很好,虽然您编写此文档是为了描述Objectify,但这也是我所读过的最简洁的App Engine数据存储解释之一。谢谢。” https://github.com/objectify/objectify/wiki/Concepts

3

如果你已经习惯了思考ORM映射实体,那么像谷歌应用引擎这样的基于实体的数据存储就是这样工作的。对于类似连接的操作,可以查看引用属性。你不需要关心它是否使用BigTable作为后端或其他什么东西,因为后端由GQL和Datastore API接口抽象。


1
引用属性的一个问题是它们可能会快速创建1+N查询问题。(先拉取1个查询以查找100个人,然后为每个人进行另一个查询以获取person.address。) - 0124816
“reference properties”的链接已经损坏,可能是由于Java支持的添加。请尝试访问以下链接:http://code.google.com/appengine/docs/python/datastore/entitiesandmodels.html#References - Spike0xff
链接已修复。如果您有足够的声望,可以随意编辑任何答案。 - Mark Cidade

0
我对数据存储的看法是,种类(kind)可以理解为表格,而实体(entity)则是表格中的每一行。如果谷歌去掉了种类,那么它就只是一个没有结构的大表格,你可以在实体中随意存储任何内容。换句话说,如果实体没有与种类相关联,你几乎可以给实体赋予任何结构并将其存储在一个位置(就像一个没有结构的大文件,每一行都有自己的结构)。
现在回到原始评论,Google Datastore和Bigtable是两个不同的东西,所以不要把Google Datastore与数据存储意义上的Datastore混淆。Bigtable比Bigquery更昂贵(这也是我们没有选择它的主要原因)。Bigquery具有适当的连接和RDBMS语言,而且更便宜,为什么不使用Bigquery呢?话虽如此,Bigquery确实存在一些限制,取决于您的数据大小,您可能会遇到这些限制。
此外,在考虑数据存储方面,我认为正确的说法应该是“考虑NoSQL数据库”。现在市面上有太多的NoSQL数据库可供选择,但是在谷歌产品中,除了谷歌云SQL(即MySQL),其他所有产品都是NoSQL。

-6

作为一个根植于数据库世界的人,对我来说,数据存储就像是一个巨大的表格(因此得名“bigtable”)。但BigTable并不是一个好的例子,因为它做了很多其他典型数据库可能不会做的事情,但它仍然是一个数据库。除非你知道自己需要构建类似Google的“bigtable”,否则使用标准数据库应该已经足够了。他们需要这样做是因为他们正在处理大量的数据和系统,并且没有任何商业可用的系统可以真正按照他们所需的方式完成工作。

(bigtable参考:http://en.wikipedia.org/wiki/BigTable


这个问题特别涉及到使用Bigtable的Google App Engine;使用关系型数据库不是一个选项。 - Nick Johnson

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