转载自:http://tech.qq.com/a/20121128/000135.htm正则表达式
近期MongoDB在Hack News上是频繁中枪。许多人更是声称恨上了MongoDB,David mytton就在他的博客中揭露了MongoDB许多现存问题。然而恨的人有之偏心的也一样不少,做为回击:Russell Smith带来了多年工做经验的总结。Russell Smith曾担任Ops和大型网站缩放顾问而且帮助过Guardian、Experian等多家公司,MongoDBLondonUserGroup的联合创始人。做为MongoDBMaster(MongoDB官方承认的MongoDB核心贡献者组织,并经过社区分享本身的专业技术),其参与工做的基础设施单服务器每秒查询超过3万次,天天活跃数据更在1TB以上。数据库
下面来看Russell对MongoDB一些常见及生僻的问题作出分析:设计模式
32位 vs 64位安全
如今大多数的服务器都对32位操做系统实现支持,更有许多新型硬件支持着容许更多RAM的64位操做系统。服务器
MongoDB也同时发布了32位及64位两个版本的数据库。归结于MongoDB使用的内存映射文件,32位版本只支持2G数据的存储。对于标准的Replica Set,MongoDB只拥有单一的处理策略 —— mongod。若是你想在将来储存2G以上的数据,请使用64位版本的MongoDB。若是拥有分片安装,那么32位版本一样可使用。网络
总结:使用64位版本或者理解32位版本的限制。数据结构
文件大小限制异步
不一样于RDBMS把数据储存在行与列中,MongoDB的数据是储存在文件中的。这些文件使用二进制存储形式,其格式为相似JSON格式的BSON格式。工具
和其它的数据库同样,单个文件的储存大小是有限制的。在旧版本的MongoDB中,单个文件都限制在4M之内。而新版本的MongoDB单文件已经支持到16M大小。这样的限制也许是使人厌烦的,可是10gen的意见是:若是这项设置不停的困扰到你,那么是否你的设计模式存在着问题;或者你可使用文件无大小限制的GridFS。性能
这种状况一般的建议是避免存储过大的文件,不按期的更新数据库中存储的各类对象。而像AmazonS3或者RackspaceCloudfiles这样的服务一般可能会是更好的选择,而非必要状况下最好别让基础设施陷入过载。
总结:把每一个文件保持在16M如下,那么一切都好。
写入失败
MongoDB在默认的状况下容许高速的写入和更新,而付出的代价就是没有明确的错误通知。默认状况下多数的驱动都在作异步、“不安全”写入 —— 这就意味着驱动程序不能当即反馈错误信息,相似于MySQL的INSERT DELAYED。若是你想知道某个事情是否成功,你必须使用getLastError手动的检查错误信息。
某些状况下若是你须要在错误发生后马上获得错误信息,即:大多数的驱动中都很容易实现同步“安全”查询。这将谋杀掉MongoDB不一样于传统数据库的优势。
若是对比“彻底安全”的同步写入你须要多一点性能,同时还想要必定程度的安全,那么你可使用getLastErrorwith‘j’让MongoDB只到一份日志提交后再发出错误报告通知。那么日志将以100毫秒一次的速度输出到磁盘,而不是60秒。
总结:若是必需要写入确认,你可使用安全写入或getLastError。
数据结构模型的弱化不等于没有数据结构模型
RDBMS通常都拥有一个预约义的数据结构模型:表格的行和列,每一个字段都拥有名称和数据类型。若是你想给其中一行加一列,那么你必须给整个表格都添加一列。
MongoDB则是移除了这个设置,对于Collection和文件没有强制的模型限定。这有益于快速开发及简易修改。
固然这不意味着你就能够无视结构模型的设计,一个合适的结构模型可让你得到MongoDB的最佳性能。赶快阅读MongoDB文档,或者观看这些结构模型设计的相关视频吧!
SchemaDesignBasics
SchemaDesignatScale
SchemaDesignPrinciplesandPractice
总结:设计结构模型并充分利用MongoDB的特点。
默认状况下修改语句修改的只是单个文件
在传统的RDBMS中除非使用LIMIT子句,修改语句做用的将是全部匹配的地方。然而MongoDB每一个查询上都默认使用等价“LIMIT 1”的设置。虽然没法作到“LIMIT 5”,可是你能够经过下面的语句整个的移除限制:
db.people.update({age: {$gt: 30}}, {$set: {past_it: true}}, false, true)
一样在官方的驱动中还有相似的选项 —— ‘multi’。
总结:能够经过指定多个文件的multi为true来完成多文件修改
查询区分大小写
字符串的查询可能不按预期的那样发展 —— 这归结于MongoDB默认区分大小写。
例如:db.people.find({name: ‘Russell’})与db.people.find({name: ‘ russell‘})是不一样的。在这里最理想的解决方案就是对须要查询数据进行确认。你也能够经过正则表达式进行查询,好比:db.people.find({name:/Russell/i}),可是这样会影响到性能。
总结:查询是区分大小写的,在牺牲速度的状况下能够利用正则表达式。
对输入的数据无容错性
当你尝试向传统数据库插入错误类型的数据,传统的数据库通常会把数据转换成预约义的类型。然而这在MongoDB中是行不通的,由于MongoDB的文件是没有预约义数据模型的。这样的话MongoDB会插入你输入的任何数据。
总结:使用准确的数据类型。
关于锁
当资源被代码的多个部分所共享时,须要确信锁必需要确保这处资源只能在一个地方被操做。
旧版本的MongoDB (pre 2.0)拥有一个全局的写入锁。这就意味贯穿整个服务器中只有一个地方作写操做。这就可能致使数据库由于某个地方锁定超负载而停滞。这个问题在2.0版本中的获得了显著的改善,而且在当前2.2版本中获得了进一步的增强。MongoDB 2.2使用数据库级别的锁在这个问题上迈进了一大步。一样值得期待的Collection级别的锁也计划在下一个版本中推出。
尽管如此,Russell仍是认为:大多数受此限制的应用程序于其说是受MongoDB影响,还不如说是程序自己的问题来的更直接。
总结:使用最新的稳定版本才能得到最高的性能。
关于包
在类Ubuntu和Debian系统上安装时,许多人都出现过“过期版本”这样的问题。解决方案很简单:使用10gen官方库,那么在Ubuntu和Debian上安装也会像在Fedora和Centos上安装同样流畅。
总结:使用拥有大多数最新版本的官方包。
使用偶数个Replica Set成员
Replica Set是增长冗余及提高MongoDB数据集群性能的有效途径。数据在全部的节点中被复制,并选出一个做为主节点。假如主节点出故障,那么会在其余的节点中票选一个做为新的主节点。
在同一个Replica Set中使用两台机器是颇有诱惑的,它比3台机器来的便宜而且也是RDBMS的标准行事风格。
可是到了MongoDB这里,同一个Replica Set中的成员数量只能是奇数个。假如你使用了偶数个成员,那么当主节点发生故障时那么其它的节点都会变成只读。发生这种状况是由于剩下待选节点的数目不知足票选主节点的规定。
若是你想节约成本,同时还但愿支持故障转移和冗余的加强,那么你可使用Arbiter。Arbiter是一种特殊的Replica Set成员,它不储存任何用户数据(这就意味着他们可使用很是小的服务器)。
总结:只可使用偶数个Replica Set成员,可是可使用Arbitter来削减成本。
没有join语句
MongoDB不支持join:若是你想在多个Collection中检索数据,那么你必须作屡次的查询。
若是你以为你手动作的查询太多了,你能够重设计你的数据模型来减小总体查询的数量。MongoDB中的文件能够是任何类型,那么能够轻易的对数据进行De-Normalize。这样就可让它始终和你的应用程序保持一致。
总结:没有join不妨看一下如何设计数据结构模型。
Journaling
MongoDB使用内存映射文件而且每60秒向磁盘输出一次通知,这就意味着最大程度上你可能丢失60秒加上向硬盘输出通知这段时间内全部的数据。
为了不数据丢失,MongoDB从2.0版本起就添加了Journaling(默认状况下开启)。Journaling把时间从60秒更改成100ms。若是数据库意外的停机,在启动以前它将会被重启用以确保数据库处于一致状态。这也是MongoDB与传统数据库最接近的地方。
固然Journaling会轻微的影响到性能,大约5%。可是对于多数人来讲额外带来的安全性确定是物有所值的。
总结:最好别关闭Journaling。
默认状况下没有身份认证
MongoDB在默认设置下并无身份验证。MongoDB会认为自身处在一个拥有防火墙的信任网络。可是这不表明它不支持身份验证,若是须要能够轻松的开启。
总结:MongoDB的安全性能够经过使用防火墙和绑定正确的接口来保证,固然也能够开启身份验证。
Replica Set中损失的数据
使用Replica Set是提升系统可靠性及易维护的有效途径。这样的话,弄清节点间故障的发生及转移机制就变得相当重要。
Replica Set中的成员通常经过oplog(记录了数据中发生增、删、改等操做的列表)来传递信息,当其中一个成员发生变化修改oplog后,其余的成员也将按照oplog来执行。若是你负责处理新数据的节点在出错后恢复运行,它将会被回滚至最后一个oplog公共点。然而在这个过程当中:丢失的“新数据”已经被MongoDB从数据库中转移并存放到你的数据目录‘rollback’里面等待被手动恢复。若是你不知道这个特性,你可能就会认为数据被弄丢了。因此每当有成员从出错中恢复过来都必需要检查这个目录。而经过MongoDB发布的标准工具来恢复这些数据是件很容易的事情。查看官方文档以了解更多相关信息。
总结:故障恢复中丢失的数据将会出如今rollback目录里面。
分片太迟
分片是把数据拆分到多台机器上,一般被用于Replica Set运行过慢时进行性能提高。MongoDB支持自动分片。然而若是你让分片进行太迟的话,问题就产生了。由于对数据的拆分和块的迁移须要时间和资源,因此若是当服务器资源基本上耗尽时极可能会致使在你最须要分片时却分不了片。
解决的方法很简单,使用一个工具对MongoDB进行监视。对你的服务器作最准确的评估,而且在占总体性能的80%前进行分片。相似的监视工具备:MMS、Munin(+MongoPlugin)和CloudWatch。
若是你肯定从一开始就要分片处理,那么更好的建议会是选用AWS或者相似的云服务进行分片。而在小型服务器上,关机或者是调整机器明显比转移成千上万条数据块来的更直接一点。
总结:尽早的分片才能有效的避免问题。
不能够更改文件中的shard key
对于分片设置,shard key是MongoDB用来识别分块对应文件的凭证。当你插入一个文件后,你就不能够对文件的shard key进行更改。而这里的解决方案是把文档删除而后从新创建,这样就容许把它指定到对应的分块了。
总结:shard key不能够修改,必要的时候能够删除文件从新创建。
不能够对256G以上的Collection进行分片
从新回到分片太迟的问题上来 —— MongoDB不容许对增加到256G以上的Collection进行分片,以前版本的设置尚未256G。这个限定在之后确定会被移除,而这里也没有更好的解决方案。只能进行重编译或者把大小控制在256G如下。
总结:在Collection达到256G之前进行分片。
惟一性索引与共享
索引的惟一性约束只能经过shard key来保证。
更多详情
选择了错误的shard key
MongDB须要你选择一个shard key来将数据分片。若是选择了错误的shard key,更改起来将是件很麻烦的事情。
点击查看如何更改
总结:选择shardkey以前先阅读这个文档。
与MongoDB通讯的未经加密
与MongoDB的链接默认状况下都是非加密的,这就意味你的数据可能被第三方记录和使用。若是你的MongoDB是在本身的非广域网下使用,那么这种状况是不可能发生的。
然而若是你是经过公网访问MongoDB的话,那么你确定会但愿你的通讯是通过加密的。公版的MongoDB是不支持SSL的。庆幸的是能够很是简单的定制本身的版本。10gen的用户则拥有特别定制的加密版本。幸运的是大部分的官方驱动都支持SSL,可是小麻烦一样是不可避免的。点击查看文档。
总结:当用公网链接时,要注意和MongoDB的通讯是未加密的。
事务
不像MySQL这些支持多行数据原子操做的传统数据库,MongoDB只支持单文件的原子性修改。解决这个问题的方法之一是在应用程序中使用异步提交的方式;另外一个是:创建一个以上的数据存储。虽然第一种方法并不适用于全部状况,可是很显然比第二个来的要好。
总结:不支持对多文件事务。
日志预分配慢
MongDB可能会告诉你已经准备就绪,但事实上它还在对日志进行分配。若是你选择了让机器自行分配,而恰巧你的文件系统和磁盘速度又很慢,那么烦恼的事情发生了。一般状况下这不会成为问题,可是一旦出现了可使用undocumentedflag–nopreallocj来关闭预分配。
总结:若是机器文件系统和磁盘过慢的话,那么日志的预分配也可能很慢。
NUMA + Linux +MongoDB
Linux、NUMA与MongoDB遇到一块儿的时候运行老是不会很好。若是你在NUMA硬件上运行MongoDB的话,这里建议是直接关掉。由于各类奇怪的问题随之而来,好比:速度会阶段性或者在CPU占用率很高的时候大幅降低。
总结:禁NUMA。
Linux里面的进程限制
若是你在MongoDB未满载的时候出过SEGMENTATION FAULT错误,你可能会发现这是由于使用了太低或者默认的打开文件或用户进程限制。10gen建议把限制设置在4K+,然而设置的大小该取决具体状况。阅读ulimit了解更多。