MongoDB 最佳实践

关于安全

为MongoDB集群启用认证鉴权

MongoDB服务器在默认安装下不启用鉴权。这意味着每一个人均可以直接链接到mongod实例并执行任意数据库操做。html

为不一样用户分配不一样的角色权限

MongoDB支持按角色定义的权限系统。你应该基于“最少权限”准则,显式的为用户分配仅须要的相应权限。mongodb

使用中央鉴权服务器

尽量使用LDAP、Kerbero之类的中央鉴权服务器,并使用强口令策略。数据库

为须要访问MongoDB的应用服务器建立白名单(防火墙配置)

若是你的服务器有多个网卡,建议只在内网的IP上监听服务。缓存

对敏感数据使用加密引擎

MongoDB企业版支持存储加密,对涉及到客户的敏感数据应该使用加密引擎来保护数据。安全

关于部署

至少使用3个数据节点的复制集

MongoDB的建议最小部署是3个数据节点构成的复制集。复制集能够提供如下优势:服务器

  • 系统99.999% 高可用
  • 自动故障切换
  • 数据冗余
  • 容灾部署
  • 读写分离

不用太早分片

分片能够用来扩展你系统的读写能力,可是分片也会带来很多新的挑战好比说管理上的复杂度,成本的增长,选择合适片键的挑战性等等。通常来讲,你应该先穷尽了其余的性能调优的选项之后才开始考虑分片,好比说,索引优化,模式优化,代码优化,硬件资源优化,IO优化等。网络

选择合适的分片数

分片的一些触发条件为:并发

  • 数据总量太大,没法在一台服务器上管理
  • 并发量过高,一台服务器没法及时处理
  • 磁盘IO压力太大
  • 单机系统内存不够大,没法装下热数据
  • 服务器网卡处理能力达到瓶颈
  • 多地部署状况下但愿支持本地化读写 取决于你分片的触发条件,你能够按照总的需求 而后除以每一台服务器的能力来肯定所需的分片数。

为每一个分片部署足够的复制集成员

分片之间的数据互相不复制。每一个分片的数据必须在分片内保证高可用。所以,对每个分片MongoDB要求至少部署3个数据节点来保证该分片在绝大部分时间都不会由于主节点宕机而形成数据不可用。异步

选择合适的片键

在分片场景下, 最重要的一个考量是选择合适的片键。选择片键须要考虑到应用的读写模式。一般来讲一个片键要么是对写操做优化,要么是对读操做优化。要根据哪一种操做更加频繁而进行相应的权衡。分布式

  • 片键值应该具备很高的基数,或者说,这个片键在集合内有不少不一样的值,例如_id就是一个基数很高的片键由于_id值不会重复
  • 片键通常不该该是持续增加的,好比说timestamp就是个持续增加的片键。此类片键容易形成热分片现象,即新的写入集中到某一个分片上
  • 好的片键应该会让查询定向到某一个(或几个)分片上从而提升查询效率。通常来讲这个意味着片键应该包括最经常使用查询用到的字段
  • 好的片键应该足够分散,让新的插入能够分布到多个分片上从而提升并发写入率。
  • 可使用几个字段的组合来组成片键,以达到几个不一样的目的(基数,分散性,及查询定向等)

关于系统

使用SSD 或RAID10 来提升存储IOPS能力

MongoDB是一个高性能高并发的数据库,其大部分的IO操做为随机更新。通常来讲本机自带的SSD是最佳的存储方案。若是使用普通的硬盘,建议使用RAID10条带化来提升IO通道的并发能力。

为Data和Journal/log使用单独的物理卷

MongoDB不少的性能瓶颈和IO相关。建议为日志盘(Journal和系统日志)单独设定一个物理卷,减小对数据盘IO的资源占用。 系统日志能够直接在命令行或者配置文件参数内指定。Journal日志不支持直接指定到另外的目录,能够经过对Journal目录建立symbol link的方式来解决。

使用XFS 文件系统

MongoDB在WiredTiger存储引擎下建议使用XFS文件系统。Ext4最为常见,可是因为ext文件系统的内部journal和WiredTiger有所冲突,因此在IO压力较大状况下表现不佳。

WiredTiger下谨慎使用超大缓存

WiredTiger 对写操做的落盘是异步发生的。默认是60秒作一次checkpoint。作checkpoint须要对内存内全部脏数据遍历以便整理而后把这些数据写入硬盘。若是缓存超大(如大于128G),那么这个checkpoint时间就须要较长时间。在checkpoint期间数据写入性能会受到影响。目前建议实际缓存设置在64GB或如下。

关闭 Transparent Huge Pages

Transparent Huge Pages (THP) 是Linux的一种内存管理优化手段,经过使用更大的内存页来减小Translation Lookaside Buffer(TLB)的额外开销。 MongoDB数据库大部分是比较分散的小量数据读写,THP对MongoDB这种工况会有负面的影响因此建议关闭。 http://docs.mongoing.com/manual-zh/tutorial/transparent-huge-pages.html

启用Log Rotation

防止MongoDB 的log文件无限增大,占用太多磁盘空间。好的实践是启用log rotation并及时清理历史日志文件。

分配足够的Oplog空间

足够的Oplog空间能够保证有足够的时间让你从头恢复一个从节点,或者对从节点执行一些比较耗时的维护操做。假设你最长的下线维护操做须要H小时,那么你的Oplog 通常至少要保证能够保存 H 2 或者 H3 小时的oplog。

关闭数据库文件的 atime

禁止系统对文件的访问时间更新会有效提升文件读取的性能。这个能够经过在 /etc/fstab 文件中增长 noatime 参数来实现。例如: /dev/xvdb /data ext4 noatime 0 0 修改完文件后从新 mount就能够: mount -o remount /data

提升默认文件描述符和进程/线程数限制

Linux默认的文件描述符数和最大进程数对于MongoDB来讲通常会过低。建议把这个数值设为64000。由于MongoDB服务器对每个数据库文件以及每个客户端链接都须要用到一个文件描述符。若是这个数字过小的话在大规模并发操做状况下可能会出错或没法响应。 你能够经过如下命令来修改这些值: ulimit -n 64000 ulimit -u 64000

禁止 NUMA

在一个使用NUMA技术的多处理器Linux 系统上,你应该禁止NUMA的使用。MongoDB在NUMA环境下运行性能有时候会可能变慢,特别是在进程负载很高的状况下。

预读值(readahead)设置

预读值是文件操做系统的一个优化手段,大体就是在程序请求读取一个页面的时候,文件系统会同时读取下面的几个页面并返回。这缘由是由于不少时候IO最费时的磁盘寻道。经过预读,系统能够提早把紧接着的数据同时返回。假设程序是在作一个连续读的操做,那么这样能够节省不少磁盘寻道时间。 MongoDB不少时候会作随机访问。对于随机访问,这个预读值应该设置的较小为好.通常来讲32是一个不错的选择。你可使用下述命令来显示当前系统的预读值: blockdev --report 要更改预读值,能够用如下命令: blockdev --setra 32

使用NTP时间服务器

在使用MongoDB复制集或者分片集群的时候,注意必定要使用NTP时间服务器。这样能够保证MongoDB集群成原则之间正确同步。

关于索引

为你的每个查询创建合适的索引

这个是针对于数据量较大好比说超过几十上百万(文档数目)数量级的集合。若是没有索引MongoDB须要把全部的Document从盘上读到内存,这会对MongoDB服务器形成较大的压力并影响到其余请求的执行。

建立合适的组合索引,不要依赖于交叉索引

若是你的查询会使用到多个字段,MongoDB有两个索引技术可使用:交叉索引和组合索引。交叉索引就是针对每一个字段单独创建一个单字段索引,而后在查询执行时候使用相应的单字段索引进行索引交叉而获得查询结果。交叉索引目前触发率较低,因此若是你有一个多字段查询的时候,建议使用组合索引可以保证索引正常的使用。 例如,若是应用须要查找全部年龄小于30岁的运动员: db.athelets.find({sport: "marathon", location: "sz", age: {$lt: 30}}}) 那么你可能须要这样的一个索引: db.athelets.ensureIndex({sport:1, location:1, age:1});

组合索引字段顺序:匹配条件在前,范围条件在后(Equality First, Range After)

以上文为例子,在建立组合索引时若是条件有匹配和范围之分,那么匹配条件(sport: “marathon”) 应该在组合索引的前面。范围条件(age: ❤️0)字段应该放在组合索引的后面。

尽量使用覆盖索引(Covered Index)

有些时候你的查询只须要返回不多甚至只是一个字段,例如,但愿查找全部虹桥机场出发的全部航班的目的地。已有的索引是: {origin: 1, dest: 1} 若是正常的查询会是这样(只须要返回目的地机场): db.flights.find({origin:"hongqiao"}, {dest:1}); 这样的查询默认会包含_id 字段,因此须要扫描匹配的文档并取回结果。相反,若是使用这个查询语句: db.flights.find({origin:"hongqiao"}, {_id:0, dest:1}); MongoDB则能够直接从索引中取得全部须要返回的值,而无需扫描实际文档(文档可能须要从硬盘里调入到内存)

建索引要在后台运行

在对一个集合建立索引时,该集合所在的数据库将不接受其余读写操做。对数据量的集合建索引,建议使用后台运行选项

程序配置

设定合适的MongoDB链接池大小 (Connections Per Host)

Java驱动的默认链接池大小是100。建议按照应用的实际状况作调整。对压力较小的应用能够适当调小减小对应用服务器的资源占用。

正确使用写关注设置(Write Concern)

MongoDB的建议最小部署是一个复制集,包含3个数据节点。默认状况下应用的写操做(更新,插入或者删除)在主节点上完成后就会当即返回。写操做则经过OPLOG方式在后台异步方式复制到其余节点。在极端状况下,这些写操做可能还未在复制到从节点的时候主节点就出现宕机。这个时候发生主备节点切换,原主节点的写操做会被回滚到文件而对应用不可见。为防止这种状况出现,MongoDB建议对重要的数据使用 {w: “marjority”} 的选项。{w: “majority”} 能够保证数据在复制到多数节点后才返回成功结果。使用该机制能够有效防止数据回滚的发生。 另外你可使用 (能够和 w:”majrotiy” 结合使用) 来指定数据必须在写入WAL日志以后才向应用返回成功确认。这个会致使写入性能有所降低,可是对于重要的数据能够考虑使用。

正确使用读选项设置(Read Preference)

MongoDB因为是一个分布式系统,一份数据会在多个节点上进行复制。从哪一个节点上读数据,要根据应用读数据的需求而定。如下是集中能够配置的读选项:

  • primary: 默认,在主节点上读数据
  • priaryPreferred: 先从主节点上读,若是为成功再到任意一台从节点上读
  • secondary: 在从节点上读数据(当有多台节点的时候,随机的使用某一台从节点)
  • secondaryPreferred: 首先从从节点上读,若是从节点因为某种缘由不能提供服务,则从主节点上进行读
  • nearest: 从距离最近的节点来读。距离由ping操做的时间来决定。 除第一个选项以外,其余读选项都存在读到的数据不是最新的可能。缘由是数据的复制是后台异步完成的。

不要实例化多个MongoClient

MongoClient是个线程安全的类,自带线程池。一般在一个JVM内不要实例化多个MongoClient实例,避免链接数过多和资源的没必要要浪费。

对写操做使用Retry机制

MongoDB使用复制集技术能够实现99.999%的高可用。当一台主节点不能写入时,系统会自动故障转移到另外一台节点。转移可能会耗时几秒钟,在这期间应用应该捕获相应的Exception并执行重试操做。重试应该有backoff机制,例如,分别在1s,2s,4s,8s等时候进行重试。

避免使用太长的字段名

MongoDB 没有表结构定义。每一个文档的结构由每一个文档内部的字段决定。全部字段名会在每一个文档内重复。使用太长的字段名字会致使对内存、网络带宽更多的需求。(因为压缩技术,长字段名对硬盘上的存储不会有太多占用)

使用投射 (projection)来减小返回的内容

MongoDB 支持相似于SQL语句里面的select,能够对返回的字段进行过滤。使用Projection能够减小返回的内容,下降网络传输的量和代码中转化成对象所需的时间。

使用TTL来自动删除过时的数据

不少时候咱们用MongoDB来存储一些时效性的数据,如7天的监控数据。与其本身写个后台脚本按期清理过时数据,你可使用TTL索引来让MongoDB自动删除过时数据: db.data.ensureIndex({create_time:1}, {expireAfterSeconds: 7*24*3600})

相关文章
相关标签/搜索