55最佳实践系列:MongoDB最佳实践

@郑昀汇总 建立日期:2012/9
 
Application Design:
1) 若是发现query没使用你预期的索引,请用hint强制使用指定索引
主站商品中心所使用的文档字段不少,各类索引建得也很多。在沙创排查慢查询时,曾百思不得其解,为何明明建的有联合索引,查询起来仍是很是慢呢,直到显式指定使用该联合索引。
hint的例子:
    db.collection.find({"age" : 18, "username" : /.*/}).hint({"username" : 1, "age" : 1})
 
2) Design documents to be self-sufficient, 设计 自给自足的文档
MongoDB 应该是一个大的、无声的数据存储(big, dumb data store)。它几乎不需作任何处理,只是负责存储和读取数据。你应该坚持这个目的,避免强迫 MongoDB 作一些客户端能够进行的计算工做。
若是真的想算一些文档里没有显式存储的数据,你有两个选择:
——招致严重的性能惩罚(让 MongoDB 用JavaScript 作运算);
——在文档中显式存储这些数据。
 
Implementation:
3) Override _id when you have your own simple, unique id
若是你的文档本身有明确的惟一键值,不须要 ObjectId 属性,那么请覆盖默认的 _id 字段,反正你也要在本身的 unique id 上建惟一索引。
 
Optimization:
4)数据量很大时,建联合索引时,不妨对比下索引升序和降序的查询效率
db.collection.ensureIndex({"store_id":- 1, "shop_id": 1})
上面的1表明ascending升序,-1表明descending降序。
单个字段建索引时,不须要考虑索引升序仍是降序,都行。
但联合索引下的排序或范围查询(包括$in, $gt, $lt 等),须要重视索引设为升序仍是降序。孙国玺认为,最好用大数据模拟一下业务场景,商户中心曾发现设-1比1快10倍以上。
 
5)如需在联合索引下排序,索引的创建方法
MongoDB 和 MySQL 都是B-Tree结构索引,因此通常来讲,联合索引均可以这么建:
查询语句是:
    db.collection.find({x : 1,y : 2}).sort({z : 1})
那么,索引能够是:
    db.collection.ensureIndex({x : 1, y : 1,z : 1})//即x+y+z
也能够是:
    db.collection.ensureIndex({y : 1, x : 1,z : 1})//即y+x+z
排序字段总在联合索引的最后。
尽可能把能过滤数据量多的字段放在前面。
但若是涉及范围查询(Range Queries),就要当心了。
查询语句是:
    db.collection.find({"country": {"$in": ["ZH", "EN"]}}).sort({"cars": 1})
那么较好的索引是:
    db.collection.ensureIndex({"cars": 1,"country": 1})
即, 在范围查询(包括$in, $gt, $lt 等)时,其实刻意在后面追加排序索引一般是没有效果的。由于在进行范围查询的过程当中,咱们获得的结果集自己并非按追加的这个字段来排的,还须要进行一次 额外的排序才行。而在这种状况下,可能反序创建索引(排序字段在前、范围查询字段在后)反而会是一个比较优的选择。固然,是否更优也和具体的数据集有关, 仍是要实测。
再举一个例子:
查询语句是:
    db.collection.find({x : 1,y : {$in:[1,2]}}).sort({z : 1})
那么较好的索引是:
    db.collection.ensureIndex({x : 1, z : 1,y : 1})//即x+z+y
 
6)养成用 explain 确认是否充分利用了索引的 习惯
请确认你的查询是否充分利用到了索引,用explain命令查看一下查询执行的状况,添加必要的索引,避免扫表操做。
 
7)查询尽可能采用分页,而且尽快释放游标
 
8) 避免使用不会命中索引的语法,如 $nin
 
Data Safety and Consistency:

9)老是使用 Replica Sets (复制群集,副本集): html

背景: Replica Sets  经过自动 failover 机制提供MongoDB的高可用性。Replica sets are a form of asynchronous  master/slave replication, adding automatic failover and automatic recovery of member nodes
应用点:在应用中,如 primary 机器出现故障,那么某一台 secondary 机器就会通 过选举成为新的 primary ,整个集群仍然可以提供正常服务。咱们的服务不会支持无同步机制的 MongoDB 部署方案。
图例:
http://images.cnblogs.com/cnblogs_com/zhengyun_ustc/255879/o_clipboard%20-011%20%E5%89%AF%E6%9C%AC.png
另,使用 Replica Sets 时,最好加1台仲裁服务器。
 
 
10) 默认开启 journaling 日志:
背景:64-bitMongoDB 1.9.2+以上默认开启  Journaling 功能。32-bit或1.9.2如下版本,则需在命令行启动时加上 --journal 。
Journaling 的出现归因于某用户在单机使用 MongoDB 时执行了 kill -9 操做,致使数据不可用后提出的。
开启该功能时,变动会先写入 Journaling 日志,按期集中提交,而后在真实数据上进行这些变动。若是服务器安全关闭,日志会被清除。在服务器启动时,若是存在 Journaling 日志,则会进行回放。这保证了那些已写入、但在服务器崩溃前尚未回放的日志能在用户链接前被执行。 
应用点: 郑昀强 烈建议你在部署时开启 Journaling 日志。注意数据文件的存放位置。在使用时,请确认你的数据文件处于一个持久化存储中(好比/data /mongodb目录)。也可使用非持久化的设备进行数据文件存储,不过你最好当心再当心,由于这可能会对你的集群架构形成影响。
热数据最好能放在内存中。可以保持热数据(以及索引数据)一直放在内存中,这一点很是重要,它将对整个集群的性能形成影响。若是经过监控发现 page fault 的数量增长,那么极可能就是热数据量超出了可用内存大小。当热数据量超出了可用内存量时,一般有两种解决方法:增长内存和数据分片。建议先增长内存,再考 虑经过数据分片的方式解决。
 
Administration:
11) 保持版本更新:
应用点:保持版本更新很重要,10gen 在每一个版本中都会修复一些问题,使 MongoDB 的运行更出色。好比在 2.0.x版本中,MongoDB 的存储性能和并发性能就有极大提升,同时还包括索引优化、Bug修复以及 compaction 命令等一系列改进。
 
12) 不要在32位系统上使用MongoDB:
背景:在32位机器上,MongoDB只能存储约2.5GB的数据。由于 MongoDB在内部实现上是经过内存映射的方式来提升性能的,因此在32位机器上其内存地址自己就限制了数据容量。
 
13) 压力过大升级服务器配置:
应用点:若是机器负载达到65%,那么应该考虑升级机器配置。在平常使用中,最好保持负载低于65%。同时这也对数据恢复和纵向扩展有影响。
 
14) 肯定热数据大小:
使用MongoDB,你最好保证你的热数据在你机器的内存大小之下,保证内存能容纳全部热数据。
 
15) 选择正确的文件系统:
MongoDB 的数据文件是采用的预分配模式,而且在 Replication 里面,Master 和 Replica Sets 的非 Arbiter 节点都是会预先建立足够的空文件用以存储操做日志。
这些文件分配操做在一些文件系统上可能会很是慢,致使进程被 Block 。因此咱们应该选择那些空间分配快速的文件系统。这里的结论是尽可能不要用ext3,用ext4或者xfs。
 
16) 选用合适的raid和磁盘:
尽可能使用raid10,避免使用raid5,经济条件容许的状况下最好使用ssd硬盘。
 
17)如何 关闭 MongoDB:
MongoDB 数据库在关闭的时候使用 kill -2 <mongo-pid>,
或者在 mongo 终端中使用  
use admin
db.shutdownServer()
 
18) 分片( sharding) 需谨慎:
应用点:分片策略会受数据访问特色的影响,因此在进行数据分片前,最好先理清楚数据的访问模式,并想明白是否确实须要分片。
因为 Shard Key 对性能的影响很是大,因此 选择一个好的 Sharding Key 是很是重要的。对于 Shard Key 的选定直接决定了集群中数据分布是否均衡、集群性能是否合理。选择 Shard Key 的一个很是重要因素是万一某一个分片完全不可访问了,受到影响的Chunk有多大(即便是用 Replica Set)。
http://images.cnblogs.com/cnblogs_com/zhengyun_ustc/255879/o_clipboard%20-%20012%E5%89%AF%E6%9C%AC.png
Config Server 对整个集群的健康运行是相当重要的,因此一旦你选择使用 分片机制,就必定要保证生产环境里有3个 Config Servers 。
永远不要删除 Config Servers 的数据,要确保频繁地对这些数据进行平常备份。若是可能,经过域名来指定节 点的地址,好比在/etc/hosts文件中指定相应的本地域名,这能让你在集群配置上更灵活。Config Servers 的压力很小,但仍是必须运行在 64-bit instances 上。
千万不要把3个 Config Servers 都放在同一个 instance 上!
 
 
参考资源:
3)部份内容来自宋涛,刘奎波和孙国玺
4)nosqlfan, mongodb资料汇总专题
赠图几枚:
70e7cafagw1dzu7kaec4fg.gif (440×570)
61d40ffatw1dlsxs3ux51j.jpg (320×240)
67efa0c0gw1dzu4ghfrl9j.jpg (440×316)