免责声明:我不构建数据库引擎,但搭建Web应用。每年我大约跑4-6个不同项目,所以我搭建了不少Web应用。我经历过有不同需求及不同的数据存储需求的应用。我部署过你听说过或没听说过的的大部分数据存储。
我也有几次做出了错误的选择。这是一个关于其中一次的故事——原本我们为什么选择它,我们是如何发现它不合适,以及我们如何修复的。这一切都发生在一个开放源码的名为Diaspora的项目中。 Diaspora项目 Diaspora 是一个有着悠久历史的分布式社交网络。早在2010年初,纽约大学四名大学生创建了一个Kickstarter视频,目的是募集10000美元,耗费一个夏天来构建一个替代Facebook的分布式项目。他们将它发送给朋友和家人,并希望有最好的结果。 但他们触到了痛处。当时正好有另一起关于Facebook的隐私丑闻,当尘埃落定,回到他们的Kickstarter项目时,他们已经从6400个不同的人那里筹集了超过200000美元,而同时这个软件项目,却连一行代码都还没有写出来。
Diaspora是第一个大大超越其目标的Kickstarter项目。其结局是,他们的项目新闻被刊登于纽约时报——但它变成了一个丑闻,因为团队照片的背景黑板上面有一个肮脏的笑话,直到最终印刷都没有人注意到这一点。这可是刊登在纽约时报!这个事件的余波实际上是我第一次听说到这个项目。 他们在Kickstarter上成功的结果,是离开学校,来到San Francisco开始写代码。他们最后在我的办公室工作。那时我在Pivotal实验室工作,他们的一个哥哥也在那工作,所以Pivotal给他们提供了免费的办公空间、网络,当然,装有啤酒的冰箱。我白天和官方客户工作,下班后和他们一起玩,周末贡献代码。
他们最后在Pivotal待了两年多的时间。虽然只是在第一个暑假的结尾,他们已经有了一个最小化的,但已经可以使用(在某种意义上说)的一个分布式社交网络,以Ruby on Rails实现,后端用MongoDB。
这里有很多流行词汇,让我们把它分解来看。 “分布式社交网络”
如果你看过《社交网络》,你就知道了所有你需要知道的关于Facebook的事。它是个web应用,它在一个单逻辑服务器上运行,它可以让你与朋友保持联系。一旦你登录,Diaspora的界面在结构上与Facebook很相似:
Diaspora用户界面的截图
在中间有个信息流,显示你所有朋友发布的信息,在边上有一些其他随机的基本没人关注过的东西。Diaspora和Facebook主要的技术差异在于对终端用户的不可见性:它是“分布的”部分。 Diaspora的基础设施并不在一个单独的网络地址上。有几百个独立的Diaspora服务器。代码是开源的,所以如果你愿意,你可以建立自己的服务器。各个服务器,被称作一个pod,有它自己的数据库和自己的用户集合,并可以与其他拥有各自数据库和用户集的Diaspora pod进行交互操作。
不同规模的Pod互相交流,而没有一个中央枢纽。
每个pod通过基于HTTP的API与其它pod通信。一旦你在一个pod上创建了一个帐号,它其实相当无聊,除非你关注一些其他人。你可以在你的pod上关注其他用户,而且你也可以关注其它pod上的用户。当你关注的人在另一个pod上发布一个更新时,将会发生这些事情:
评论按同样的方式运作。在任何单一的发布中,一些评论的评论人可能来自与发布作者相同的pod,而还有一些可能来自其他的pod。任何有权查看这个发布的人将会看到所有的评论,就像你期待的,好像每个人都在一个单一的逻辑服务器上一样。 谁关心?
这个架构有技术上和法律上的优势。技术上的主要优势在于容错机制
这是每个办公室都应该有的一个重要的容错系统。
如果pod中的任意一个宕机,这不会引起其他的也宕机。系统的生存,甚至期望,网络的分割。对于这,有一些有趣的政治上的暗示——例如,如果你在一个关闭了外接网络而无法访问Facebook和Twitter的国家,你的pod依旧会在本地运行,并和你所在国家内的其他人相连接,即使无法访问外部。 主要的法律优势是服务器的独立性。每个pod都是一个法律独立的实体,由其创建所在地的法律所掌管。每个pod也设置自己的服务条款。对于其中的大多数,你可以在不放弃对内容权限的条件下发布内容,这与Facebook不同。Diaspora是一个免费的软件,其意义在于“免费”和“自由”,而且大多数运行pod的人非常在意这种事情。
所以这就是这个系统的架构。我们再来从一个单一的pod看看这个架构。 这是一个 Rails 应用
每个pod都是一个Ruby on Rails应用,后端有一个数据库,最初是MongoDB。从某种程度来说,这个代码库是一个‘典型’的Rails应用——它同时具有一个可视化与可编程的UI,一些Ruby代码,以及一个数据库。但在其他方面,它决不是典型的。
视觉UI显然就是网站用户与Diaspora交互的方式。这个API被各种Diaspora移动客户端使用——这一部分相当典型——但同时它也被用于"federation"(联邦),这也是描述pod之间通讯的技术名词。(我有一次问过这与罗慕伦人的相似点在哪里,结果得到一堆白眼,擦。)所以说这个系统的分布式特性,给代码库增加了典型应用中所不存在的中间层。
当然,MongoDB是一种数据存储的典型选择。而绝大多数的Rails应用程序是由PostgreSQL(近来不常见)或MySQL所支持。
所以代码部分就是这样。我们再来考虑一下我们存储的是什么样的数据。
(译注:罗慕伦帝国是科幻系列《星际旅行》中虚构的外星帝国,2158年,罗慕伦帝国与当时的地球联邦发生了一场核战争。这场战争同时威胁到了其他的一些种族,并最终促使了星际联邦的建立。2160年罗慕伦人被打败,后与星际联邦签订合约并划定中立区。) 我不认为单词的意思是你认为的意思
“社交数据”是关于我们朋友、他们朋友和他们活动的网络信息。从概念上来看,我们确实认为它是一个网络——一个以我们为中心,朋友围绕在我们身边的无向网络。
所有照片来自rubyfriends.com。感谢Matt Rogers、Steve Klabnik、Nell Shamrell、Katrina Owen、Sam Livingston-Grey、Josh Susser、Akshay Khole、Pradyumna Dandwate和Hephzibah Watharkar对#rubyfriends的贡献!
当我们存储社交数据时,我们存储的是那个图的拓扑结构,和那些随着边移动的活动信息。 多年之后的今天,大家公认社交数据并不是关系型的,如果你把它存储在关系型数据库中,你就错了。
但是,其它方案有哪些呢? 有人说图数据库更自然些,不过这里我不打算介绍它,因为图数据库太过小众而不适合用于生产环境。另外一些人说文档数据库对社交数据来说堪称完美,而且也更为主流化足以投入实际使用。下面,让我们看看为什么人们认为社交数据更适合放在MongoDB里,而不是放在PostgreSQL里。 MongoDB 如何存储数据
MongoDB是一个面向文档的数据库。它把你的数据存储在由独立的文档组成的集合中,而不是像关系型数据库那样,存储在由独立的行组成的表中。在MongoDB中, 一个文档是一大块JSON数据,没有特定的格式或模式。
比如说,你需要对下列一组关系进行建模。这和来自于Pivotal的一个使用了MongoDB的项目类似,是我见过的最适合于文档数据库的用例。
根元素是电视节目。每个节目有很多季,每一季都有很多片段,每个片段都有很多评论和演员表。当用户进入这个网站后,一般都是直接访问一个特定电视节目的页面。在这个页面里他们可以看到所有的季、所有的片段、所有的评论和所有的演员表。从应用的角度来看,当用户访问一个页面时,我们就将检索所有有关电视节目的信息。
有很多方法可以为此数据建模。在典型的关系型数据存储中,上面的每一个方框就是一个表。你必须有一个叫电视节目的表、一个有外键是电视节目的每一季表、一个有外键是每一季的片段表以及外键是片段的评论表和演员表。所以,要得到电视节目的所有信息,你必须要在5个表中查询。 我们也可以以这样的数据作为一组嵌套的哈希值进行建模。有关特定电视节目的信息的集合是一个大的嵌套的keyvalue数据结构。 在电视节目里有一个季节的数组,每一个季节是一个hash。 在每个季节里,每一个episodes都是一个hash等等. 这就是Mongo如何建立数据模型的. 每一个电视节目是一个包含我们需要的所有信息的文档。 这是一个电视节目文档的例子, Babylon 5.
它有一些标题的元数据,然后是一个季节的数组. 每个季节本身就是一个带有元数据的哈希数组episodes. 反过来, 每个episode 都有一些reviews和cast_members的元数据和数组。 这就像是一个巨大的分形体数据结构。
集合的集合的集合的集合。就像是个分形体!
所有我们需要的电视节目数据都在一个文档里,所以要检索一次所有信息时是很快的,即使这个文档很庞大。美剧“综合医院”发布了50+季,超过12000集。在我的笔记本电脑上,PostgresSQL查询所有数据得1分钟,而在MongoDB中用一个ID查询时是秒级。
所以,不管怎么说,这个应用对于存储文档模型还是不错的选择。 好吧,那社交数据又是怎样的?
好的,当你进入社交网站后,映入眼帘的唯一重要页面部分是你的活动流。活动流显示了你所有关注人的信息,这些信息是按最新时间排序的。每一条信息里都是网状结构,比如说图片,喜欢,分享以及评论。
网状结构的活动流看起来和上面提到的电视节目很相似。
用户有朋友,朋友有帖子,帖子有评论和喜欢,每一个评论有一个评论者,每一个喜欢有一个喜欢的人。这种关系并不比电视节目的复杂。和电视节目一样,当用户登录后,我们就想一次取出所有的数据。此外,在关系数据库中,所有的数据都是规格化的,这就得在7个表中查询才能得到所有数据。 7个表联合查询。啊!如果将每个用户的活动流作为一个大的非标准化的网状结构来存储的话,要比每一次连接查询看起来要好的多。
在2010年时,Diaspora团队做出了这个决定,并深受Etsy有关用文档结构存储文章的影响,尽管当时他们曾公开远离了MongoDB数据存储。同样的,Facebook的Cassandra也曾呼唤要远离关系数据库。Diaspora与时俱进的选择了MongoDB。从他们的信息数据来看,这样的选择是明智的。 |