mongodb 学习总结

原文地址:http://blog.csdn.net/jakenson/article/details/7060431

 MongoDB的内部构造《MongoDB The Definitive Guide》

MongoDB的官方文档基本是how to do的介绍,而关于how it worked却少之又少,本人也刚买了《MongoDB TheDefinitive Guide》的影印版,还没来得及看,本文原作者将其书中一些关于MongoDB内部现实方面的一些知识介绍如下,值得一看。

今天下载了《MongoDB The Definitive Guide》电子版,浏览了里面的内容,还是挺丰富的。是官网文档实际应用方面的一个补充。和官方文档类似,介绍MongoDB的内部原理是少之又少,只有在附录的一个章节中介绍了相关内容。

对于大多数的MongoDB的用户来说,MongoDB就像是一个大黑盒,但是如果你能够了解到MongoDB内部一些构造的话,将有利于你更好地理解和使用MongoDB。

BSON

在MongoDB中,文档是对数据的抽象,它被使用在Client端和Server端的交互中。所有的Client端(各种语言的Driver)都会使用这种抽象,它的表现形式就是我们常说的BSON(Binary JSON )。

BSON是一个轻量级的二进制数据格式。MongoDB能够使用BSON,并将BSON作为数据的存储存放在磁盘中。

当Client端要将写入文档,使用查询等等操作时,需要将文档编码为BSON格式,然后再发送给Server端。同样,Server端的返回结果也是编码为BSON格式再放回给Client端的。

使用BSON格式出于以下3种目的:

效率

BSON是为效率而设计的,它只需要使用很少的空间。即使在最坏的情况下,BSON格式也比JSON格式再最好的情况下存储效率高。

传输性

在某些情况下,BSON会牺牲额外的空间让数据的传输更加方便。比如,字符串的传输的前缀会标识字符串的长度,而不是在字符串的末尾打上结束的标记。这样的传输形式有利于MongoDB修改传输的数据。

性能

最后,BSON格式的编码和解码都是非常快速的。它使用了C风格的数据表现形式,这样在各种语言中都可以高效地使用。

更多关于BSON的介绍,可以参考:http://www.bsonspec.org

写入协议

Client端访问Server端使用了轻量级的TCP/IP写入协议。这种协议在MongoDB Wiki中有详细介绍,它其实是在BSON数据上面做了一层简单的包装。比如说,写入数据的命令中包含了1个20字节的消息头(由消息的长度和写入命令标识组成),需要写入的Collection名称和需要写入的数据。

数据文件

在MongoDB的数据文件夹中(默认路径是/data/db)由构成数据库的所有文件。每一个数据库都包含一个.ns文件和一些数据文件,其中数据文件会随着数据量的增加而变多。所以如果有一个数据库名字叫做foo,那么构成foo这个数据库的文件就会由foo.ns,foo.0,foo.1,foo.2等等组成。

数据文件每新增一次,大小都会是上一个数据文件的2倍,每个数据文件最大2G。这样的设计有利于防止数据量较小的数据库浪费过多的空间,同时又能保证数据量较大的数据库有相应的空间使用。

MongoDB会使用预分配方式来保证写入性能的稳定(这种方式可以使用–noprealloc关闭)。预分配在后台进行,并且每个预分配的文件都用0进行填充。这会让MongoDB始终保持额外的空间和空余的数据文件,从而避免了数据增长过快而带来的分配磁盘空间引起的阻塞。

名字空间和盘区

每一个数据库都由多个名字空间组成,每一个名字空间存储了相应类型的数据。数据库中的每一个Collection都有各自对应的名字空间,索引文件同样也有名字空间。所有名字空间的元数据都存储在.ns文件中。

名字空间中的数据在磁盘中分为多个区间,这个叫做盘区。在下图中,foo这个数据库包含3个数据文件,第三个数据文件属于空的预分配文件。头两个数据文件被分为了相应的盘区对应不同的名字空间。

上图显示了名字空间和盘区的相关特点。每一个名字空间可以包含多个不同的盘区,这些盘区并不是连续的。与数据文件的增长相同,每一个名字空间对应的盘区大小的也是随着分配的次数不断增长的。这样做的目的是为了平衡名字空间浪费的空间与保持某一个名字空间中数据的连续性。上图中还有一个需要注意的名字空间:$freelist,这个名字空间用于记录不再使用的盘区(被删除的Collection或索引)。每当名字空间需要分配新的盘区的时候,都会先查看$freelist是否有大小合适的盘区可以使用。

内存映射存储引擎

MongoDB目前支持的存储引擎为内存映射引擎。当MongoDB启动的时候,会将所有的数据文件映射到内存中,然后操作系统会托管所有的磁盘操作。这种存储引擎有以下几种特点:

* MongoDB中关于内存管理的代码非常精简,毕竟相关的工作已经有操作系统进行托管。

* MongoDB服务器使用的虚拟内存将非常巨大,并将超过整个数据文件的大小。不用担心,操作系统会去处理这一切。

* MongoDB无法控制数据写入磁盘的顺序,这样将导致MongoDB无法实现writeahead日志的特性。所以,如果MongoDB希望提供一种durability的特性(这一特性可以参考我写的关于Cassandra文章:http://www.cnblogs.com/gpcuster/tag/Cassandra/),需要实现另外一种存储引擎。

* 32位系统的MongoDB服务器每一个Mongod实例只能使用2G的数据文件。这是由于地址指针只能支持32位。

其他

在《MongoDB The Definitive Guide》中介绍的MongoDB内部构造只有这么多,如果真要把它说清楚,可能需要另外一本书来专门讲述了。比如内部的JS解析,查询的优化,索引的建立等等。有兴趣的朋友可以直接参考源代码

 

 

MongoDB的架构

当前架构                           双服务器架构

 

 

 

 

 

 

 

 

 

 

 

 

 


当前架构为单shard+replica Set模式,双服务器为双Shard+Replica Set模式。同一个Shard中的primary和Secondary存储内容一致。而双Shard则是两个Shard分布式存储不同数据,备份由shard内部进行。

双服务器中的两个Shard各含一个primary ,一个secondary,和一个arbiter(arbiter的唯一作用是在primary 宕机后选举新的primary时拥有投票权,用以使存活节点数大于50%,不包括50%,否则系统将整个down掉,以及在票数相同的情况下用以打破选举的平衡,并不存储和读取数据)。

因为同一个shard中,只有primary可以用以写,secondary只是用于对primary节点的备份并用于读操作,然后再primary宕机的情况下接管它的工作。所以,双shard模式下,两个服务器分别包含一个primary,而且同一个shard的arbiter必须和secondary在一个服务器上。这样子既保证了两个服务器都可以进行读、写操作,而且在primary down的时候也能够继续使得选取成功secondary。

后续扩展时,可以再在集群中添加新的shard,然后与老的shard进行balance均衡操作。

MongoDB的特点

MongoDB 是一个面向集合的,模式自由的文档型数据库.

面向集合, 意思是数据被分组到若干集合,这些集合称作聚集(collections). 在数据库里每个聚集有一个唯一的名字,可以包含无限个文档. 聚集是RDBMS中表的同义词,区别是聚集不需要进行模式定义.

模式自由, 意思是数据库并不需要知道你将存入到聚集中的文档的任何结构信息.实际上,你可以在同一个聚集中存储不同结构的文档.

文档型, 意思是我们存储的数据是键-值对的集合,键是字符串,值可以是数据类型集合里的任意类型,包括数组和文档. 我们把这个数据格式称作 "[BSON]"即 "Binary Serialized dOcument Notation."

u  面向文档存储:(类JSON数据模式简单而强大)。

u  高效的传统存储方式:支持二进制数据及大型对象(如照片和视频)。

u  复制及自动故障转移:Mongo数据库支持服务器之间的数据复制,支持主-从模式及服务器之间的相互复制。

u  Auto-Sharding自动分片支持云级扩展性(处于早期alpha阶段):自动分片功能支持水平的数据库集群,可动态添加额外的机器。

u  动态查询:它支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。

u  全索引支持:包括文档内嵌对象及数组。Mongo的查询优化器会分析查询表达式,并生成一个高效的查询计划。

u  支持RUBY,PYTHON,JAVA,C++,PHP等多种语言。

u  面向集合存储,易存储对象类型的数据:存储在集合中的文档,被存储为键-值对的形式。键用于唯一标识一个文档,为字符串类型,而值则可以是各中复杂的文件类型;

u  *模式自由:存储在mongodb数据库中的文件,我们不需要知道它的任何结构定义;

u  *支持完全索引,包含内部对象。

u  *支持复制和故障恢复。

u  *自动处理碎片: 自动分片功能支持水平的数据库集群,可动态添加额外的机器

u 查询监视:Mongo包含一个监视工具用于分析数据库操作的性能

MongoDB的功能

       查询:基于查询对象或者类SQL语句搜索文档. 查询结果可以排序,进行返回大小限制,可以跳过部分结果集,也可以返回文档的一部分.

       插入和更新 : 插入新文档,更新已有文档.

       索引管理 : 对文档的一个或者多个键(包括子结构)创建索引,删除索引等等

常用命令: 所有MongoDB 操作都可以通过socket传输的DB命令来执行.

MongoDB的局限性与不足

本文来源于对Quora上一个问答的整理,主要列举了MongoDB身上一些局限的功能及目前做得不够好的地方。其中包括了原本就并非MongoDB想做的部分,也包括了MongoDB想做但没做好的方面。

  • 在32位系统上,不支持大于2.5G的数据。详见这里
  • 单个文档大小限制为 4 M/16 M(1.8版本后升为16M)
  • 锁粒度太粗,MongoDB使用的是一把全局的读写锁,详见这里
  • 不支持join操作和事务机制,这个确实是非MongoDB要做的领域
  • 对内存要求比较大,至少要保证热数据(索引,数据及系统其它开销)都能装进内存
  • 用户权限方面比较弱,这一点MongoDB官方推荐的是将机器部署在安全的内网环境中,尽量不要用权限,详见这里
  • MapReduce在单个实例上无法并行,只有采用Auto-Sharding才能并行。这是由JS引擎的限制造成的
  • MapReduce的结果无法写入到一个被Sharding的Collection中,2.0版本对这个问题的解决好像也不彻底
  • 对于数组型的数据操作不够丰富
  • Auto-Sharding还存在很多问题,所谓的水平扩展也不是那么理想

适用范围

u  适合实时的插入,更新与查询,并具备应用程序实时数据存储所需的复制及高度伸缩性。

u  适合作为信息基础设施的持久化缓存层。

u  适合由数十或数百台服务器组成的数据库。因为Mongo已经包含对MapReduce引擎的内置支持。

u  Mongo的BSON数据格式非常适合文档化格式的存储及查询。

网站数据:Mongo非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。

u  ◆缓存:由于性能很高,Mongo也适合作为信息基础设施的缓存层。在系统重启之后,由Mongo搭建的持久化缓存层可以避免下层的数据源过载。

u  ◆大尺寸,低价值的数据:使用传统的关系型数据库存储一些数据时可能会比较昂贵,在此之前,很多时候程序员往往会选择传统的文件进行存储。

u  ◆高伸缩性的场景:Mongo非常适合由数十或数百台服务器组成的数据库。Mongo的路线图中已经包含对MapReduce引擎的内置支持。

u  ◆用于对象及JSON数据的存储:Mongo的BSON数据格式非常适合文档化格式的存储及查询

 

 

MongoDB的不适用范围

·        高度事务性的系统。

·        传统的商业智能应用。

·        级为复杂的SQL查询。

·        ◆高度事务性的系统:例如银行或会计系统。传统的关系型数据库目前还是更适用于需要大量原子性复杂事务的应用程序。

·        ◆传统的商业智能应用:针对特定问题的BI数据库会对产生高度优化的查询方式。对于此类应用,数据仓库可能是更合适的选择。

·        ◆需要SQL的问题

 

u   

要点

跟mysqld一样,一个mongod服务可以有建立多个数据库,每个数据库可以有多张表,这里的表名叫collection,每个collection可以存放多个文档(document),每个文档都以BSON(binary json)的形式存放于硬盘中。跟关系型数据库不一样的地方是,它是的以单文档为单位存储的,你可以任意给一个或一批文档新增或删除字段,而不会对其它文档造成影响,这就是所谓的schema-free,这也是文档型数据库最主要的优点。跟一般的key-value数据库不一样的是,它的value中存储了结构信息,所以你又可以像关系型数据库那样对某些域进行读写、统计等操作。可以说是兼备了key-value数据库的方便高效与关系型数据库的强大功能。

索引

跟关系型数据库类似,mongodb可以对某个字段建立索引,可以建立组合索引、唯一索引,也可以删除索引。当然建立索引就意味着增加空间开销,我的建议是,如果你能把一个文档作为一个对象的来考虑,在线上应用中,你通常只要对对象ID建立一个索引即可,根据ID取出对象某些数据放在memcache即可。如果是后台的分析需要,响应要求不高,查询非索引的字段即便直接扫表也费不了太多时间。如果还受不了,就再建一个索引得了。

默认情况下每个表都会有一个唯一索引:_id,如果插入数据时没有指定_id,服务会自动生成一个_id,为了充分利用已有索引,减少空间开销,最好是自己指定一个unique的key为_id,通常用对象的ID比较合适,比如商品的ID。

capped collection

capped collection是一种特殊的表,它的建表命令为:

db.createCollection("mycoll",{capped:true, size:100000})

允许在建表之初就指定一定的空间大小,接下来的插入操作会不断地按顺序APPEND数据在这个预分配好空间的文件中,如果已经超出空间大小,则回到文件头覆盖原来的数据继续插入。这种结构保证了插入和查询的高效性,它不允许删除单个记录,更新的也有限制:不能超过原有记录的大小。这种表效率很高,它适用于一些暂时保存数据的场合,比如网站中登录用户的session信息,又比如一些程序的监控日志,都是属于过了一定的时间就可以被覆盖的数据。

复制与分片

mongodb的复制架构跟mysql也很类似,除了包括master-slave构型和master-master构型之外,还有一个Replica pairs构型,这种构型在平常可以像master-slave那样工作,一但master出现问题,应用会自动了连接slave。要做复制也很简单,我自己使用过master-slave构型,只要在某一个服务启动时加上–master参数,而另一个服务加上–slave与–source参数,即可实现同步。

分片是个很头疼的问题,数据量大了肯定要分片,mysql下的分片正是成为无数DBA的噩梦。在mongodb下,文档数据库类似key-value数据库那样的易分布特性就显现出来了,无论构造分片服务,新增节点还是删除节点都非常容易实现。但mongodb在这方面做还不足够成熟,现在分片的工作还只做到alpha2版本(mongodb v1.1),估计还有很多问题要解决,所以只能期待,就不多说了。

性能

在我的使用场合下,千万级别的文档对象,近10G的数据,对有索引的ID的查询不会比mysql慢,而对非索引字段的查询,则是全面胜出。mysql实际无法胜任大数据量下任意字段的查询,而mongodb的查询性能实在让我惊讶。写入性能同样很令人满意,同样写入百万级别的数据,mongodb比我以前试用过的couchdb要快得多,基本10分钟以下可以解决。补上一句,观察过程中mongodb都远算不上是CPU杀手。

GridFS

gridfs是mongodb一个很有趣的类似文件系统的东西,它可以用一大块文件空间来存放大量的小文件,这个对于存储web2.0网站中常见的大量小文件(如大量的用户头像)特别有效。使用起来也很方便,基本上跟一般的文件系统类似。

用合适的数据库做适合的事情

mongodb的文档里提到的user case包括实时分析、logging、全文搜索,国内也有人使用mongodb来存储分析网站日志,但我认为mongodb用来处理有一定规模的网站日志其实并不合适,最主要的就是它占空间过于虚高,原来1G的日志数据它可以存成几个G,如此下去,一个硬盘也存不了几天的日志。另一方面,数据量大了肯定要考虑sharding,而mongodb的sharding到现在为止仍不太成熟。由于日志的不可更新性的,往往只需APPEND即可,又因为对日志的操作往往只集中于一两列,所以最合适作为日志分析的还是列存储型的数据库,特别是像infobright那样的为数据仓库而设计的列存储数据库。

由于mongodb不支持事务操作,所以事务要求严格的系统(如果银行系统)肯定不能用它。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值