赞
踩
转自:http://hi.baidu.com/hanxinis2/blog/item/4d67f1eed25c27a99f514642.html
MongoDB的官方文档基本是how to do的介绍,而关于how it worked却少之又少,本人也刚买了《MongoDB TheDefinitive Guide》的影印版,还没来得及看,本文原作者将其书中一些关于MongoDB内部现实方面的一些知识介绍如下,值得一看。
今天下载了《MongoDB The Definitive Guide》电子版,浏览了里面的内容,还是挺丰富的。是官网文档实际应用方面的一个补充。和官方文档类似,介绍MongoDB的内部原理是少之又少,只有在附录的一个章节中介绍了相关内容。
对于大多数的MongoDB的用户来说,MongoDB就像是一个大黑盒,但是如果你能够了解到MongoDB内部一些构造的话,将有利于你更好地理解和使用MongoDB。
在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解析,查询的优化,索引的建立等等。有兴趣的朋友可以直接参考源代码
当前架构 双服务器架构
当前架构为单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 是一个面向集合的,模式自由的文档型数据库.
面向集合, 意思是数据被分组到若干集合,这些集合称作聚集(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包含一个监视工具用于分析数据库操作的性能
查询:基于查询对象或者类SQL语句搜索文档. 查询结果可以排序,进行返回大小限制,可以跳过部分结果集,也可以返回文档的一部分.
插入和更新 : 插入新文档,更新已有文档.
索引管理 : 对文档的一个或者多个键(包括子结构)创建索引,删除索引等等
常用命令: 所有MongoDB 操作都可以通过socket传输的DB命令来执行.
本文来源于对Quora上一个问答的整理,主要列举了MongoDB身上一些局限的功能及目前做得不够好的地方。其中包括了原本就并非MongoDB想做的部分,也包括了MongoDB想做但没做好的方面。
u 适合实时的插入,更新与查询,并具备应用程序实时数据存储所需的复制及高度伸缩性。
u 适合作为信息基础设施的持久化缓存层。
u 适合由数十或数百台服务器组成的数据库。因为Mongo已经包含对MapReduce引擎的内置支持。
u Mongo的BSON数据格式非常适合文档化格式的存储及查询。
网站数据:Mongo非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。
u ◆缓存:由于性能很高,Mongo也适合作为信息基础设施的缓存层。在系统重启之后,由Mongo搭建的持久化缓存层可以避免下层的数据源过载。
u ◆大尺寸,低价值的数据:使用传统的关系型数据库存储一些数据时可能会比较昂贵,在此之前,很多时候程序员往往会选择传统的文件进行存储。
u ◆高伸缩性的场景:Mongo非常适合由数十或数百台服务器组成的数据库。Mongo的路线图中已经包含对MapReduce引擎的内置支持。
u ◆用于对象及JSON数据的存储:Mongo的BSON数据格式非常适合文档化格式的存储及查询
· 高度事务性的系统。
· 传统的商业智能应用。
· 级为复杂的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是一种特殊的表,它的建表命令为:
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是mongodb一个很有趣的类似文件系统的东西,它可以用一大块文件空间来存放大量的小文件,这个对于存储web2.0网站中常见的大量小文件(如大量的用户头像)特别有效。使用起来也很方便,基本上跟一般的文件系统类似。
mongodb的文档里提到的user case包括实时分析、logging、全文搜索,国内也有人使用mongodb来存储分析网站日志,但我认为mongodb用来处理有一定规模的网站日志其实并不合适,最主要的就是它占空间过于虚高,原来1G的日志数据它可以存成几个G,如此下去,一个硬盘也存不了几天的日志。另一方面,数据量大了肯定要考虑sharding,而mongodb的sharding到现在为止仍不太成熟。由于日志的不可更新性的,往往只需APPEND即可,又因为对日志的操作往往只集中于一两列,所以最合适作为日志分析的还是列存储型的数据库,特别是像infobright那样的为数据仓库而设计的列存储数据库。
由于mongodb不支持事务操作,所以事务要求严格的系统(如果银行系统)肯定不能用它。
一、主从配置(Master Slave)
主从数据库需要两个数据库节点即可,一主一从(并不一定非得两台独立的服务器,可使用--dbpath参数指定数据库目录)。一个从节点可以有多个主节点,这种情况下,local.sources中会有多条配置信息。一台服务器可以同时即为主也为从。如果一台从节点与主节点不同步,比如从节点的数据更新远远跟不上主节点或者从节点中断之后重启但主节点中相关的数据更新日志却不可用了。这种情况下,复制操作将会终止,需要管理者的介入,看是否默认需要重启复制操作。管理者可以使用{resync:1} 命令重启复制操作,可选命令行参数--autoresync可使从节点在不同步情况发生10秒钟之后,自动重启复制操作。如果指定了--autoresync参数,从节点在10分钟以内自动重新同步数据的操作只会执行一次。
--oplogSize命令行参数(与--master一同使用)配置用于存储给从节点可用的更新信息占用的磁盘空间(M为单位),如果不指定这个参数,默认大小为当前可用磁盘空间的5%(64位机器最小值为1G,32位机器为50M)。
二、互为主从(Replica Pairs)
数据库自动协调某个时间点上的主从关系。开始的时候,数据库会判断哪个是从哪个是主,一旦主服务器负载过高,另一台就会自动成为主服务器。
remoteserver组中的其他服务器host,可加:port指定端口。
arbiterserver 仲裁(arbiter )的host,也可指定端口。仲裁是一台mongodb服务器,用于协助判断某个时间点上的数据库主从关系。如果同组服务器在同一个交换机或相同的ec2可用区域内,就没必要使用仲裁了。如果同组服务器之间不能通信,可是使用运行在第三方机器上的仲裁,使用“抢七”方式有效地敲定主服务器,也可不使用仲裁,这样所有的服务器都假定是主服务器状态,可通过命令人工检测当前哪台数据库是主数据库:
$ ./mongo
> db.$cmd.findOne({ismaster:1});
{ "ismaster" : 0.0 , "remote" : "192.168.58.1:30001" , "ok" : 1.0 }
一致性:故障转移机制只能够保障组中的数据库上的数据的最终一致性。如果机器L是主服务器,然后挂了,那么发生在它身上的最后几秒钟的操作信息就到达不了机器R,那么机器R在机器L恢复之前是不能执行这些操作的。
安全性:同主从的操作相同。
数据库服务器替换。当一台服务器失败了,系统能自动在线恢复。但当一台机器彻底挂了,就需要替换机器,而替换机器一开始是没有数据的,怎么办?以下会解释如何替换一组服务器中的一台机器。
MongoDB语法 MySql语法
db.test.find({'name':'foobar'})<==> select * from test where name='foobar'
db.test.find() <==> select *from test
db.test.find({'ID':10}).count()<==> select count(*) from test where ID=10
db.test.find().skip(10).limit(20)<==> select * from test limit 10,20
db.test.find({'ID':{$in:[25,35,45]}})<==> select * from test where ID in (25,35,45)
db.test.find().sort({'ID':-1}) <==> select * from test order by IDdesc
db.test.distinct('name',{'ID':{$lt:20}}) <==> select distinct(name) from testwhere ID<20
db.test.group({key:{'name':true},cond:{'name':'foo'},reduce:function(obj,prev){prev.msum+=obj.marks;},initial:{msum:0}}) <==> select name,sum(marks) from testgroup by name
db.test.find('this.ID<20',{name:1}) <==> select name from test whereID<20
db.test.insert({'name':'foobar','age':25})<==>insertinto test ('name','age') values('foobar',25)
db.test.remove({}) <==> delete * from test
db.test.remove({'age':20}) <==> delete test where age=20
db.test.remove({'age':{$lt:20}}) <==> elete test where age<20
db.test.remove({'age':{$lte:20}}) <==> delete test where age<=20
db.test.remove({'age':{$gt:20}}) <==> delete test where age>20
db.test.remove({'age':{$gte:20}})<==> delete test where age>=20
db.test.remove({'age':{$ne:20}}) <==> delete test where age!=20
db.test.update({'name':'foobar'},{$set:{'age':36}})<==> update test set age=36 where name='foobar'
db.test.update({'name':'foobar'},{$inc:{'age':3}})<==> update test set age=age+3 where name='foobar'
进行了一下Mongodb亿级数据量的性能测试,分别测试如下几个项目:
(所有插入都是单线程进行,所有读取都是多线程进行)
1) 普通插入性能 (插入的数据每条大约在1KB左右)
2) 批量插入性能 (使用的是官方C#客户端的InsertBatch),这个测的是批量插入性能能有多少提高
3) 安全插入功能 (确保插入成功,使用的是SafeMode.True开关),这个测的是安全插入性能会差多少
4) 查询一个索引后的数字列,返回10条记录(也就是10KB)的性能,这个测的是索引查询的性能
5) 查询两个索引后的数字列,返回10条记录(每条记录只返回20字节左右的2个小字段)的性能,这个测的是返回小数据量以及多一个查询条件对性能的影响
6) 查询一个索引后的数字列,按照另一个索引的日期字段排序(索引建立的时候是倒序,排序也是倒序),并且Skip100条记录后返回10条记录的性能,这个测的是Skip和Order对性能的影响
7) 查询100条记录(也就是100KB)的性能(没有排序,没有条件),这个测的是大数据量的查询结果对性能的影响
8) 统计随着测试的进行,总磁盘占用,索引磁盘占用以及数据磁盘占用的数量
为MongoDB提供技术支持的10gen公司CEO凯文-赖安Dwight Merriman说:“我们公司成立于3月29日,我认为我们选择的不是一个缝隙市场,相反,我认为我们会慢慢改变企业用户市场。现在我们可以看到,MongoDB.org网站每月的下载量达到了3万次,而几个月前,下载量还为零”。
10gen公司CEO Dwight Merriman
MongoDB的名字源自一个形容词humongous(巨大无比的),在向上扩展和快速处理大数据量方面,它会损失一些精度,在旧金山举行的MondoDB大会上,Merriman说:“你不适宜用它来处理复杂的金融事务,如证券交易,数据的一致性可能无法得到保证”。
NoSQL数据库都被贴上不同用途的标签,如MongoDB和CouchDB都是面向文档的数据库,但这并不意味着它们可以象JSON(JavaScript ObjectNotation,JavaScript对象标记)那样以结构化数据形式存储文本文档。
JSON被认为是XML的代替品,它是一个轻量级的,基于文本交换数据的标准,和XML一样具有人类易读的特性。简单的JSON数据结构叫做对象,可能包括多种数据类型,如整型(int),字符串(string),数组(array),日期(date),对象(object)和字节数组(bytearray)。
面向文档的数据库与关系数据库有着显著的区别,面向文档的数据库用一个有组织的文件来存储数据,而不是用行来存储数据,在MongoDB中,一组文档被看作是一个集合,在关系数据库中,许多行的集合被看作是一张表。
但同时它们的操作又是类似的,关系数据库使用select,insert,update和delete操作表中的数据,面向文档的数据库使用query,insert,update和remove做意义相同的操作。
MongoDB中对象的最大尺寸被限制为4MB,但对象的数量不受限制,MongoDB可以通过集群加快操作的执行速度,当数据库变得越来越大时,可以向集群增加服务器解决性能问题。
Wordnik工程副总裁Tony Tam说他的公司有5百万个文档,以前保存在MySQL数据库中,大约有1.5TB,一个月前迁移到MongoDB上了,Wordnik专门收集所有单词的定义和信息,因此数据量是非常大的,迁移到MongoDB后,Tony Tam说他感到更放心。
Tam说使用MySQL数据库时,Wordnik项目一直都象是在颠簸的路上前行,数据表的冻结时间有时甚至超过了10秒,这是任何人都不能容忍的。每天会有大约200个新单词出现,我们要负责收集,并要向数据库增加1500个例子显示它们的用法,我们希望写入数据库的时间只需要1秒。Tam说:“我们不关心一致性,前后两个用户的查询结果不一定非得保持一致,我们本来就是时刻在做着更新,这一点我们无法保证”。
Wordnik系统就象是一个庞大的在线词典,有很多人同时在线查询,但同时我们也在做更新,使用MongoDB后,我们可以保持高速添加数据,不用担心数据库会出现堵塞。Tam在MondoDB大会上曾做过一个题为“Wordnik:从MySQL到MongoDB”的演讲,他说他们公司只花了一天时间就从MySQL迁移到MongoDB上了。
延伸阅读
Mongo是一个高性能,开源,无模式的文档型数据库,它在许多场景下可用于替代传统的关系型数据库或键/值存储方式。Mongo使用C++开发,提供了以下功能:
◆面向集合的存储:适合存储对象及JSON形式的数据。
◆动态查询:Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。
◆完整的索引支持:包括文档内嵌对象及数组。Mongo的查询优化器会分析查询表达式,并生成一个高效的查询计划。
◆查询监视:Mongo包含一个监视工具用于分析数据库操作的性能。
◆复制及自动故障转移:Mongo数据库支持服务器之间的数据复制,支持主-从模式及服务器之间的相互复制。复制的主要目标是提供冗余及自动故障转移。
◆高效的传统存储方式:支持二进制数据及大型对象(如照片或图片)。
◆自动分片以支持云级别的伸缩性(处于早期alpha阶段):自动分片功能支持水平的数据库集群,可动态添加额外的机器。
MongoDB的主要目标是在键/值存储方式(提供了高性能和高度伸缩性)以及传统的RDBMS系统(丰富的功能)架起一座桥梁,集两者的优势于一身。根据官方网站的描述,Mongo适合用于以下场景:
◆网站数据:Mongo非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。
◆缓存:由于性能很高,Mongo也适合作为信息基础设施的缓存层。在系统重启之后,由Mongo搭建的持久化缓存层可以避免下层的数据源过载。
◆大尺寸,低价值的数据:使用传统的关系型数据库存储一些数据时可能会比较昂贵,在此之前,很多时候程序员往往会选择传统的文件进行存储。
◆高伸缩性的场景:Mongo非常适合由数十或数百台服务器组成的数据库。Mongo的路线图中已经包含对MapReduce引擎的内置支持。
◆用于对象及JSON数据的存储:Mongo的BSON数据格式非常适合文档化格式的存储及查询。
自然,MongoDB的使用也会有一些限制,例如它不适合:
◆高度事务性的系统:例如银行或会计系统。传统的关系型数据库目前还是更适用于需要大量原子性复杂事务的应用程序。
◆传统的商业智能应用:针对特定问题的BI数据库会对产生高度优化的查询方式。对于此类应用,数据仓库可能是更合适的选择。
◆需要SQL的问题
MongoDB支持OS X、Linux及Windows等操作系统,并提供了Python,PHP,Ruby,Java及C++语言的驱动程序,社区中也提供了对Erlang及.NET等平台的驱动程序。(
SQL or NoSQL?That’s a question!SQL 与 NoSQL 的争论从来没有停息过,但其实任何一种技术都不会是适合一切应用场景的,重要的是你要充分了解自己的需求,再充分了解你要选择的技术的优劣。
下面是一个关于 MongoDB 优缺点的列表,希望对打算使用 MongoDB 的同学,能有一些作用:
优势:
快速!(当然,这和具体的应用方式有关,通常来说,它比一般的关系型数据库快5位左右。)
很高的可扩展性 – 轻轻松松就可实现PB级的存储(但是可能我们并不需要PB级的存储,10TB可能就够了)
他有一个很好的 replication 模式 (replica sets)
有很完善的Java API
他的存储格式是Json的,这对Java来说非常好处理,对javascirpt亦然。
运维起来非常方便,你不用专门为它安排一个管理员。
它有一个非常活跃的社区(我提出的一个bug在20分钟内就能得到修复。多谢Elliot)
他的版本控制非常清楚。
MongoDB 背后的公司(10gen)已经准备好了明天在 MongoDB 上面的投入的资金了。
劣势:
应用经验缺乏,我们都没有相关NoSQL 产品的使用经验。
项目相对来说还比较新。
和以往的存储相比,数据的关系性操作不再存在。
另附趣图一张:
本文详细讲下Memcached和Mongodb一些看法,以及结合应用有什么好处,希望看到大家的意见和补充。
Memcached
Memcached的优势我觉得总结下来主要体现在:
1) 分布式。可以由10台拥有4G内存的机器,构成一个40G的内存池,如果觉得还不够大可以增加机器,这样一个大的内存池,完全可以把大部分热点业务数据保存进去,由内存来阻挡大部分对数据库读的请求,对数据库释放可观的压力。
2) 单点。如果Web服务器或App服务器做负载均衡的话,在各自内存中保存的缓存可能各不相同,如果数据需要同步的话,比较麻烦(各自自己过期,还是分发数据同步?),即使数据并不需要同步,用户也可能因为数据的不一致而产生用户体验上的不友好。
3) 性能强。不用怀疑和数据库相比确实是,根源上还是内存的读写和磁盘读写效率上几个数量级的差距。有的时候我们在抱怨数据库读写太差的情况下可以看看磁盘的IO,如果确实是瓶颈的话装啥强劲的数据库估计也档不了,强不强无非是这个数据库多少充分的利用了内存。
但是也不太建议在任何情况下使用Memcached替代任何缓存:
1) 如果Value特别大,不太适合。因为在默认编译下Memcached只支持1M的Value(Key的限制到不是最大的问题)。其实从实践的角度来说也 不建议把非常大的数据保存在Memcached中,因为有序列化反序列化的过程,别小看它消耗的CPU。说到这个就要提一下,我一直觉得 Memcached适合面向输出的内容缓存,而不是面向处理的数据缓存,也就是不太适合把大块数据放进去拿出来处理之后再放进去,而是适合拿出来就直接给输出了,或是拿出来不需要处理直接用。
2) 如果不允许过期,不太适合。Memcached在默认情况下最大30天过期,而且在内存达到使用限制后它也会回收最少使用的数据。因此,如果我们要把它当 作static变量的话就要考虑到这个问题,必须有重新初始化数据的过程。其实应该这么想,既然是缓存就是拿到了存起来,如果没有必定有一个重新获取重新缓存的过程,而不是想着它永远存在。
在使用Memcached的过程中当然也会有一些问题或者说最佳实践:
1) 清除部分数据的问题。Memcached只是一个Key/Value的池,一个公共汽车谁都可以上。我觉得对于类似的公共资源,如果用的人都按照自己的规 则来的话很容易出现问题。因此,最好在Key值的规范上上使用类似命名空间的概念, 每一个用户都能很明确的知道某一块功能的Key的范围,或者说前缀。带来的好处是我们如果需要清空的话可以根据这个规范找到我们自己的一批Key然后再去 清空,而不是清空所有的。当然有人是采用版本升级的概念,老的Key就让它过去吧,到时候自然会清空,这也是一种办法。不过Key有规范总是有好处的,在 统计上也方便一点。
2) Value的组织问题。也就是说我们存的数据的粒度,比如要保存一个列表,是一个保存在一个键值还是统一保存为一个键值,这取决于业务。如果粒度很小的话最好是在获取的时候能批量获取,在保存的时候也能批量保存。对于跨网络的调用次数越少越好,可以想一下,如果一个页面需要输出100行数据,每一个数据都需要获取一次,一个页面进行上百次连接这个性能会不会成问题。
那么Memcached主要用在哪些功能上呢?
其实我觉得平时能想到在内存中做缓存的地方我们都可以考虑下是不是可以去适用分布式缓存,但是主要的用途还是用来在前端或中部挡一下读的需求来释放Web服务器App服务器以及DB的压力。
下面讲讲Mongodb。
Mongodb
Mongodb是一款比较优良的非关系型数据库的文档型的数据库。它的优势主要体现在:
1) 开源。意味着即使我们不去改也可以充分挖掘它,MS SQL除了看那些文档,谁又知道它内部如何实现。
2) 免费。意味着我们可以在大量垃圾服务器上装大量的实例,即使它性能不怎么高,也架不住非常多的点啊。
3) 性能高。其它没比较过,和MS SQL相比,同样的应用(主要是写操作)一个撑500用户就挂了,一个可以撑到2000。在数据量上到百万之后,即使没索引,MS SQL的插入性能下降的也一塌糊涂。其实任何事物都有相对性的,在变得复杂变得完善了之后会牺牲一部分的性能,MS SQL体现的是非常强的安全性数据完整性,这点是Mongodb办不到的。
4) 配置简单并且灵活。在生产环境中对数据库配置故障转移群集和读写分离的数据库复制是很常见的需求,MS SQL的配置繁琐的步骤还是很恐怖的,而Mongodb可以在五分钟之内配置自己所需要的故障转移组,读写分离更是只需要一分钟。灵活性体现在,我们可以配置一个M一个S,两个M一个S(两个M写入的数据会合并到S上供读取),一个M两个S(一个M写入的数据在两个S上有镜像),甚至是多个M多个S(理论上可以创建10个M,10个S,我们只需要通过轮询方式随便往哪个M上写,需要读的时候也可以轮训任意一个S,当然我们要知道不可能保证在同一时间所有的 S都有一致的数据)。那么也可以配置两个M的对作为一套故障转移群集,然后这样的群集配置两套,再对应两个S,也就是4个M对应2个S,保证M点具有故障 转移。
5) 使用灵活。在之前的文章中我提到甚至可以通过SQL到JS表达式的转换让Mongodb支持SQL语句的查询,不管怎么说Mongodb在查询上还是很方便的。
之前也说过了,并不是所有数据库应用都使用采用Mongodb来替代的,它的主要缺点是:
1) 开源软件的特点:更新快,应用工具不完善。由于更新快,我们的客户端需要随着它的更新来升级才能享受到一些新功能,更新快也意味着很可能在某一阶段会缺乏某个重要功能。另外我们知道MS SQL在DEV/DBA/ADM多个维度都提供了非常好的GUI工具对数据库进行维护。而Mongodb虽然提供了一些程序,但是并不是非常友好。我们的 DBA可能会很郁闷,去优化Mongodb的查询。
2) 操作事务。Mongodb不支持内建的事务(没有内建事务不意味着完全不能有事务的功能),对于某些应用也就不适合。不过对于大部分的互联网应用来说并不存在这个问题。
在使用Mongodb的过程中主要遇到下面的问题:
1) 真正的横向扩展?在使用Memcached的过程中我们已经体会到这种爽了,基本可以无限的增加机器来横向扩展,因为什么,因为我们是通过客户端来决定键值保存在那个实例上,在获取的时候也很明确它在哪个实例上,即使是一次性获取多个键值,也是同样。而对于数据库来说,我们通过各种各样的方式进行了 Sharding,不说其它的,在查询的时候我们根据一定的条件获取批量的数据,怎么样去处理?比如我们按照用户ID去分片,而查询根本不在乎用户ID, 在乎的是用户的年龄和教育程度,最后按照姓名排序,到哪里去取这些数据?不管是基于客户端还是基于服务端的Sharding都是非常难做的,并且即使有了 自动化的Sharding性能不一定能有保障。最简单的是尽量按照功能来分,再下去就是历史数据的概念,真正要做到实时数据分散在各个节点,还是很困难。
2) 多线程,多进程。在写入速度达不到预期的情况下我们多开几个线程同时写,或者多开几个Mongodb进程(同一机器),也就是多个数据库实例,然后向不同 的实例去写。这样是否能提高性能?很遗憾,非常有限,甚至可以说根本不能提高。为什么使用Memcached的时候多开线程可以提高写入速度?那是因为内 存数据交换的瓶颈我们没达到,而对于磁盘来说,IO的瓶颈每秒那么几十兆的是很容易达到的,一旦达到这个瓶颈了,无论是开多少个进程都无法提高性能了。还 好Mongodb使用内存映射,看到内存使用的多了,其实我对它的信心又多了一点(内存占用多了我觉得CPU更容易让它不闲着),怕就怕某个DB不使用什 么内存,看着IO瓶颈到了,内存和CPU还是吃不饱。
Memcached和Mongodb的配合
其实有了Memcached和Mongodb我们甚至可以让80%以上的应用摆脱传统关系型数据库。我能想到它们其实可以互相配合弥补对方的不足:
Memcached适合根据Key保存Value,那么有的时候我们并不知道需要读取哪些Key怎么办呢?我在想是不是可以把Mongodb或 说数据库当作一个原始数据,这份原始数据中分为需要查询的字段(索引字段)和普通的数据字段两部分,把大量的非查询字段保存在Memcached中,小粒 度保存,在查询的时候我们查询数据库知道要获取哪些数据,一般查询页面也就显示20-100条吧,然后一次性从Memcached中获取这些数据。也就是 说,Mongodb的读的压力主要是索引字段,而数据字段只是在缓存失效的时候才有用,使用Memcached挡住大部分实质数据的查询。反过来说,如果我们要清空Memcached中的数据也知道要清空哪些Key。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。