MongoDB中的常见问题(五)——诊断


MongoDB诊断

我们可以在哪里找到非正常停止运行的mongod进程相关信息?

如果mongod在Unix或基于Unix平台上非正常地停止时,并且如果mongod不能记录停机或错误信息,那么我们可以检查系统日志来查看关于MongoDB的信息。例如,位于/var/log/messages中的日志,使用下面的命令:

sudo grep mongod /var/log/messages
sudo grep score /var/log/messages

TCP的keepalive时间是否会影响MongoDB部署?

如果我们在客户端和服务器之间或者分片集群成员之间或者复制集中遇到了socket错误,没有别的合一原因,那么可以检查TCP keepalive值(例如,Linux系统上存储的tcp_keepalive_time值)。一个常用的keepalive时间是7200秒(2个小时),然而,不同的版本和OS X可能会有不同的配置。

对于MongoDB,我们最好有一个更短的keepalive时间,大约120秒(2分钟)。

如果我们的MongoDB部署遇到了keep-alive相关的问题,我们必须调整运行着MongoDB进程的所有机器上的keep alive值。这里面就包括了所有运行着mongos或者mongod进程的机器和所有运行着连接到MongoDB客户端进程的机器。

注意:对于非Linux系统,大于或等于600秒(10分钟)的值将会被mongod和mongos忽略。对于Linux, 大于300秒(5分钟)的值将会被最大为300秒的mongod和mongos sockets重写。

在Linux系统上
– 如果我们想要查看keep alive 设置,我们可以使用下面的命令之一:

sysctl net.ipv4.tcp_keepalive_time

或者

cat /proc/sys/net/ipv4/tcp_keepalive_time

返回的值单位为:秒。

  • 如果我们想要修改keep alive 设置,我们可以使用下面的命令之一:
sudo sysctl -w net.ipv4.tcp_keepalive_time=<value>

或者

echo <value> | sudo tee /proc/sys/net/ipv4/tcp_keepalive_time

这些操作不会在系统重启后持久化。如果想要保存这些配置,将下面这一行添加到/etc/sysctl.conf中:

net.ipv4.tcp_keepalive_time = <value>

OS X系统
– 使用下面的命令查看keep alive配置:

sysctl net.inet.tcp.keepinit
  • 我们可以使用下面的命令来修改net.inet.tcp.keepint值:
sysctl -w net.inet.tcp.keepinit=<value>

上面设置TCP keep alive的值都不是持久化的,在每次重启系统时我们还是得重新设置这个值。查阅相关操作系统的文档来查看持久化设置TCP keepalive值的指令。

Windows系统
– 使用下面的命令来查看keep alive配置:

reg query HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters /v KeepAliveTime

默认不会展示注册表的值。如果不存在该值的话,系统默认值是720000毫秒或者16进制中的0x6ddd00。

  • 在以管理员身份运行的命令行提示符中使用下面的命令修改KeepAliveTime值,其中以16进制的形式表示(例如,120000表示为0x1d4c0)
reg add HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\ /v KeepAliveTime /d <value>

如果想要新的系统级别的keepalive设置生效,我们需要重启mongod或者mongos进程。

为什么MongoDB会记录如此多的“接受连接”事件?

如果我们在MongoDB日志总看到大量连接和重连的信息,那么说明客户端正在频繁地连接和终止到MongoDB服务器的连接。这是应用中未使用请求池的正常行为,例如CGI。考虑使用FastCGI,一个Apache模块或者一些别的持续应用服务器来减少连接开销。

如果这些连接不影响我们的性能,我们可以使用运行时quiet选项或者命令行选项--quit来避免日志中的这些信息。

可以使用什么工具来监控MongoDB?

MongoDB Cloud Manager和Ops Manager,是MongoDB高级企业版中提供的解决方案,包含了监控功能,从正在运行的MongoDB部署中收集数据并且基于这些数据提供可视化和警报。

MMAPv1存储引擎的内存诊断

我是否需要配置交换空间?

请为系统分配交换空间。没有交换空间的话,我们的系统可能会在一些特别急需内存、内存泄漏或者多个项目使用相同内存的情况下不可靠。把交换空间看做水蒸气释放阈值,能够允许系统释放额外的压力,并且不会影响到系统的整体功能。

然而,运行MongoDB的系统需要交换空间用于常规操作。数据库文件是内存映射的,会占据我们的大部分MongoDB内存使用。因此,mongod不可能在正常操作中使用任何交换空间。操作系统将会在不需要交换空间的情况下,从内存映射文件中释放内存,MongoDB可以在不需要交换系统的情况下,将数据写入到数据文件。

什么是“工作集”?

工作集是客户端最常读取的那部分数据。

我们的工作集大小必须小于RAM吗?

我们的工作集应该维护在内存中以实现较优的性能。否则的话,将会出现许多随机磁盘输出输出,除非我们使用SSD,这将会非常缓慢。

可以通过索引读取模式来查看管理工作集的大小。如果我们在任意位置插入索引(例如,id是通过哈希随机生成的),我们将会持续更新整个索引。相反,如果我们能够创建近似升序的id(例如,日期与一个随机数结合的ID),那么所有的更新将会位于b-tree的右侧。索引页面的工作集大小将会更小。

如果数据库和虚拟大小大于RAM也是可以的。

我们如何计算应用所需的RAM值?

我们需要的RAM值受几个方面的影响,包括但不限于:
– 数据库存储和工作集之间的关系
– 操作系统针对近期最少使用(LRU)的缓存策略
– 日志的影响
– 分页错误的比例
– 每个数据库连接线程会需要高达1MB的RAM

MongoDB与操作系统不同的地方在于将数据从磁盘倒入到内存的时候。它简单地将所有数据文件映射到内存,然后依赖于操作系统缓存文件。在有内存限制时,操作系统一般将近期最少使用的数据从内存中移出。例如,如果客户端读取索引比读取文档更频繁,那么索引会更有可能维持在内存中,但是这根据我们特定的用途而定。

如果想要计算我们需要多少RAM,我们必须计算我们的工作集大小,或者客户端最经常使用的数据比例。而这又依赖于我们的读取模式:我们拥有什么索引以及文档的大小。由于MongoDB对每个连接模型都使用一个线程,因此,数据库连接会需要高达1MB的RAM,不管这个连接是处于活跃还是空闲的状态。

如果经常出现分页错误,我们的工作集放入RAM。如果错误率高于该值,那么就可能会出现性能降低的风险。旋转型磁盘面临的问题比SSD面临的问题严重。

我们如何在UNIX的top命令中读取内存统计?

由于mongod使用内存映射文件,top中的内存统计需要以一种特殊的方式编译。在一个大型数据库中,VSIZE(虚拟字节)比较有可能是整个数据库的大小。如果mongod没有其它的进程运行,RSIZE(常驻字节)是机器的全部内存,因为这个值计算了文件系统缓存的内容。

而在Linux系统中,使用vmstat命令来帮助了解系统如何使用内存。在OS X系统中,使用vm_stat。

WiredTiger存储引擎的内存诊断

我们的工作集大小必须小于RAM吗?

不需要。如果缓存没有足够的空间来导入额外的数据,WiredTiger从缓存中取出页面来释放空间。

注意:storage.wiredTiger.engineConfig.cacheSizeGB限制了WiredTiger内部缓存的大小。操作系统将会使用可用的空闲内存作为文件系统缓存,这使得压缩的MongoDB数据文件可以保存在内存中。此外,操作系统将会使用任意空闲的RAM来缓存文件系统块和文件系统缓存。为了安置RAM的其它消费者,我们可能会需要降低WiredTiger的内部缓存大小。

默认的WiredTiger内部缓存大小值假定每台机器上只有一个单一的mongod实例。如果一台机器上包含多个MongoDB示例,那么我们应该要降低该设置以满足其他mongod示例的需求。

如果我们在一个无法读取系统所有可用RAM的容器(例如,lxc,cgroups,Docker等)中运行mongod,我们必须将storage.wiredTiger.engineConfig.cacheSizeGB设置到一个小于容器可用内存的值。具体的大小根据容器中运行的其它进程而定。

我们可以使用serverStatus命令来查看缓存和回收统计。wiredTiger.cache字段包含了缓存和回收的信息。

...
"wiredTiger" : {
   ...
   "cache" : {
      "tracked dirty bytes in the cache" : <num>,
      "bytes currently in the cache" : <num>,
      "maximum bytes configured" : <num>,
      "bytes read into cache" :<num>,
      "bytes written from cache" : <num>,
      "pages evicted by application threads" : <num>,
      "checkpoint blocked page eviction" : <num>,
      "unmodified pages evicted" : <num>,
      "page split during eviction deepened the tree" : <num>,
      "modified pages evicted" : <num>,
      "pages selected for eviction unable to be evicted" : <num>,
      "pages evicted because they exceeded the in-memory maximum" : <num>,,
      "pages evicted because they had chains of deleted items" : <num>,
      "failed eviction of pages that exceeded the in-memory maximum" : <num>,
      "hazard pointer blocked page eviction" : <num>,
      "internal pages evicted" : <num>,
      "maximum page size at eviction" : <num>,
      "eviction server candidate queue empty when topping up" : <num>,
      "eviction server candidate queue not empty when topping up" : <num>,
      "eviction server evicting pages" : <num>,
      "eviction server populating queue, but not evicting pages" : <num>,
      "eviction server unable to reach eviction goal" : <num>,
      "pages split during eviction" : <num>,
      "pages walked for eviction" : <num>,
      "eviction worker thread evicting pages" : <num>,
      "in-memory page splits" : <num>,
      "percentage overhead" : <num>,
      "tracked dirty pages in the cache" : <num>,
      "pages currently held in the cache" : <num>,
      "pages read into cache" : <num>,
      "pages written from cache" : <num>,
   },
   ...

可以查看文档了解一些关键的缓存和回收统计。

可以使用storage.wiredTiger.engineConfig.cacheSizeGB--wiredTigerCacheSizeGB对WiredTiger内部缓存大小进行调整。

我们应该如何计算应用需要多少RAM?

使用WiredTiger存储引擎时,MongoDB同时利用WiredTiger内部的缓存和文件系统缓存。

从3.4版本开始。WiredTiger内部缓存默认使用下面值中的较大值:
– RAM的50%减去1GB
– 256MB

通过文件系统缓存,MongoDB自动使用所有没有被WiredTiger缓存使用或其它进程使用的空闲内存。文件系统缓存中的数据是已经经过压缩的。

可以使用storage.wiredTiger.engineConfig.cacheSizeGB--wiredTigerCacheSizeGB对WiredTiger内部缓存大小进行调整。不要将WiredTiger内部缓存大小超过默认值。

注意:storage.wiredTiger.engineConfig.cacheSizeGB限制了WiredTiger内部缓存的大小。操作系统将会使用可用的空闲内存作为文件系统缓存,这使得压缩的MongoDB数据文件可以保存在内存中。此外,操作系统将会使用任意空闲的RAM来缓存文件系统块和文件系统缓存。为了安置RAM的其它消费者,我们可能会需要降低WiredTiger的内部缓存大小。

默认的WiredTiger内部缓存大小值假定每台机器上只有一个单一的mongod实例。如果一台机器上包含多个MongoDB示例,那么我们应该要降低该设置以满足其他mongod示例的需求。

如果我们在一个无法读取系统所有可用RAM的容器(例如,lxc,cgroups,Docker等)中运行mongod,我们必须将storage.wiredTiger.engineConfig.cacheSizeGB设置到一个小于容器可用内存的值。具体的大小根据容器中运行的其它进程而定。

我们可以使用serverStatus命令来查看缓存和回收统计。wiredTiger.cache字段包含了缓存和回收的信息。

分片集群诊断

维护一个成功分片集群的两个最重要的因素是:
– 选择一个合适的片键
– 充足的容量以支持现有的和未来的操作

我们可以通过保证选择针对部署而言最佳可能的片键和保证在当前资源饱和之前经常向我们的集群增加额外的容量来避免分片中遇到的大部分问题。继续阅读下面的内容来了解我们在生产环境中可能遇到的特定问题。

在一个新的分片集群中,为什么所有的数据都维持在一个分片中?

我们的集群必须有充足的数据用于分片发挥作用。分片通过在分片之间迁移块,直到每个分片有差不多相同数目的数据块。

默认的数据块大小是64MB。MongoDB将在集群中数据块的不均衡度超过一个迁移阈值之后才会开始迁移。这个行为帮助避免了不必要的迁移,这将会降低我们集群的整体性能。

如果我们刚部署完一个集群系统,保证我们拥有足够多的数据来使得分片生效。如果我们没有足够的数据来创建超过8个64GB的数据块,那么所有的数据都将会维持在一个分片中。要么降低块大小的设置,要么向集群中添加更多数据。

作为一个相关的问题,系统将只会在插入或更新时分块,这就意味着如果我们配置分片,并且不继续进行插入操作和更新操作的话,那么数据库将不会创建新的块,我们可以选择等待应用插入数据或者是手动分块。

最后,如果我们的片键有低基数,MongoDB可能无法再数据之间创建足够的分割。

为什么一个分片会收到集群中不合适数量的流量?

在一些情况下,一个单一的分片或者集群的子集将会收到不合适比例的流量和工作服在。在大多数情况下,这是片键无法有效地允许写扩展的结果。

也有可能我们有“热数据块”。在这种情况下,我们可能可以通过分割和迁移这些数据块的一部分来解决这个问题。

最坏的情况下,我们也许不得不考虑对我们的数据进行重新分片,选择一个不同的片键来纠正这个模式。

什么可能会影响集群均衡?

如果我们刚部署我们的分片集群,可以查看第一个小问题。

如果集群开始的时候是均衡的,但是之后出现数据的不均匀分布,考虑下面可能的原因:

  • 我们从集群中删除了大量的数据。如果我们增加了额外的数据,这可能会根据片键会有一个不同的分布。
  • 我们的片键有低基数,MongoDB不能再进一步分割块
  • 我们的数据集增长非常迅速,均衡器来不及在集群之间分发数据。这是不普遍的,一般有下面几种原因:
    • 与数据增长率相比,一个均衡窗口太小
    • 需要更多数据迁移的写操作的不均衡分布。我们也许不得不选择饿一个不同的片键来解决这个问题
    • 分片之间交叉的网络连接,可能会导致块的迁移花费太长的时间来完成。了解我们的网络配置和不同分片间的相互连接。

为什么块迁移会影响分片集群的性能?

如果以迁移影响了我们的集群或应用的性能,可以根据影响的本质考虑采取以下措施:

  • 如果迁移只会偶然地影响我们的集群,我们可以限制均衡窗口来避免在高峰时间段的均衡活动。保证留下充足的时间来避免数据再次变得不均衡。
  • 如果均衡器经常迁移块,影响了整体集群性能:
    • 我们可以尝试降低块的大小来限制迁移的大小
    • 我们的集群也许超过了容量,我们可以尝试想集群增加一个或两个分片来分布负载。

也有可能是我们的片键造成应用将所有的写入导到一个单一的分片。这种类型的活动模式可能会要求均衡器在写入数据之后尽快迁移大部分数据。考虑使用一个提供了更佳写入扩展的片键来重新部署集群。

打赏

mickey

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