Manual阅读笔记,MongoDB性能优化

MongoDB是一个高性能可扩展基于文档的NoSQL数据库,高性能也需要在多个关键维度的配置,包括硬件、应用模式、模式设计、索引、磁盘I/O等。

9. 存储

9. 存储…
1

9.1 存储引擎…
1

9.1.1 WiredTiger存储引擎…
1

9.1.1.1 文档级别并发…
1

9.1.1.2 快照和检查点…
2

9.1.1.3 Journaling.
2

9.1.1.4 压缩…
2

9.1.1.5 内存使用…
3

9.1.2 MMAPv1存储引擎…
4

9.1.3 In-Memory存储引擎…
4

9.2 Journaling.
4

9.2.1 Journaling和WiredTiger引擎…
4

9.2.2 journal和MMAPv1引擎…
5

9.2.3 Journal和in-memory存储引擎…
6

 

存储引擎

9.1 存储引擎

存储引擎是数据库的一个组件用于管理数据存储。Mongodb支持多存储引擎,不同的存储引擎对于指定负荷可能会有更好的性能。

WiredTiger在Mongodb
3.2之后作为默认的存储引擎,能够很好的适应很多负荷。WiredTiger提供文档级别的并发模型,检查点,压缩,和其他特性。

MMAPv1是Mongodb老的存储引擎在mongodb 3.2之前是默认的存储引擎。

In-Memory Storage Engine在Mongodb 企业版可用。和其他存储引擎不同数据是存放在内存减少数据延迟。

WiredTiger是3.0以后的默认存储引擎,细粒度的并发控制和数据压缩提供了更高的性能和存储效率。3.0以前默认的MMAPv1也提高了性能。在MongoDB复制集中可以组合多钟存储引擎,各个实例实现不同的应用需求。

9.1.1 WiredTiger存储引擎

从3.0开始已经在64位系统上可用。

在3.2版本中WiredTiger存储引擎为默认存储引擎。如果没有指定–storageEngine或者storage.engine会自动决定存储引擎的使用并且存放在storage.dbPath下。

硬件

9.1.1.1 文档级别并发

WiredTiger使用文档级别并发控制写入操作。这样多个客户端可以同时对不同的文档进行修改。对于很多读写操作WiredTiger使用最优的并发控制。WiredTiger在全局,数据库,collection上使用意向锁。当存储引擎发现2个冲突操作,一个操作会出现写入重提,需要显示的重试这个操作。

一些全局操作,通常执行时间短的涉及到多个数据库的,还是会使用全局锁。对于drop
collection还是会有数据库x锁。

MongoDB初衷是采用水平扩展构建集群,而不是价格更高的硬件升级。采用复制保证高可用,自动分片使数据均匀分布在各节点服务器上;in-memory计算提供更高的计算性能,该引擎只有企业版有

9.1.1.2 快照和检查点

WiredTiger使用多版本并发控制(MVCC),在操作之前,WiredTiger提供一个数据快照。快照表示内存数据库的窗口。

当写入磁盘,WiredTiger写入所有的快照中的数据到磁盘。新的固化数据被当做数据文件的checkpoint。

MongoDB在每60秒或者2GB journal数据创建checkpoint。

当在创建新的检查点,老的检查点还是可用。如果MongDB在写入checkpoint的时候发生错误或者中断,MongoDB可以使用最后一次有效的checkpoint。

当WiredTiger元数据表自动更新引用新的检查点,新的检查点变成可用,并且持久。一旦新的checkpoint可用,老的checkpoint就被释放。

如果没有开启journaling,mongodb会从最后一次checkpoint恢复,checkpoint之后的恢复需要用到journaling。

硬件配置要求

9.1.1.3 Journaling

WiredTiger使用日志先行配合checkpoint来保证数据持久性。WiredTiger journal记录了checkpoint之间的数据修改。如果MongoDB在2次checkpoint之间退出,mongodb会使用journal来重播最后一次checkpoint之后所有的修改。

WiredTiger使用snappy压缩journal。可以指定其他的压缩方法,或者不压缩,可以使用storage.wiredTiger.engineConfig.journalCompressor来设置。

 

Note:WiredTiger最小的日志记录是128 bytes,如果小于128 bytes,那么就不会压缩记录

 

也可以直接关闭journal减少对journal的维护,storage.journal.enabled=false。如果是单实例,不使用journal就意味着会丢失修改的数据,特别是mongodb在checkpoint之间不可预见的退出。对于复制集,复制进程可以满足持久性要求。

 

  • 确保内存设置能满足性能需求:确保内存>索引容量+高频访问数据容量,通过storage.wiredTiger.engineConfig.cacheSizeGB或–wiredTigerCacheSizeGB进行设置。通过监控db.serverStatus()命令查看并评估
  • 写操作负载高的应用采用SSD:SATA接口的SSD提供强大的顺序写性能,能提高MongoDB数据和jounal日志文件的写入速度;同时最为强大的是随机读取性能,这点符合MongoDB的数据访问vi
    模式。部署采用RAID-10,比RAID-5/RAID-6减少很多限制,并提供更高性能。
  • 存储引擎配置数据压缩和I/O-intensive
    worloads:WiredTiger存储引擎支持本地压缩,能够更好的使用多核新处理器资源。
  • 为每个MongoDB服务提供专用服务器:在单一服务器上安装多个MongoDB实例,系统会计算每个实例合适的缓存,并为每个实例分配默认的cache_size。通过storage.wiredTiger.engineConfig.cacheSizeGB参数调整。
  • 使用多路查询路由:在不同服务器上使用多个mongos,最好将mongos部署在应用服务器上。
  • 利用多核心:WiredTiger存储引擎是多线程且可以充分利用CPU多核心。MMAPv1存储引擎因为其并发模型,所以并不要求更多的CPU核心。
  • 开启NTP时间同步服务
  • 禁用NUMA:MongoDB运行在NUMA系统上会导致操作问题,禁用NUMA需要配置mermory
    interleave policy

9.1.1.4 压缩

使用WiredTiger,MongoDB支持对所有collection和index 进行压缩。压缩可以减少存储的使用但是会花费一点cpu。

默认,WiredTiger使用块压缩,使用snappy压缩方式压缩所有collection,对所有索引使用前缀压缩。

对于collection,块压缩也可以使用zlib。

设置storage.wiredTiger.collectionConfig.
blockCompressor。

对于index,取消前缀压缩,storage.wiredTiger.indexConfig.prefixCompression

压缩设置可以在每个collection或者索引上在创建时设置。对于很多负荷默认压缩设置平衡了存储效率和处理要求。

编写脚本/etc/init.d/pro-startMongo

9.1.1.5 内存使用

使用WiredTiger,MongoDB利用了WiredTiger的cache和文件系统的cache。在3.2之后,WiredTiger
cache默认为一下2个大的一个:

1.60%的内存减去1GB

2.1GB

对于大于10GB的内存,新的默认设置会比3.0占用更多内存。对于文件系统缓存,MongoDB自动使用空闲的内存。数据在文件系统cache是被压缩的。

可以通过配置storage.wiredTiger.engineConfig.cacheSizeGB来调整WiredTiger内存大小。

单机修改存储引擎到WiredTiger

考虑:修改存储引擎使用mongodump和mongorestore工具来导入导出数据。保证这些工具已经安装了。另外保证有足够的空间用于导入导出。为了可以使用WiredTiger必须使用3.0以上版本的MongoDB。

过程

1.启动mongod,如果已经启动跳过

2.使用mongodump导出

3.创建一个数据库目录用于新版本的mongodb运行

4.启动带WiredTiger的MongoDB

5.使用mongorestore来恢复数据。

 

修改复制集到WiredTiger

考虑:对于复制集有成员使用不同的存储引擎。那么你可以以滚动的方式逐个修改到WiredTiger。在全部修改到WiredTiger之前,最好使用混合的存储引擎方式运行一段时间。

过程

1.关闭该secondary成员,db.shutdownServer()

2.准备一个有WiredTiger的MongoDB创建一个数据库目录

3.启动带WiredTiger的MongoDB,因为没有数据,mongod会执行初始化同步,初始化时间取决于数据库的大小和网络连接。

4.重复以上过程,修改所有secondary

 

修改shard集群的存储引擎到WiredTiger

考虑:这个过程可能会有下线时间,特别是一个或者多个shard是单机的。如果修改shard的host和端口,就必须去修改shard配置。

过程

1.停用均衡器

2.关闭第三个config
server保证元数据是只读的。

3.导出第二个config
server的数据

4.对于第二个config服务,创建一个新的数据目录,给wiredtiger使用

5.停止第二个config服务

6.启动带wiredtiger的mongodb启动在第二个config服务上。

7.导入之前导出的数据

8.关闭第二个config服务,保证元数据只读

9.启动第三个config
server服务,并按3-7步骤执行

10.对于第一个config
server,并按3-7步骤执行

11.启动第二个config
server

12.启动均衡器

9.1.2 MMAPv1存储引擎

具体看:

# disable NUMA

9.1.3 In-Memory存储引擎

具体看:

sysctl -w vm.zone_reclaim_mode=0

9.2 Journaling

启动时必须指定numactl方式

9.2.1 Journaling和WiredTiger引擎

WiredTiger使用checkpoint来提供内存和磁盘上的一致性数据窗口,允许MongoDB从最近的checkpoint恢复。如果mongodb出现异常退出,那么journaling会被用来恢复数据。

journal恢复数据过程:

1.查看数据文件找到最后一次checkpoint标示

2.在journal文件中查找匹配最后一次checkpoint

3.从最后一次checkpoint开始应用journal文件

 

journal过程

当启动Journal,WiredTiger为每个客户端的写入记录一条日志。内部修改操作也会记录日志。比如update了一个文档可能会导致索引修改,WiredTiger会创建一个日志记录其中包括文档和相关索引的修改。

MongoDB为WiredTiger创建了journal记录的cache。线程协调的分配和复制自己部分的buffer。所有journal记录最多128KB的buffer。

在以下状况下WiredTiger同步journal buffer:

1.每50毫秒

2.MongoDB发生checkpoint,checkpoint每60秒或者journal数据达到2GB。

3.如果写入操作敏感为j:true,WiredTiger强制同步journal文件

4.因为mongodb使用journal文件最多为100MB,当WiredTiger创建新文件时,同步之前文件的缓存。

 

Journal文件

对于journal文件,MongoDB创建一个子目录journal在dbpath下面。WiredTiger
journal文件被命令为WiredTigerLog.<sequence>,<sequence>会用0填充。

每个写入操作有一个记录,每个记录包含唯一的识别符。

MongoDB使用snappy压缩journal数据。

最小的WiredTiger的日志为128字节,如果小于就不再压缩。

WiredTiger的journal文件最大限制为100MB,一旦超过会创建一个新的journal文件。

WiredTiger会自动删除来的journal文件,只维护最近checkpoint以来的journal文件。

WiredTiger会预分配journal文件。

numactl –interleave=all <path> <options>

9.2.2 journal和MMAPv1引擎

使用MMAPv1,当写操作发生,MongoDB更新内存窗口。如果启动了journal,MongoDB会先把内存修改记录到journal文件。如果MongoDB在提交数据修改前发生中断或者出现错误,MongoDB可以使用journal文件,维护数据一致性。

 

journal过程

MongoDB存储引擎层有2个内部数据的窗口,Private view用来写入到,shared
view用来写入到数据文件。

1.MongoDB先把修改数据写入到private view

2.MongoDB然后把这些修改写入到journal文件上,大概100毫秒一次。MongoDB以组提交的方式吧这些修改写入到文件。组提交可以最小化性能影响。写入到文件炒作是原子的,保证journal文件的一致性。也可以通过storage.journal.commitIntervalMs来设置。

3.journal提交完之后,MongoDB把修改写入到shared view

4.最后,MongoDB把在shared view上的修改应用到数据文件上。默认应用间隔是60s。操作系统也可以选择比60s频率更高的,特别是可用内存低的情况下。可以通过设置参数来决定刷新频率,storage.syncPeriodSecs。

如果MongoDB
crash没有写数据文件,jrounal会以重放这些修改到shared
view最后写入到数据文件。

一旦journal操作应用到了shared
view并且刷新到了磁盘,MongoDB要求超重系统重新remap到private view,介绍内存使用。

 

Journal文件

当启动了journal,MongoDB在dbpath下创建一个子目录journal。

Journal文件包含日志先行的,每个journal项描述了data file中的修改。Journal文件是追加日志的。当journal文件有1GB的数据,MongoDB会创建一个新的journal文件。如果使用了storage.smallFiles选项那么每个文件的大小被限制为128MB。

Lsn文件包含最后一次mongodb刷新到数据文件的时间。

一旦mongodb应用了所有某个journal文件到数据文件,那么这个文件就可以被重用。

除非你每秒写入一些字节,journal目录只要2,3个journal文件。

干净关闭删除所有journal目录的文件。Crash的话journal目录就有文件,当下次启动的时候可以自动恢复。

 

Journal目录

为了保证书序写入的,最好能够把数据文件和journal目录放在不同的文件系统下。

 

预分配

mongodb可以预分配journal文件,特别是当mongod进程觉得预分配会比创建新的个更高效的时候。

根据文件系统,可以在第一次启动mongod,启用了journal的时候显示的预分配。预分配可能比较花时间。

  • 禁用THP:Transparent Huge
    Pages(THP)是Linux内存管理策略会占用大量的内存

9.2.3 Journal和in-memory存储引擎

具体看:

 

编写脚本/etc/init.d/pro-startMongoDB,加入了前面禁用NUMA部分

# disable transparent hugespages
case $1 in
  start)
    if [ -d /sys/kernel/mm/transparent_hugepage ]; then
      thp_path=/sys/kernel/mm/transparent_hugepage
    elif [ -d /sys/kernel/mm/redhat_transparent_hugepage ]; then
      thp_path=/sys/kernel/mm/redhat_transparent_hugepage
    else
      return 0
    fi

    echo 'never' > ${thp_path}/enabled
    echo 'never' > ${thp_path}/defrag

    re='^[0-1]+$'
    if [[ $(cat ${thp_path}/khugepaged/defrag) =~ $re ]]
    then
      # RHEL 7
      echo 0  > ${thp_path}/khugepaged/defrag
    else
      # RHEL 6
      echo 'no' > ${thp_path}/khugepaged/defrag
    fi

    unset re
    unset thp_path

        # disable NUMA
        sysctl -w vm.zone_reclaim_mode=0
    ;;
esac

更改脚本权限并加入自启动

sudo chmod 755 /etc/init.d/pro-startMongo

sudo chkconfig –add pro-startMongo

 

通过以下测试是否关闭了THP

cat /sys/kernel/mm/transparent_hugepage/enabled

cat /sys/kernel/mm/transparent_hugepage/defrag

如下显示表示成功

always madvise [never]

  • 设置ulimit:默认的ulimit设置太小,以下是推荐设置

-f (file size): unlimited

-t (cpu time): unlimited

-v (virtual memory): unlimited

-n (open files): 64000

-m (memory size): unlimited

-u (processes/threads): 64000

我是新建/etc/security/limits.d/99-mongodb-nproc.conf文件,加入如下配置:

mongod          soft    fsize     unlimited

mongod          hard    fsize     unlimited

mongod          soft    cpu       unlimited

mongod          hard    cpu       unlimited

mongod          soft    as        unlimited

mongod          hard    as        unlimited

mongod          soft    nofile    64000

mongod          hard    nofile    64000

mongod          soft    nproc     64000

mongod          hard    nproc     64000

 

应用、模式设计

  • 模式设计:关联模式和嵌套模式,见下图,根据应用设计最合理的模式。

  • 文档的key命名尽量短,比如ParkingId可以改为pid。超过16MB,大文档尽量使用GridFS
  • 在集群环境下,避免scatter-gather查询。其实这依赖于sharding的方式和sharding
    key的选择,要尽量满足大部分的业务需求。
  • 读写分离设计:在读延迟允许范围内,进行读写分离是个不错的选择,可以大大降低主节点压力。
  • 类似关系型数据库,一些优化建议:只查询和更新需要的字段,减少带宽;避免使用否定条件查询;合理使用覆盖索引并考虑最左前缀匹配原则;

硬盘I/O

  • 预读设置:使用blockdev –setra
    <value>命令设置预读,单位是512B(扇区大小),不少于32kb,也不要设置太大,浪费I/O,设置大的预读有助于顺序读,具体的配置根据业务需求设定。
  • 使用EXT4或者XFS文件系统。不要使用EXT3哦。
  • 禁止Access
    Time设置。操作系统会维护文件最后的访问时间metadata,对于数据库意味着每次文件系统每访问一个页就会提交一个写操作,这将降低整个数据库的性能。

编辑/etc/fstab文件,指定noatime和nodiratime

/dev/sdb                /data                   ext4   
defaults,noatime,nodiratime        1 2

  • 禁用Huges
    Pages虚拟内存页,MongoDB使用普通虚拟内存页性能更好,这个前面提到过。
  • 使用RAID10,性能高于RAID-5或RAID-6
  • swap设置:设置充足的swap空间防止OOM程序杀掉mongod进程,对于Linux下的MMAPv1存储引擎,不会使用swap空间,可以少量设置swap空间;Windows下的MMAPv1存储引擎需要设置swap空间;WiredTiger存储引擎,需要设置较大的swap空间。使用WiredTiger测试过程中,发现对于大查询,没有足够的swap空间甚至会报错。在/etc/sysctl.conf设置vm.swappiness为0,尽量使用内存。swap分区尽可能大些,以下是一些建议。测试过程中是128G内存,我设置交换分区内存大小也是128G。

图片 1.png)

  • 日志和数据文件分开存储,日志存储在普通SAS盘上,数据存储在SSD上。数据库的读一般都是随机读,SSD在随机读性能上表现非常好;日志文件主要是顺序写,速度本身较高,而SSD在顺序读写上的表现非常糟糕,而且比普通SATA性能还要低。Mongodb的日志有journal和syslog,journal日志只能放在dbPath下面的journal目录中,而系统日志可以指定日志路径。journal可以挂载个SATA硬盘设备。这样做到日志和数据分离。
  • 多路径设置:设置storage.wiredTiger.engineConfig.directoryForIndexes使索引和数据分离,设置该参数为true后,会在dbPath下面建立一个index目录,在index目录下挂载一个硬盘设备,使索引和数据做到分离。设置directoryPerDB每个数据库不同目录,对于每个目录最好挂载到不同的硬盘设备上。
  • 压缩:WiredTiger提供snappy和zlib两种数据压缩方式。snappy提供压缩比低但是性能损耗低,zlib压缩比高但性能损耗较高,通过storage.wiredTiger.collectionConfig.blockCompressor参数设置

 

 

 

 

 

 

Leave a Comment.