MongoDB 4.x——性能基准

发布时间:2026/7/23 3:00:36
MongoDB 4.x——性能基准 MongoDB 4.x性能基准1、性能基准1.1、了解基准测试1.2、吞吐量、并发数、响应时间2、WiredTiger读写模型2.1、读缓存2.2、写缓冲2.3、缓存页管理2.4、数据压缩2.5、小结3、性能监控工具3.1、mongostat3.2、mongotop3.3、Profiler模块3.4、db.currentOp4、使用YCSB测试MongoDB性能4.1、YCSB简介4.2、执行压力测试4.3、生成时序指标序列5、使用nmon监视服务器性能1、性能基准1.1、了解基准测试基准测试benchmarking是用于评估系统性能的常用手段其对系统容量规划及成本评估有着重要意义。通常基准测试需要在一种相对确定的系统配置和环境包括工作负载中展开测试在此过程中对产生的结果进行收集、分析以及评估并以此建立可度量的参考标准。基准测试方法需要满足3个基本原则。可重复性指测试过程可以反复进行对于同样过程产生的结果应该是稳定的不会受到时间、地点、执行者的影响。可度量性指测试产生的结果可以进行量化一次基准测试的结果通常会涉及多种数据指标。可对比性可基于不同的系统配置或环境条件进行多组测试比较每次测试的结果为系统调优和规划工作提供评估参考。在当前流行的Web架构中数据库一直处于非常关键的位置这与大多数Web应用属于I/O密集型应用有关。同时在许多性能调优实践中可以发现数据库往往最容易成为性能的瓶颈。由此可见提前对数据库进行基准测试这项工作就变得非常重要了。通过对业务数据库开展基准测试我们可以从以下几个方面获得受益。应用层优化对于不同的数据库设计模式schema​是选择内嵌还是拆表可能导致较大的性能差异。通过基准测试可以识别一些问题并帮助我们确定最佳模式。数据库优化对系统的某些特定配置进行调优之后或是采用不同的数据库版本、硬件环境进行对比测试以此论证系统调优所带来的成本缩减。容量规划基于业务需求包含读写比例、数据集的大小等和现有的数据库配置规格进行基准负载测试建立数据库性能基线为系统在未来的容量发展规划提供可信的依据。1.2、吞吐量、并发数、响应时间在一组数据库基准测试中我们需要关注如下3个指标。响应时间客户端从发出请求开始到返回响应所需要的时长。响应时间的指标包含平均响应时间、最短响应时间、最长响应时间以及时间百分比指标如p50/p75/p95​。吞吐量TPS/QPS​数据库每秒处理的事务数。一个事务对一次请求响应的过程。并发数同一时间点请求服务器的用户数一般是通过建立并发连接或线程进行模拟。在服务器没有达到瓶颈的情况下这3个指标的关系如下吞吐量并发数/响应时间在假定每个事务的响应时间相对稳定的情况下增加一定的并发数线程​通常可以达到更高的吞吐量。而反过来在并发数一定的条件下事务的响应时间越长吞吐量就会越小即每秒钟能完成的事务更少了。除此之外在压测过程中同样需要对系统资源用量、响应错误率等指标进行观察及收集并以此确定系统所能承受的最大压力。一般的测试方法是通过逐步加大系统的并发数观察系统性能指标的变化以及拐点的出现如图所示。上图中说明了这几项指标的一些关系。在并发数达到一定的数量后系统的吞吐量开始呈现平稳的趋势当压力持续增大并超过系统负荷之后吞吐量反而会下降此时通常也伴随着响应时间的延长而明显增大。2、WiredTiger读写模型2.1、读缓存理想情况下MongoDB可以提供近似内存式的读写性能。WiredTiger引擎实现了数据的二级缓存第一层是操作系统的页面缓存第二层则是引擎提供的内部缓存如图所示。读取数据时的流程如下数据库发起Buffer I/O读操作由操作系统将磁盘数据页加载到文件系统的页缓存区。引擎层读取页缓存区的数据进行解压后存放到内部缓存区。在内存中完成匹配查询将结果返回给应用。可以看出如果数据已经被存储在内部缓存中MongoDB则可以发挥最佳的读性能。稍差的情况是内部缓存中找不到但数据仍然被存储在操作系统的页缓存中此时需要花费一些数据解压缩的开销。直接从磁盘加载数据的性能是最差的因此MongoDB为了尽可能保证业务查询的“热数据”能快速被访问其内部缓存的默认大小达到了内存的一半该值由wiredTigerCacheSize参数指定其默认的计算公式如下wiredTigerCacheSizeMath.max((RAM-1GB),256MB)2.2、写缓冲当数据发生写入时MongoDB并不会立即持久化到磁盘上而是先在内存中记录这些变更之后通过CheckPoint机制将变化的数据写入磁盘。为什么要这么处理主要有以下两个原因。如果每次写入都触发一次磁盘I/O那么开销太大而且响应时延会比较大。多个变更的写入可以尽可能进行I/O合并降低资源负荷。所以缓冲写不失为一种好办法。但是数据一旦被延迟持久化就避不开另外一个问题可靠性。MongoDB单机下保证数据可靠性的机制包括以下两个部分。CheckPoint检查点机制快照snapshot描述了某一时刻point-in-time数据在内存中的一致性视图而这种数据的一致性是WiredTiger通过MVCC多版本并发控制实现的。当建立CheckPoint时WiredTiger会在内存中建立所有数据的一致性快照并将该快照覆盖的所有数据变化一并进行持久化fsync​。成功之后内存中数据的修改才得以真正保存。默认情况下MongoDB每60s建立一次CheckPoint在检查点写入过程中上一个检查点仍然是可用的。这样可以保证一旦出错MongoDB仍然能恢复到上一个检查点。Journal日志Journal是一种预写式日志writeahead log机制主要用来弥补CheckPoint机制的不足。如果开启了Journal日志那么WiredTiger会将每个写操作的redo日志写入Journal缓冲区该缓冲区会频繁地将日志持久化到磁盘上。默认情况下Journal缓冲区每100ms执行一次持久化。此外Journal日志达到100MB或是应用程序指定journaltrue写操作都会触发日志的持久化。一旦MongoDB发生宕机重启程序时会先恢复到上一个检查点然后根据Journal日志恢复增量的变化。由于Journal日志持久化的间隔非常短数据能得到更高的保障如果按照当前版本的默认配置则其在断电情况下最多会丢失100ms的写入数据。结合CheckPoint和Journal日志数据写入的内部流程如图所示。用文字描述为应用向MongoDB写入数据插入、修改或删除​。数据库从内部缓存中获取当前记录所在的页块如果不存在则会从磁盘中加载BufferI/O​。WiredTiger开始执行写事务修改的数据写入 页块的一个更新记录表此时原来的记录仍然保持不变。如果开启了Journal日志则在写数据的同时会写入一条Journal日志Redo Log​。该日志在最长不超过100ms之后写入磁盘。数据库每隔60s执行一次CheckPoint操作此时内存中的修改会真正刷入磁盘。Journal日志的刷新周期可以通过参数storage.journal.commitIntervalMs指定MongoDB 3.4及以下版本的默认值是50ms而3.6版本之后调整到了100ms。由于Journal日志采用的是顺序I/O写操作频繁地写入对磁盘的影响并不是很大。CheckPoint的刷新周期可以调整storage.syncPeriodSecs参数默认值60s​在MongoDB 3.4及以下版本中当Journal日志达到2GB时同样会触发CheckPoint行为。如果应用存在大量随机写入则CheckPoint可能会造成磁盘I/O的抖动。在磁盘性能不足的情况下问题会更加显著此时适当缩短CheckPoint周期可以让写入平滑一些。2.3、缓存页管理需要明确的是WiredTiger仍然使用Page页作为数据存取的单元。其中内存和磁盘中的页结构是不同的Block Manager被用于处理这些差异。页块在内存中以类B树的结构进行组织中间节点用于存放key而叶子节点则存放key和value如图所示。与传统B树结构稍微不同的是叶子节点LeafPage通过父级指针Parent Pointer来实现范围遍历操作避免并发写产生DeadLock​。当读取数据时会先通过B树索引找到对应的叶节点页面而在页内则使用二分查找来查找记录。当叶节点产生数据写入时这些更新记录会被写入节点的一块独立区域此时该节点被标记为脏页如图所示。其中Inserts和Updates是单独的跳跃表skiplist结构分别存放插入和修改操作这里的删除操作也被认为是一种修改状态变更为删除​。所以如果存在修改则读取时还会从跳跃表中做合并查找。到了前面所提到的CheckPoint时刻引擎会通过BlockManager发起Reconciliation过程Reconciliation用于将内存页转换为磁盘页上的格式​。此时CheckPoint线程会遍历内存中的全部页并找到所有的脏页进行持久 化为了不阻塞读使用的是Copy-On-Write方式如图所示。可以看到对于脏页的处理并不是就地更新而是为需要变更的页块生成新的节点包括其父级节点​而且每次都会产生一个新的根节点Root Page​。当持久化工作完成后由这个新的根节点接管操作淘汰不用的节点。在整个Reconciliation过程中BlockManager需要将内存中的页块转换为磁盘上的页而内存页要比磁盘上的页大一些具体如下。memory_page_max内部缓存页大小的最大值默认是5MB。internal_page_max磁盘中间页大小的最大值默认是4KB。leaf_page_max磁盘叶节点页大小的最大值默认是32KB。allocation_size磁盘文件的存储单元默认是4KB, internal_page_max、leaf_page_max必须是它的整数倍。其中memory_page_max的取值会影响写入延迟。这个值如果太小则会导致频繁地分裂和淘汰阻塞写入​如果太大则会导致每次产生阻塞的时间变长。internal_page_max存储的是B树的索引因此它会影响树的深度。除此之外在需要大量扫描磁盘记录的场景中leaf_page_max需要加大可减少I/O次数而在特别关注读写时延的场景中则需要适当减小。allocation_size则需要与操作系统的页缓存大小对齐以达到最好的效率。实际上除了CheckPoint一些其他条件也会触发Reconciliation例如缓存中的页超过了最大值存在大量的修改​产生分裂此时会触发evict命令并持久化。缓存中的脏数据比例达到了一定阈值触发缓存淘汰evict​。缓存淘汰策略WiredTiger基于LRU算法来实现缓存的淘汰常态下会由后台的evict线程来负责淘汰页面。如果内存非常紧张那么用户线程也会加入缓存淘汰的工作中此时表现出读写请求有一定阻塞。目前有4个可配置的参数来控制WiredTiger存储引擎的eviction策略见下表。2.4、数据压缩默认情况下WiredTiger会对集合数据和索引使用压缩算法当页面被写入磁盘时执行压缩而从磁盘中读入缓存时对页面进行解压。其中对集合采用的是块压缩block compression算法默认选择Snappy​而索引则会使用前缀压缩prefix compression算法。内部缓存和磁盘中的数据有着不同的格式。磁盘中的数据和文件系统的缓存是一致的这些都是经过压缩的。文件系统缓存是操作系统层的机制这是为了减少磁盘I/O而做出的优化。集合数据在内部缓存中是未经过压缩的方便直接读写​而在磁盘和页缓存中则保持压缩的格式。索引在磁盘和页面缓存中均保持前缀压缩的形态其在内部缓存中是另外一种结构但同样利用了前缀压缩算法。此外持久化的Journal日志也会采用Snappy压缩算法。对于数据压缩算法MongoDB也提供了一些可调整的选项。storage.wiredTiger.collectionConfig.blockCompressor用于指定集合数据的压缩算法选项如下。None不启用压缩。Snappy默认的压缩算法由谷歌开源的一款强大而稳定的压缩算法最高可达30%以下的压缩比有着不错的性价比。Zlib相比Snappy来说压缩率更好但需要消耗更多的CPU。Zstd由Facebook提供的新型高速压缩算法能以较低的CPU消耗实现更高的压缩比。该算法于MongoDB 4.2版本开始支持。storage.wiredTiger.indexConfig.prefixCompression用于指定是否启用索引前缀压缩key prefixcompression​默认是true。前缀压缩对于CPU的消耗很小平均可达到近50%的压缩率。2.5、小结总体来说WiredTiger采用的大多是以空间换时间的做法。如果理解了这个思路就能明白为什么MongoDB需要占用这么多内存了。足够的缓存空间和Copy-On-Write机制让数据的读写能在内存中高效地完成。为了最大限度提升并发能力内存中采用了和磁盘文件截然不同的松散的页面结构主要还是为了实现无锁化lock-free​。那么是不是缓存越大越好呢并非如此缓存进一步加大会导致操作系统的剩余可用内存变小除了OS进程MongoDB连接线程以及一些内存排序和管理 性操作创建索引、数据备份都需要消耗额外的内存。而且内部缓存增大之后内存中允许驻留的脏数据也会更多这会导致磁盘的I/O抖动问题更加明显。因此应用时使用默认值已经足够建议只有在充分了解缓存机制并经过利弊权衡之后再考虑调整。3、性能监控工具3.1、mongostatmongostat是MongoDB自带的监控工具其可以提供数据库节点或者整个集群当前的状态视图。该功能的设计非常类似于Linux系统中的vmstat命令可以呈现出实时的状态变化。不同的是mongostat所监视的对象是数据库进程。mongostat常用于查看当前的QPS/内存使用/连接数以及多个分片的压力分布。命令如下./mongostat-h127.0.0.1--prot27017-uadmin-padmin2016--authenticationDatabaseadmin--discover-n30021.参数说明-h指定监听的主机分片集群模式下指定到一个mongos实例也可以指定单个mongod或者副本集的多个节点。--port接入的端口如果不提供则默认为27017。-u接入用户名等同于-user。-p接入密码等同于-password。--authenticationDatabase鉴权数据库。--discover启用自动发现可展示集群中所有分片节点的状态。-n 300 2表示输出300次每次间隔2s。也可以不指定“-n 300”​此时会一直保持输出。2.输出示例3.指标说明mongostat需要关注的指标主要有如下几个。插入、删除、修改、查询的速率是否产生较大波动是否超出预期。-qrqw、araw队列是否较高若长时间大于0则说明此时读写速度较慢。-conn连接数是否太多。-dirty百分比是否较高若持续高于10%则说明磁盘I/O存在瓶颈。-netIn、netOut是否超过网络带宽阈值。-repl状态是否异常如PRI、SEC、RTR为正常若出现REC等异常值则需要修复。4.使用交互式模式mongostat一般采用滚动式输出即每一个间隔后的状态数据会被追加到控制台中。从MongoDB 3.4开始增加了–interactive选项用来实现非滚动式的监视非常方便。前面的命令可以调整为如下所示mongostat采用Go语言实现其内部使用了db.serverStatus命令要求执行用户需具备clusterMonitor角色权限。3.2、mongotopmongotop命令可用于查看数据库的热点表通过观察mongotop的输出可以判定是哪些集合占用了大部分读写时间。1.命令参考./mongotop--port27017-uadmin-padmin22016--authenticationDatabaseadminmongotop与mongostat的实现原理类似同样需要clusterMonitor角色权限。默认情况下mongotop会持续地每秒输出当前的热点表如下所示2.指标说明mongotop通常需要关注的因素主要包括热点表操作耗费时长是否过高。这里的时长是在一定的时间间隔内的统计值它代表某个集合读写操作所耗费的时间总量。在业务高峰期时核心表的读写操作一般比平时高一些通过mongotop的输出可以对业务尖峰做出一些判断。是否存在非预期的热点表。一些慢操作导致的性能问题可以从mongotop的结果中体现出来。mongotop的统计周期、输出总量都是可以设定的代码如下这样就表示最多输出100次每次间隔时间为2s。3.3、Profiler模块Profiler模块可以用来记录、分析MongoDB的详细操作日志。默认情况下该功能是关闭的对某个业务库开启Profiler模块之后符合条件的慢操作日志会被写入该库的system.profile集合中。Profiler的设计很像代码的日志功能其提供了几种调试级别见下表。对当前的数据库开启Profiler模块代码如下db.setProfilingLevel(2)将level设置为2此时所有的操作会被记录下来。检查是否生效可以用db.getProfilingStatus()命令代码如下db.getProfilingStatus(){was:2,slowms:10000,sampleRate:1.0}其中slowms是慢操作的阈值单位是毫秒sampleRate表示日志随机采样的比例1.0则表示满足条件的全部输出。如果希望只记录时长超过500ms的操作则可以将level设置为1代码如下db.setProfilingLevel(1,500)还可以进一步设置随机采样的比例代码如下db.setProfilingLevel(1,{slowms:500, sampleRate:0.5})1.查看操作日志开启Profiler模块之后可以通过system.profile集合查看最近发生的操作日志代码如下这里需要关注的一些字段主要如下所示。Op操作类型描述增加、删除、修改、查询。Ns名称空间格式为{db}.{collection}。Command原始的命令文档。Cursorid游标ID。numYield yield操作数大于0表示等待锁或者是磁盘I/O操作。nreturned返回条目数。keysExamined扫描索引条目数如果比nreturned大出很多则说明查询效率不高。docsExamined扫描文档条目数如果比nreturned大出很多则说明查询效率不高。locks锁占用的情况。storage存储引擎层的执行信息。responseLength响应数据大小字节数​一次性查询太多的数据会影响性能可以使用limit、batchSize进行一些限制。millis命令执行的时长单位是毫秒。planSummary查询计划的概要如IXSCAN表示使用了索引扫描。planSummary查询计划的概要如IXSCAN表示使用了索引扫描。execStats执行过程统计信息。ts命令执行的时间点。根据这些字段可以执行一些不同维度的查询。比如查看执行时长最大的10条操作记录代码如下db.system.profile.find().limit(10).sort({millis: -1}).pretty()查看某个集合中的update操作日志代码如下db.system.profile.find({op:query, ns:mydb.foo}).pretty()2.注意事项system.profile是一个1MB的固定大小的集合随着记录日志的增多一些旧的记录会被滚动删除。在线上开启Profiler模块需要非常谨慎这是因为其对MongoDB的性能影响比较大。建议按需部分开启同时slowms的值不要设置太低。sampleRate的默认值是1.0该字段可以控制记录日志的命令数比例但只有在MongoDB 4.0版本之后才支持。Profiler模块的设置是内存级的重启服务器后会自动恢复默认状态。3.4、db.currentOpProfiler模块所记录的日志都是已经发生的事情db.currentOp命令则与此相反它可以用来查看数据库当前正在执行的一些操作。想象一下当数据库系统的CPU发生骤增时我们最想做的无非是快速找到问题的根源这时db.currentOp就派上用场了。db.currentOp读取的是当前数据库的命令快照该命令可以返回许多有用的信息比如操作的运行时长快速发现耗时漫长的低效扫描操作。执行计划信息用于判断是否命中了索引或者存在锁冲突的情况。操作ID、时间、客户端等信息方便定位出产生慢操作的源头。我们先看看currentOp的一段输出代码如下返回结果字段中包含了inprog数组其中包含运行中的操作列表。对示例操作的解读如下。从ns、op字段获知当前进行的操作正在对test.items集合执行update命令。command字段显示了其原始信息。其中command.q和command.u分别展示了update的查询条件和更新操作。“planSummary”“COLLSCAN” 说明情况并不乐观update没有利用索引而是正在全表扫描。microsecs_runningNumberLong186070表示操作运行了186ms注意这里的单位是微秒。下一步的优化方向可以是为value字段加上索引。当然如果待更新的数据集非常大一定要避免大范围的update操作通常的做法是将其切分成多个小批量的操作达到更加可控的目的。细心的你可能会注意到 “opid”4001这个字段它表示当前操作在数据库进程中的唯一编号。如果已经发现该操作正在导致数据库系统响应缓慢则可以考虑将其“杀”死代码如下db.killOp(4001)db.currentOp默认输出当前系统中全部活跃的操作由于返回的结果较多我们可以指定一些过滤条件比如下面几个。查看等待锁的增加、删除、修改、查询操作代码如下db.currentOp({waitingForLock:true,$or:[{op:{$in:[insert,update,remove]}},{query.findandmodify:{$exists:true}}]})查看执行时间超过1s的操作db.currentOp({secs_running:{$gt:1}})查看test数据库中的操作代码如下db.currentOp({ns:/^test\./})1.currentOp命令输出说明currentOp.type操作类型可以是op、idleSession、idleCursor的一种一般的操作信息以op表示。其为MongoDB 4.2版本新增功能。currentOp.host主机的名称。currentOp.desc连接描述包含connectionId。currentOp.connectionId客户端连接的标识符。currentOp.client客户端主机和端口。currentOp.appName应用名称一般是描述客户端类型。currentOp.clientMetadata关于客户端的附加信息可以包含驱动的版本。currentOp.currentOpTime操作的开始时间。MongoDB 3.6版本新增功能。currentOp.lsid会话标识符。MongoDB 3.6版本新增功能。currentOp.opid操作的标志编号。currentOp.active操作是否活跃。如果是空闲状态则为false。currentOp.secs_running操作持续时间以秒为单位​。currentOp.microsecs_running操作持续时间以微秒为单位​。currentOp.op标识操作类型的字符串。可能的值是“none” “update” “insert” “query”“command”“getmore” “remove” “killcursors”。其中command操作包括大多数命令如createIndexes和findAndModify。currentOp.ns操作目标的集合命名空间。currentOp.command操作的完整命令对象的文档。如果文档大小超过1KB则会使用一种$truncate形式表示。其中find操作的命令示例如下getMore操作的命令示例如下这里getMore的值对应了游标ID。currentOp.planSummary查询计划的概要信息。currentOp.locks当前操作持有锁的类型和模式。currentOp.waitingForLock是否正在等待锁。currentOp.numYields当前操作执行yield让步的次数。一些锁互斥或者磁盘I/O读取都会导致该值大于0。currentOp.lockStats当前操作持有锁的统计。currentOp.lockStats.acquireCount操作以指定模式获取锁的次数。currentOp.lockStats.acquireWaitCount操作获取锁等待的次数等待是因为锁处于冲突模式。acquireWaitCount小于或等于acquireCount。currentOp.lockStats.timeAcquiringMicros操作为了获取锁所花费的累积时间以微秒为单位​。timeAcquiringMicros除以acquireWaitCount可估算出平均锁等待时间。currentOp.lockStats.deadlockCount在等待锁获取时操作遇到死锁的次数。2.注意事项db.currentOp返回的是数据库命令的瞬时状态因此如果数据库压力不大则通常只会返回极少的结果。如果启用了副本集那么currentOp还会返回一些复制的内部操作针对local.oplog.rs​需要做一些筛选。db.currentOp的结果是一个BSON文档如果大小超过16MB则会被压缩。可以使用聚合操作$currentOp获得完整的结果。4、使用YCSB测试MongoDB性能4.1、YCSB简介YCSBYahooCloud Serving Benchmark是雅虎提供的一个用于云服务基准压测的框架。一开始是其内部使用的一个测试工具随着项目开源之后越来越多的特性被加入目前已经覆盖了绝大多数NoSQL数据库产品如Cassandra、MongoDB、HBase、Redis等。YCSB几乎已经成为开源中间件的基准测试必备工具许多NoSQL数据库的性能数据评测也通过该工具来生成。YCSB是Java编写的工具主要能帮助我们完成初始化一个测试数据集基于数据集完成增加、删除、修改、查询的基准性能压测。其核心实现分为两个部分DB连接层用于支持各种不同的数据库客户端并将具体数据库驱动的API转换为YCSB的API。核心负载执行层用于加载工作负载配置并执行具体的操作逻辑。工作负载工作负载workload主要用于指定读写的比例、并发线程、操作数等。下载到的YCSB程序包一般会内置几个常用的工作负载分别代表不同的压测负载类型见表。例如工作负载定义了如下配置参数说明如下。recordcount数据集的总记录数。operationcount操作总数也就是采样sample的数量。workload负载处理类默认是com.yahoo.ycsb.workloads.CoreWorkload。readallfields是否读取全部字段默认为true。readproportion读操作的比例。updateproportion更新操作的比例。scanproportion范围扫描scan操作的比例。insertproportion插入操作的比例。requestdistribution请求的分布方式决定操作选择什么样的记录。常用的请求分布方式主要有3种。均匀分布uniform​连续性随机的选择方式。齐夫分布zipfian​一种常用的离散幂律。概率分布用于表述现实世界中的长尾模型。最近分布latest​优先使用最近记录的方式。4.2、执行压力测试下面我们即将对一个部署好的MongoDB单节点进行压力测试。具体的服务器环境配置如下。CPU:Intel® Xeon® Gold.6266C3.00GHz 8coreMemory:32 GBOS:CentOS Linux release 7.6.1810 (Core)Disk:300GB SSD1.安装ycsb-mongodbYCSB全量的安装包非常庞大其中内置了大量的中间件客户端。我们只需要下载支持MongoDB的ycsb-mongodb-binding适配包即可执行ls-lh可以看到ycsb-mongodb的目录如下其中bin目录存放了启动测试的命令工作负载则用于存放压力测试的负载配置文件。YCSB是由Java语言实现的需事先确保测试环境已经安装了JRE运行环境。2.创建用户角色通过Mongo客户端接入MongoDB服务器执行下面的命令这里创建了一个YCSB数据库、一个同名的数据库用户用于性能测试。3.配置负载策略编辑workloads/mongodb.load文件内容如下说明负载中使用了1000万条记录数据测试的样本操作数为2000万次。数据库的读写比例50%读操作35%更新操作15%插入操作。threadcount表示启动的默认并发线程数为50个。mongodb.url指定了MongoDB的连接URL其中URL附带了用户名和密码。4.装载数据执行ycsb load命令进行数据装载代码如下这里的-s表示打印状态指定该选项后每隔10s控制台会输出中间的信息以方便调试。-P则指定了具体的负载文件。数据装载工作完成后日志输出如下默认情况下YCSB会向usertable集合写入1000万条记录。5.运行压测执行ycsb run命令启动压力测试代码如下这里使用-threads指定并发的线程数为32命令行参数会覆盖配置文件中的threadcount设定。整个测试过程需要耗费一些时间结束后输出的结果如下从Throughput的输出值可获知本次压力测试的平均吞吐量为28532 ops/sec。除此之外还可以看到read、insert、update几种操作的平均延迟分别如下在执行测试过程中还可以启动mongostat来同步观察MongoDB服务器的压力情况代码如下不难发现在高强度的写压力之下脏数据的比例达到了20%以上WiredTiger持续处于刷盘的状态此时磁盘会非常繁忙。Res一列表示MongoDB使用的物理内存。在我们的用例设计中1000万条数据仅占用了不到13GB的内存此时WiredTiger缓存使用率只有70%左右这也意味着几乎所有的读都是命中缓存的。通常当数据集更大时超过内存大小​MongoDB的读性能可能会产生一定程度的下降。6.多组测试对比接下来分别使用不同的线程数对MongoDB进行压测得到结果如图所示。从多组的测试结果看当线程数达到16时MongoDB服务器的吞吐量不再明显提升而随着线程数继续增大响应时延也产生了大幅度的增加。由此可以判断在并发数大约为16时系统达到最佳的性能。需要注意的是不同的测试场景、环境配置对于性能测试结果的影响是非常大的。实际上在合理使用的前提下MongoDB能表现出很高的性能。4.3、生成时序指标序列YCSB在一次压测用例过程中可以指定输出一系列时序的指标值这可以用来观察压测时详细的吞吐量、时延的变化。使用下面的命令这里使用-p定义了两个参数measurementtypetimeseries表示输出时间序列折线图的结果数据。timeseries.granularity2000表示时间序列每隔2s打一次点。最后可以使用Excel将生成的结果集数据绘制为图表如图所示。5、使用nmon监视服务器性能nmon是一款非常小巧易用的Linux工具可以对CPU、磁盘、内存等多个资源指标进行实时监控。在数据库压测过程中可以先使用nmon对服务器的资源压力情况进行收集最后将结果输出为报表作为性能分析的依据。下面介绍使用方法。1.下载nmon从nmon的主页中找到对应的程序版本进行下载即可。2.启动监控在启动MongoDB压测之前执行下面的命令启动监控程序-s 5表示每隔5s进行一次数据采集。-c 10000表示执行采集的次数这个值尽量设置得大一些保证监控到性能压测的全过程。-F result.nmon表示将结果输出到result.nmon文件。3.停止监控性能压测结束后停止监控程序代码如下4.生成图表最后得到的result.nmon是一个格式化的文本文件可以使用nmon-analyser一个Excel脚本工具打开进行分析如图所示。从YCSB压测的数据模型上看主要的压力在于高并发的写入update和insert​。服务器的监视图表也说明了这种情况CPU使用率大约在60%左右而iops则屡次接近磁盘的极限吞吐量值。与此同时MongoDB数据所在磁盘的使用率也持续在80%以上如图所示。