如何在数据存储而不是数据库中思考?
例如,Google App Engine使用Google数据存储区(而不是标准数据库)来存储数据。有人对使用Google数据存储区而不是数据库有任何提示吗?看来我已经训练好自己的思维,以100%地考虑直接映射到表结构的对象关系,现在很难看到任何不同的东西。我可以理解Google数据存储区的一些好处(例如性能和分发数据的能力),但是却牺牲了一些良好的数据库功能(例如联接)。
使用Google Datastore或者BigTable的人是否有与他们合作的好建议?
解决方案
根植于数据库世界,对我而言,数据存储将是一个巨大的表(因此名称为" bigtable")。 BigTable是一个不好的例子,因为它做了很多其他的事情,而典型的数据库可能做不到,但它仍然是数据库。除非我们知道需要构建类似Google的" bigtable"之类的东西,否则使用标准数据库可能会很好。他们之所以需要这样做,是因为他们正在一起处理大量的数据和系统,并且没有任何一个商业上可用的系统能够真正以证明他们需要完成工作的确切方式来完成这项工作。
(bigtable参考:http://en.wikipedia.org/wiki/BigTable)
我一直在进行思维转换的方式是完全忘记数据库。
在关系数据库世界中,我们始终必须担心数据规范化和表结构。丢掉这一切。只是布局你的网页。莱大家都出去了。现在看看他们。你已经在那里2/3了。
如果我们忘记了数据库大小很重要且不应重复数据的想法,那么位置就是3/4,甚至不必编写任何代码!让意见决定模型。我们不必像在关系世界中那样将物体变成二维物体。我们现在可以存储具有形状的对象。
是的,这是对磨难的简化解释,但是它帮助我忘记了数据库,只是制作了一个应用程序。到目前为止,我已经使用这种理念制作了4个App Engine应用程序,并且还会有更多应用程序。
如果我们习惯于考虑ORM映射的实体,那么基本上就是基于实体的数据存储(例如Google的App Engine)如何工作的。对于诸如连接之类的东西,我们可以查看参考属性。我们真的不需要担心它是将BigTable用于后端还是其他方式,因为后端是由GQL和Datastore API接口抽象的。
与"传统"关系数据库相比,关于App Engine数据存储区有两点要习惯:
- 数据存储区在插入和更新之间没有区别。当我们在实体上调用put()时,该实体将使用其唯一键存储到数据存储中,所有具有该键的内容都将被覆盖。基本上,数据存储区中的每种实体类型都像一个巨大的地图或者一个已排序的列表。
- 正如我们所提到的,查询的局限性要大得多。首先不要加入。
要意识到的关键因素以及这两种差异背后的原因是,Bigtable基本上就像一个巨大的有序字典。因此,放置操作仅设置给定密钥的值,而不管该密钥的任何先前值,并且提取操作仅限于提取单个密钥或者连续范围的密钥。索引使更复杂的查询成为可能,这些索引基本上只是它们自己的表,使我们可以将更复杂的查询实现为对连续范围的扫描。
一旦了解了这一点,便拥有了了解数据存储的功能和局限性所需的基本知识。似乎是任意的限制可能更有意义。
这里的关键是,尽管这些是我们在关系数据库中可以执行的操作的限制,但是这些相同的限制也使得可以按比例扩展到Bigtable设计要处理的规模。我们根本无法执行在表面上看起来不错但在SQL数据库中非常慢的查询。
在如何改变你的代表数据而言,最重要的是预计算。与其在查询时进行联接,不如预先计算数据并将其存储在数据存储中。如果要选择随机记录,请生成一个随机数并将其与每个记录一起存储。这里有这些技巧和窍门的完整食谱。编辑:食谱不再存在。
当人们发现它与关系无关时,我总是会轻笑。我用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()