MongoDB性能

MongoDB性能

时间飞逝,转眼间,九月就悄然而至了。正式工作也已经满1个半月了。有收获,有成长,也有挫折,但是,我会继续努力,继续脚踏实地地做好每一件事情!


在使用MongoDB开发和操作应用时,在很多情况下,我们需要分析应用和数据库的性能。当出现性能下降时,这经常是数据库存取策略、硬件资源以及数据库连接数共同作用的结果。

一些用户可能会由于不充足或不合适的索引策略、糟糕的模式设计而遇到性能的限制。锁性能讨论了这些因素会怎么影响MongoDB的内部锁。

性能问题有可能意味着数据库的容量已经快到限制,应该要增加额外的数据库容量了。特别地,应用的工作集应该能够完全放在物理内存中。查阅内存和MMAPv1存储引擎了解更多关于工作集的信息。

在一些情况下,性能问题可能是暂时的,与不正常的流量负载相关。正如连接数这一章讨论的,拓展可以缓解过多的流量。

数据库分析器可以帮助我们理解什么操作正在造成性能的下降。

锁性能

MongoDB使用一个锁系统来保证数据集的一致性。如果一些操作运行时间比较长或者是一个队列的形式,那么由于请求和操作都在等待锁,性能将会降低。

锁相关的性能降低是间断性的。可以查看serverStatus输出结果的locksglobalLock部分,以了解锁是否影响了应用的性能。

使用locks.timeAcquiringMicros除以locks.acquireWaitCount可以获得一个特定锁模式的大致平均等待时间。

locks.deadlockCount提供了锁请求遇到死锁的次数。

如果globalLock.currentQueue.total持续很高的话,有可能许多请求都在等一个锁。这就意味着一个可能的并发问题正在影响性能。

如果与uptime相比globalLock.totalTime非常高的话,说明数据库已经在锁定状态有一段时间了。

长查询可能由索引的不高效使用、未经优化的模式设计、糟糕的查询结构、系统架构问题或者RAM不足带来的分页错误和磁盘读取等原因造成。

内存以及MMAPv1存储引擎

内存使用

使用MMAPv1存储引擎时,MongoDB使用内存映射文件存储数据。给定足够大小的数据集合,mongod进程将会将系统中所有可用的内存分配用于使用。

尽管这是特意设计的,对性能有提高,内存映射文件使得非常难以确定RAM的大小是否足够存放数据集。

serverStatus输出中的memory usage statuses指标可以提供MongoDB内存使用的信息。

mem.resident字段提供了使用中剩余内存的大小。如果该值超过了系统内存的大小并且磁盘中有大量的数据不在RAM中,那么我们需要扩大系统的容量。

我们可以检查mem.mapped来查看mongod使用的映射内存的大小。如果这个值大于系统内存的值,一些操作将会请求一个分页错误以从磁盘中读取数据。

分页错误

使用MMAPv1存储引擎时,当MongoDB从目前不在物理内存中的数据或数据文件的一部分进行读写时,可能会发生分页错误。相反,当操作系统的物理内存非常紧缺并且物理内存的分页被交换到磁盘上时,操作系统将会发生分页错误。

MongoDB在一秒钟之内会将其触发的分页错误作为分页错误的总数进行报告。可以查看serverStatus输出中的extra_info.page_faults值了解分页错误数。

MongoDB分页错误计数器的快速增长可能意味着服务器的物理内存太小。当读取大量数据集或者扫描整个集合是也有可能出现分页错误。

一个简单的分页错误会快速完成并不会造成什么问题。然而,大量累计的分页错误一般意味着MongoDB从磁盘中读取的数据太大。

MongoDB经常会在一个分页错误之后“释放”读锁,在mongod将下一页导入到内存中时允许其它京城读取。分页错误之后释放读锁提高了并发,并且提高了大容量系统中整体的吞吐。

提高MongoDB可以存取的RAM大小可以帮助降低分页错误的频率。如果不能这样做的话,我们可以考虑部署分片集群或者向部署中增加分片来分发mongod实例之间的负载。

连接数

在一些情况下,应用和数据库之间的连接数可能会超过服务器处理请求的能力。serverStatus文档中的下面字段可以提供一些信息:

  • globalLock.activeClients包含正在进行或排队的活跃操作的客户端总数计数器
  • connections是以下两个字段的容器:
    • connections.current是目前连接到数据库实例的客户端总数
    • connections.available是可以用于新客户端的未使用的连接总数

如果有许多个并发的应用请求,数据可能会无法满足需求。在这种情况下,我们需要提高部署的容量。

对于重读取的应用,增大复制集的大小然后将读操作分发到从节点。

对于重写入的应用,部署分片然后向分片集群中增加一个或多个分片以分发mongod实例之间的负载。

连接数中的峰值有可能是应用或驱动错误的结果。所有官方支持的MongoDB驱动都实现了连接池,允许客户端更高效地使用和重用连接。特别高的连接数特别是没有相应的工作负载时,该值经常是驱动或其它配置错误的结果。

除非是系统级别的限制,MongoDB对不断增加的连接没有任何限制。在基于Unix的系统中,我们可以使用ulimit命令或者通过编辑我们系统的/etc/sysctl文件修改系统限制。

数据库分析

MongoDB的“分析器”是一个数据库调优系统,可以帮助识别低效的查询和操作。可以使用以下分析等级:

级别 设置
0 关闭,没有分析
1 打开,只包含“”操作
2 打开,包含所有操作

通过在mongo shell中使用下面的命令设置profile的值来启用分析器:

db.setProfilingLevel(1)

slowOpThresholdMs设置定义了操作的组成内容。我们可以在db/setProfilingLevel()操作中传递一个参数对slowOsThreshold进行配置,从而定义分析器中认为超过该值就为“慢”操作(因此,会包含在级别1的分析数据中),

默认地,mongod记录所有的操作到日志中,其中,该操作由slowOpThreshold定义。

注意:由于数据库分析器可能会负面影响性能,在生产系统中尽可能少地在某些策略时间段内启用分析器。我们也可以在每个mongod基础上启用分析器,这个配置将不会通过复制集和分片集群进行传播。

我们可以在数据库的system.profile集合中查看分析器的输出,通过在mongo shell中使用show profile命令,或者使用下面的操作:

db.system.profile.find({millis: {$gt: 100}})

该操作将会返回所有执行时间超过100毫秒的操作。请确保这里指定的值(100,本示例)超过slowOpThresholdMs的阈值。

我们必须使用$query操作符来获取system.profile中文档的query字段。

打赏
微信扫一扫支付
微信logo微信扫一扫, 打赏作者吧~

mickey

记录生活,写给几十年后的自己。