背景web
最近在学习DDIA(Designing Data-Intensive Applications)这本分布式领域很是急经典的入门书籍,里面第二章《数据模型与查询语言》,强调了对一对多、多对1、多对多等各类不一样的数据关系进行建模时要怎样选择合适的数据库模型,并重点阐述了关系型数据库(如PostgreSQL、MySQL)、文档性数据库(MongoDB)、以及进来大热的图数据库在不一样数据关系建模时的使用。这个学习过程当中收益匪浅。其中为了增强对相关知识的理解消化,又选择了一些重点的资料进行阅读。sql
今天要翻译的这一篇《 Why You Should Never Use MongoDB》即是其中一篇关于多对一场景下选择文档数据库MongoDB时,为何有事会对应用的拓展形成灾难性后果的阐述。读完以后,相信咱们会对文档性数据库为何特别适合在多对一场景下使用,以及若是使用不当会形成什么样的后果由很是清晰的认识。mongodb
特别说明下:这一篇文章的标题有夸大的嫌疑,将MongoDB在实际产品开发中的应用视为灾难级的。但其实在适当的场景下使用MongoDB会使得应用在数据表现力方面取得极大的便利性。另外这篇文章的年头也有点久了(2013年发表的),当前最新的MongoDB在数据聚合等方面也作了不少改进。可是之因此这篇文章为何我感受它的价值还很大,其实就在于做者对文档性数据库的使用阐述得特别详细,很是适合咱们理解多对一关系以及文档性数据库的使用原理。数据库
文章很长,整个翻译大体须要一周左右时间,一点点放进来的。编程
---------------------如下开始是原文翻译------------------------------后端
为何你永远都不要使用MongoDB数组
声明:个人工做并非开发数据库引擎,我开发web应用。每一年我都会主导管理4-6个项目的开发工做,所以,我开发了不少的web 应用。在这个过程当中,我发现每个应用都有不一样的需求,须要不一样的数据库引擎。因此,我使用过不少不一样的数据存储组件或应用,基本上全部大家听到过的数据存储应用我都使用过,其中有一些大家可能历来没有据说过。服务器
在个人职业生涯中,我碰到过少数几回数据存储组件选择失误的例子。这里我将分享其中一个选择错误的例子,包括为何最初咱们会选择它,咱们是怎么发现这个选择是错误的,最后又是怎么去修复它,整个过程都发生在Diaspora这个开源项目中。网络
项目数据结构
Diaspora是一个背后有着复杂故事的分布式社交网络应用。时间回退到2010年,四个来自纽约大学的本科生在Kickstarter上发布一个众筹$10,000的视频,目的是利用一个暑假的时间开发一个能够替换Facebook的分布式社交网络应用。他们将这个想法推送给加人、朋友,指望获得一个好的结果。
他们真的是踩在点上,那个时候Facebook正好暴露出一个关于用户隐私的丑闻。因此当他们在Kickstarter发布这个想法的时候,得到了来自6400多人高达200,000美刀的支持,而这些支持仅仅是为了一个一行代码都没有的一个软件项目。
Diaspora是第一个众筹金额远远超过其初期目标的Kickstarter项目。所以,一篇他们事迹的文章被发表在纽约时报上,可是这里有一个小插曲,因为在团队照片的背景图上的一个黑板上面写着一个一个黄色的笑话,且直到文章在纽约时报上出版的时候都没有人发现,因此这篇文章也致使了一些丑闻。也所以,我才第一次了解到这个项目。
得益于他们众筹项目的成功,这些家伙离开了学校来到旧金山开始为这个项目开发代码。他们最终落脚在我工做的地方。那个时候我在Pivotal实验室工做,这些家伙里的其中一个的哥哥也在这个实验室工做,因此Pivotal免费为他们提供办公位置、网络,固然也包括冰啤酒。那段时间我主要服务与官方的客户,在下班的时候就跟他们一些而且在周末的时候为项目贡献代码。
最后,他们在实验室呆了超过两年的时间,在第一个暑假结束额时候,他们已经有一个很小的,可是在某种意义上来说已经可用的分布式社交网络的实现,这个实现开发语言是Ruby on Rails,后端的数据库是MongoDB。
套用一个流行词,让咱们慢慢来解剖这个项目。
分布式社交网络
若是你了解过社交网络的话,那么你已经知道了全部关于Facebook你须要知道的东西。说白了社交网络就是一个运行在一个单一的逻辑服务器上面且让你能够跟别人保持联系的web应用。当你登录应用后,Diaspora的界面结构看起来跟Facebook类似:
在页面的中间展现的是好友的发布事件流。在两边则是没有人回去关注,随便填充的乱七八糟的东西。Diaspora跟Facebook在技术上最重要的区别是终端用户看不到的,这个区别就是‘分布式’的部分。
Diaspora的基础架构并非部署在一个单一的web地址上,而是部署在几百台各自独立的Diaspora服务器上。Diaspora的代码是开源的,所以若是你愿意的话,你也能够在你本身的服务器上面部署一个Diaspora服务。每个Diaspora服务都叫作一个POD,都有本身的数据库以及用户集,并且将与其余全部的Diaspora服务彼此之间相互交互数据。
每个POD都与其余的pod经过HTTP接口进行消息交互。当你在其中一个pod上面建立一个帐号后,若是你没有关注其余人(就是当被人的粉丝),那实际上是至关无聊的。你能够关注同一个pod上的用户,也能够关注跟你处于不一样pod上面的用户。当某个你关注的用户在其余pod上面发布一条状态的时候,会发生下面的几件事:
一、这条状态存储到发布者坐在pod的数据库上。
二、你所在的pod会经过HTTP接口收到通知;
三、这条状态会被被保存在你所在pod的数据库上
四、当你刷新新的事件流时,会看到这条更新的状态会跟随其余你关注的人的状态一块儿展示在你的最新事件流上。
评论也是一样的方式,每一条的状态的全部评论,有一些是来自于跟发布者用同一个pod上的用户,有一些是来自于其余pod上面的用户。每个有权限查看状态的用户均可以看到这些全部的评论,就像你在那些部属单一逻辑服务器上的其余应用所期待的那样。
谁在乎呢?
这个架构有技术上跟法律上的优势。技术上最主要的优势是容错性。
任何一个pod崩溃了,都不会影响到其余的pod的正常运行。系统任然可用,即便发生网络分区,这个特性有一些很是有趣的隐含意义,例如,若是你所在的国家切断了外部的网络,禁止你访问Facebook and Twitter,你所在的本地pod任然可让你跟同一个过年的用户进行联系,即便任何外部的东西你都访问不到。
法律上最重要的优势是服务的独立性。每个pod都是一个独立的实体,受部署所在地的法律约束。每个pod都有各自的服务声明,对于绝大多数的pod而言,你能够在上面发布内容而且不须要放弃你的权利(译者注:这里应该是数据的全部者权利),而不像在Facebook那样,你本身没法拥有数据。Diaspora是一个拥有‘自由’、‘免费’标签的软件,大多数完pod的人都是很是在乎这两点的。
上述就是整个系统级别的大体架构,下面咱们一块儿看下单个pod内部的架构。
每一个pod都是依赖一个后端数据库的Ruby on Rails的web应用,后端数据库最初是MongoDB。某种意义上来说,整个工程的代码库就是一个典型的Rails应用,拥有一个可视化且可编程的用户界面,一些Ruby代码,还有一个数据库。可是从另一个角度上讲它有含有任何东西但且是经典的。
可视化的界面固然是定义网站用户怎样跟Diaspora交互的方式。API接口一个很重要的做用是服务于大量的移动端用户,另外API接口也用于构建联盟,联盟这个词用来描述pod内部之间相互交互的技术用意。应为分布式的本质使得在基础代码上构建了增长了几层的应用,而这些额外的应用是不会出如今一个典型的应用中的。
固然,使用MongoDB来做为数据存储是一个非典型的选择。基本上绝大多数的Rails应用都会选择PostgreSQL做为后端的数据库存储或者Mysql。
上述咱们分析了代码,接下来让咱们将目光转移到咱们存储了哪些类型的数据。
我认为这个词的意思并非你想象中的意思
‘社交数据’是关于咱们本身的朋友网络,朋友们各自的朋友网络,以及全部人在这个大网络中的活动事件。从概念上来说,咱们将‘社交数据’当成这样一个网络:咱们处于网络的中心,而咱们的朋友从这个中心往外扩散并由此造成的一个没有方向的图。
任什么时候候咱们存储社交数据,其实就是在存储上述的拓扑图,还有全部在这个拓扑图的边上流动的活动事件。
过去几年,咱们一直在听到这样一个貌似聪明的论断:社交数据并非关系型的,若是你讲这些数据存储在关系型数据库上,那么你就作错啦。
那咱们有什么其余的选择吗?一些家伙可能会说图数据库是最天然的选择,可是在这里我不想讨论这个图数据库话题,由于他目前仍是很小众的不适合用于实际产品中。另一些家伙可能会说文档型数据库最匹配社交关系数据了,并且在实际的应用中也足够主流。下面让咱们一块儿看一下,为何有人会认为MongoDB会比PostgreSQL更适合社交数据的存储。
MongoDB是怎样存储数据的
MongoDB是一个面向文档的数据库,区别于关系型数据库将数据存储在由一个个单独的行组成的表那样,稳定性的数据库将数据存储在由一个个单独的文档组成的集合里面。在MongoDB里面,一个文档是由一个大型的JSON字符串组成的,这个JSON字符串没有特殊的格式或者模式。
让咱们假设你要对一个相似于下面这样一个关系集合进行建模,其实这个模型已经跟使用MongoDB的Pivotal项目的模型很类似了,也是我能够找到的来解释文档型数据库最好的例子了。
在根节点上,你有一个电视节目(TV show)的集合。每个节目有不少季,每一季有不少集,每一季有不少评论跟不少角色。当用户访问这个视频站点的时候,他们会直接进入某个特定的电视节目页面。在这个页面上用户能够看到这个电视节目的全部季、全部集、全部角色、全部评论。因此从应用的角度来看,每当有用户访问这个电视节目所在的网页时,咱们都但愿将于这个节目关联的数据所有检索出来。
有不少对这些数据建模的方式。在一个典型的关系型数据库中,每个类型的数据都放在一个表里面。因此你会有一个tv_shows
表,一个
seasons
表,并经过外键与tv_shows
挂钩,一个
episodes
表经过外键与seasons
表挂钩,一个reviews
表,一个cast_members
表,这两个表均经过外键与
episodes
表挂钩。所以,若是想要获取一个电视节目的全部信息,那么你就须要有一个链接
5
个数据表的数据库操做。
咱们也能够把这个数据建模为一个内嵌的哈希集合.每个特定电视节目的信息都是一个大的嵌套的k/v数据结构。在‘电视节目’键内部,是一个季的数组,每个‘季’也是一个哈希。在每一季内部,是一个角色的数组,每个角色键也是一个哈希。这是MongoDB对数据建模的方式。每个TV show都是一个文档,文档内部有咱们须要的关于这个节目的全部信息。
下面是Babylon 5这个电视节目对应的文档的例子。
基本就是一个巨大的分型数据结构。
任何咱们须要的关于一个TV show的信息都放在一个文档里面,因此即便是一个很大的完档,咱们任然很容易就能够一次性将全部的信息检索出来。
所以从不少方面来说,这个TV Show应用基本表明了文档型存储的一个理想的使用例子。
可是对于社交网络又是怎样的呢?
是的,当你回过头来看社交网络站点的时候,你会发现咱们在这个网站页面中咱们最关心的只有一个,那就是你的活动事件流。这个活动事件流会根据时间顺序从最新的开始,查询全部你关注的用户的状态发布。每一条发布状态都有嵌套的信息在里面,包括照片、点赞、评论等。
---未完待续 ---