MongoDB 4.2——监控 MongoDB

📅 2026/7/28 17:15:02
MongoDB 4.2——监控 MongoDB
监控 MongoDB1、监控内存使用情况1.1、计算机内存简介1.2、跟踪内存使用情况1.3、跟踪缺页错误1.4、I/O等待2、计算工作集的大小3、跟踪性能情况4、跟踪剩余空间5、监控复制情况1、监控内存使用情况访问内存中的数据速度很快而访问磁盘中的数据速度会比较慢。不幸的是内存比较昂贵磁盘则相对便宜​MongoDB 通常会在使用任何其他资源之前优先使用内存。1.1、计算机内存简介计算机中一般会有容量小且访问速度快的内存以及容量大但访问速度慢的磁盘。当请求存储在磁盘上还不在内存中的数据页时系统会发生缺页错误并将该页从磁盘复制到内存中。然后就可以快速访问内存中的数据页了。如果程序没有定期使用该页的内容并且内存又被其他页占满那么旧页就会从内存中被清除只存在于磁盘上。将一个页面从磁盘复制到内存比从内存中读取这个页面花费的时间要长得多。因此MongoDB 从磁盘复制数据的次数越少越好。如果 MongoDB 可以完全在内存中运行它就能够更快地访问数据。因此MongoDB 的内存使用量是需要跟踪的最重要的统计数据之一。1.2、跟踪内存使用情况MongoDB 在 Ops Manager 中会报告 3 种“类型”的内存常驻内存、虚拟内存和映射内存。常驻内存是MongoDB 在 RAM 中显式拥有的内存。如果查询一个文档并将其加载进内存中那么这个页面就会被添加到MongoDB 的常驻内存中。MongoDB 会获得该页面的地址。这个地址不是 RAM 中页面的真实地址而是一个虚拟地址。MongoDB 可以将它传递给内核内核会查找出页面的真正位置。这样如果内核需要从内存中清除该页面那么 MongoDB 仍然可以使用该地址对其进行访问。MongoDB 会向内核请求内存然后内核会查看页面缓存如果发现页面不存在就产生缺页错误并将页面复制到内存中最后再返回给MongoDB。MongoDB 的映射内存包括 MongoDB 曾经访问过的所有数据在其中有地址的所有数据页​。它的大小通常和整个数据集的大小差不多。虚拟内存是操作系统提供的一种抽象它对软件进程隐藏了物理存储的细节。每个进程都可以看到一个连续的内存地址空间。在 Ops Manager 中MongoDB 的虚拟内存使用通常是映射内存的两倍。下图是 Ops Manager 的内存信息图其描述了MongoDB 所使用的虚拟内存、常驻内存和映射内存的大小。映射内存仅适用于使用 MMAP 存储引擎的旧4.0之前版本部署。现在 MongoDB 使用了 WiredTiger 存储引擎应该能够看到映射内存的使用量为 0。在专门用于运行 MongoDB 的机器上常驻内存应该略小于总内存大小假设工作集与内存一样大或大于内存​。常驻内存是对实际 RAM 中数据大小的统计但它本身并不能说明 MongoDB 所使用的内存大小。如果数据可以完全被放入内存中那么常驻内存应该与数据大小相当。当我们说数据在“内存中”时通常说的是在RAM 中。从图中可以看出内存指标往往相当稳定但随着数据集的增长虚拟内存顶部那条线也会随之增长。常驻内存中间那条线会增长到可用 RAM 的大小然后保持稳定。1.3、跟踪缺页错误除了每种类型内存的数量还可以通过其他统计数据来了解 MongoDB 是如何使用内存的其中一个有用的统计指标是缺页错误的数量它表示 MongoDB 所查找的数据不在 RAM 中的频率。下图展示了缺页错误随时间变化的图表。图中发生缺页错误的次数比图中少但是这个信息本身并不是很有用。如果图中的磁盘可以处理这么多的缺页错误而应用程序也可以处理磁盘查找所造成的延迟那么即使有这么多或更多的缺页错误也不会有什么特别的问题。另外如果应用程序不能处理从磁盘读取数据所造成的延迟那么只能将所有数据存放在内存中或使用 SSD。无论应用程序能否处理这些延迟当磁盘超载时缺页错误都会成为一个问题。磁盘能够处理的负载量不是线性的一旦磁盘开始超载每个操作都必须等待越来越长的时间从而引发连锁反应。通常存在一个临界点超出临界点后磁盘性能便会迅速下降。因此应该尽量避免磁盘在其最大负载下运转。应该不断跟踪缺页错误的数量。如果在缺页错误达到某一数量时应用程序运行良好那么就有了一个系统可以处理多少缺页错误的基线。如果随着缺页错误的上升性能开始下降那么就有了一个应该发出告警的阈值。通过查看 serverStatus 输出中的 “page_faults” 字段可以看到每个数据库的缺页错误统计db.adminCommand({serverStatus:1})[extra_info]{note:fields vary by platform,page_faults:50}其中 “page_faults” 表示 MongoDB 必须去磁盘读取数据的次数自启动以来​。1.4、I/O等待缺页错误通常与 CPU 空闲等待磁盘响应称为 I/O 等待的时间密切相关。一些 I/O 等待是正常的MongoDB 有时不得不去磁盘读取数据并且无法完全避免对其他操作的妨碍。重要的是要确保 I/O 等待不会持续增加或者接近 100%。从下图中可以看出I/O 等待处于 100% 左右这表明磁盘正在超载。2、计算工作集的大小通常来说内存中的数据越多MongoDB 的运行速度就越快。因此按照从最快到最慢的顺序应用程序可能有以下几种情况。整个数据集都在内存中。这是非常好的但通常代价过大或不可行。对于某些依赖于快速响应时间的应用程序来说这可能是必要的。工作集在内存中。这是最常见的选择。工作集是应用程序使用的数据和索引。这可能是其所有内容但通常会有一个涵盖 90% 请求的核心数据集比如 users 集合和最近一个月的活动​。如果这个工作集能够放入 RAM 中那么 MongoDB 的运行速度通常会很快只有在遇到一些“不寻常的”请求时才需要访问磁盘。索引在内存中。索引的工作集在内存中。内存中没有可用的数据子集。如果可能的话应该避免这种情况。这会非常慢。只有知道工作集的内容和大小才能知道是否可以将其保存在内存中。计算工作集大小的最佳方法是跟踪分析常用的操作以确定应用程序的读写量。假设应用程序每周会创建 2GB 的新数据其中 800MB 的数据是经常被访问的。用户倾向于访问最近一个月的数据超过一个月的数据通常不会被用到。这样工作集的大小大约是 3.2GB800MB/周×4 周​再加上预估的索引大小总共为5GB。如图所示可以跟踪一段时间内被访问的数据来考虑这个问题。如图所示如果选择满足 90% 的请求那么在这段时间内生成的数据和索引就会形成工作集。可以测量这段时间的长短来计算出数据集增长了多少。注意这个示例使用了时间时间是最常见的一种访问模式​但可能还有另一种对应用程序更有意义的访问模式。一些工作集的例子假设有一个 40GB 的工作集。总共有 90% 的请求能够命中工作集而 10% 的请求会命中其他数据。如果有500GB 的数据和 50GB 的 RAM那么工作集可以完全放入 RAM 中。一旦应用程序访问了通常需要访问的数据此过程称为预热​那么就不需要在访问工作集时再次访问磁盘了。然后它还有 10GB 的可用空间用于460GB 较少被访问的数据。显然MongoDB 大多数情况下需要到磁盘上获取那些非工作集数据。另外假设工作集不能被放入 RAM 中比如你只有35GB 的 RAM​那么工作集通常会占用大部分 RAM。工作集驻留在 RAM 中的可能性更高因为它被访问的频率更高但在某些时候访问频率较低的数据也会被加载进内存中从而将工作集或其他访问频率较低的数据​“驱逐”出内存。因此会不断发生同磁盘的数据交换访问工作集不再具有可预测的性能。3、跟踪性能情况跟踪查询的性能并使其保持稳定通常很重要。有几种方式可以跟踪 MongoDB 是否能够承受当前的请求负载。对于 MongoDB 来说CPU 的大部分占用时间和 I/O 相关表现为很高的 I/O 等待​。WiredTiger 存储引擎是多线程的可以利用额外的 CPU 核。与旧的 MMAP 存储引擎相比这可以从更高的 CPU 使用量指标中看出。然而如果用户或系统时间接近 100%或者 100% 乘以CPU 数量​那么最常见的原因是缺少了某个常用查询的索引。跟踪 CPU 使用情况是一个很好的方法特别是在部署了应用程序的新版本之后​这样可以确保所有查询都按照其期望的方式运行。注意图中的显示是正常的如果缺页错误的数量较少那么 I/O 等待可能会被其他 CPU 活动抵消。只有当其他活动逐渐增加时糟糕的索引才可能是罪魁祸首。另一个类似的指标是队列长度即有多少请求在等待MongoDB 的处理。当一个请求在等待读操作或写操作的锁时即被认为是在队列中。读写队列随时间变化的曲线如图所示。没有队列最好基本上是一个空图​但是示例中这个图也无须担心。在一个繁忙的系统中操作必须等待一段时间才能获得所需的锁这很正常。WiredTiger 存储引擎提供了文档级别的并发性它允许对同一个集合进行多个并发写操作。这大大提高了并发操作的性能。其所使用的凭证系统可以控制正在使用的线程数量以避免饥饿现象它会为读写操作发出凭证默认情况下每个线程有 128 个凭证​在此之后新的读或写操作则需要排队。serverStatus 的wiredTiger.concurrentTransactions.read.available 和wiredTiger.concurrentTransactions.write.available 字段可用于跟踪可用凭证的数量何时降低为零这分别表示两种操作现在开始排队了。可以查看进入队列的请求数量以判断是否发生了请求堆积。通常队列大小的数值应该较低。一个很长且始终存在的队列表示 mongod 无法处理这个负载。这时应该尽快降低该服务器上的负载。4、跟踪剩余空间另一个基本但很重要的监控指标是磁盘使用情况。有时候用户会等到磁盘空间用完后才考虑如何处理这个问题。通过监控磁盘使用情况并跟踪剩余的磁盘空间可以预测当前驱动器足够用多长时间并提前对空间不足的情况进行规划。当磁盘空间不足时有以下几种选择。如果正在使用分片那么可以再添加一个分片。删除未使用的索引。可以对特定集合使用 $indexStats 聚合来识别它们。如果还没有进行过压缩操作那么可以在一个从节点上执行压缩看看是否有帮助。这通常只在从集合中删除了大量数据或索引且没有新数据替换的情况下才有用。关闭副本集的每个成员一次一个​将其数据复制到更大的磁盘中并进行挂载。重新启动该成员然后继续对下一个成员重复此操作。用较大驱动器的成员替换副本集中的成员删除旧成员并添加新成员然后让新成员追赶上副本集中的其余成员。对集合中的每个成员重复此操作。如果使用了 directoryperdb 选项并且数据库增长速度非常快则可以将数据库移动到其自身的驱动器中。然后将磁盘卷作为一个数据目录进行挂载。这样其他数据就不需要移动了。无论选择哪种技术都需要提前计划以尽量减少对应用程序的影响。进行备份依次修改集合中的每个成员将数据从一个地方复制到另一个地方这些操作都需要时间。5、监控复制情况复制延迟和 oplog 长度是需要跟踪的重要指标。延迟是指从节点无法跟上主节点的速度。它的计算方法是将应用在从节点上最后一个操作的时间减去主节点上最后一个操作的时间。如果从节点刚刚应用了时间戳为 3:26:00p.m. 的操作而主节点刚刚应用了时间戳为 3:29:45p.m. 的操作那么从节点的延迟就是 3 分 45 秒。延迟应该尽可能接近于 0并且通常是毫秒级别的。如果从节点能够与主节点保持同步那么复制延迟应该如图所示即基本上始终为 0。如果从节点复制写操作的速度比主节点的写入速度慢就会出现非零的延迟。最极端的情况是当复制过程卡住时由于某种原因从节点不能应用更多的操作。此时延迟将以“每秒一秒”的速度增长形成如图 22-10 所示的陡坡。这可能是由于网络问题或缺少 “_id” 索引造成的要使复制正常工作每个集合都需要这个索引。由于同步被完全阻塞因此每过一秒都会使延迟增加一秒。如果一个集合缺少 “_id” 索引则将服务器从副本集中脱离并作为单机服务器启动然后创建 “_id” 索引。确保将 “_id” 索引创建为唯一索引。一旦创建“_id” 索引就不能被删除或更改除非删除整个集合​。如果系统处于超载状态那么从节点可能会逐渐落后。复制过程仍然在进行因此通常不会在图中看到“每秒一秒”这样的斜率特征。尽管如此如果从节点不能跟上高峰流量或者正在逐渐落后那么这仍然需要重点关注。主节点不会为了“帮助”从节点赶上来而限制写操作因此在一个超载的系统中从节点落后很常见尤其是MongoDB 中写操作的优先级比读操作要高这意味着主节点上的复制过程无法获取足够的资源​。通过在写关注中使用 “w”可以在一定程度上强制对主节点进行限制。还可以将正在处理的请求路由到另一个成员从而尝试减轻从节点的负载。而在一个负载非常低的系统中可能会看到另一个有趣的模式复制延迟会突然出现峰值如图 22-11 所示。这里所显示的峰值实际上并不是延迟它们是由抽样的变化引起的。mongod 每隔几分钟处理一次写操作。因为延迟是以主节点和从节点上的时间戳之差来度量的所以在对从节点时间戳的测量恰好发生在主节点写入之前时就会使得从节点看起来落后了几分钟。如果增加写操作的频率这些峰值就会消失。另一个需要跟踪的重要的复制指标是每个成员的 oplog长度。每个可能成为主节点的成员都应该拥有超过一天的oplog。如果一个成员可能是另一个成员的同步源那么它的 oplog 应该比初始同步完成所需的时间要长。下图展示了标准的 oplog 长度图。这个 oplog 的长度非常好1111 小时超过一个月的数据通常来说只要有足够的磁盘空间oplog 就应该尽可能地长。oplog 的使 用方式使其基本上不占用任何内存而一个长的 oplog可能意味着运维体验上的天壤之别。下图展示了由较短的 oplog 和变化流量引起的一些不寻常的变化。这仍然是正常的但是这台机器上的oplog 可能太短了维护时间窗口在 6 和 11 小时之间​。当管理员有机会时应该延长 oplog 的长度。