zoukankan      html  css  js  c++  java
  • 记一次MongoDB性能问题

    下面文章转载自火丁笔记,原作者描述了一次MongoDB数据迁移过程中遇到的性能问题及其解决方案,中间追查问题的方法和工具值得我们学习。下面是其原文:


    最近忙着把一个项目从MySQL迁移到MongoDB,在导入旧数据的过程中,遇到了些许波折,犯了不少错误,但同时也学到了不少知识,遂记录下来。

    公司为这个项目专门配备了几台高性能务器,清一色的双路四核超线程CPU,外加32G内存,运维人员安装好MongoDB后,就交我手里了,我习惯于在使用新服务器前先看看相关日志,了解一下基本情况,当我浏览MongoDB日志时,发现一些警告信息:

    WARNING: You are running on a NUMA machine. We suggest launching mongod like this to avoid performance problems: numactl –interleave=all mongod [other options]

    当时我并不太清楚NUMA是什么东西,所以没有处理,只是把问题反馈给了运维人员,后来知道运维人员也没有理会这茬儿,所以问题的序幕就这样拉开了。

    迁移工作需要导入旧数据。MongoDB本身有一个mongoimport工具可供使用,不过它只接受json、csv等格式的源文件,不适合我的需求,所以我没用,而是用PHP写了一个脚本,平稳运行了一段时间后,我发现数据导入的速度下降了,同时PHP抛出异常:

    cursor timed out (timeout: 30000, time left: 0:0, status: 0)

    我一时判断不出问题所在,想想先在PHP脚本里加大Timeout的值应付一下:

    <?php
    
    MongoCursor::$timeout = -1;
    
    ?>

    可惜这样并没有解决问题,错误反倒变着花样的出现了:

    max number of retries exhausted, couldn’t send query, couldn’t send query: Broken pipe

    接着使用strace跟踪了一下PHP脚本,发现进程卡在了recvfrom操作上:

    shell> strace -f -r -p <PID>
    recvfrom(<FD>,

    通过如下命令查询recvfrom操作的含义:

    shell> apropos recvfrom
    receive a message from a socket

    或者按照下面的方式确认一下:

    shell> lsof -p <PID>
    shell> ls -l /proc/<PID>/fd/<FD>

    此时如果查询MongoDB的当前操作,会发现几乎每个操作会消耗大量的时间:

    mongo> db.currentOp()

    与此同时,运行mongostat的话,结果会显示很高的locked值。

    我在网络上找到一篇:MongoDB Pre-Splitting for Faster Data Loading and Importing,看上去和我的问题很类似,不过他的问题实质是由于自动分片导致数据迁移所致,解决方法是使用手动分片,而我并没有使用自动分片,自然不是这个原因。

    询问了几个朋友,有人反映曾遇到过类似的问题,在他的场景里,问题的主要原因是系统IO操作繁忙时,数据文件预分配堵塞了其它操作,从而导致雪崩效应。

    为了验证这种可能,我搜索了一下MongoDB日志:

    shell> grep FileAllocator /path/to/log
    [FileAllocator] allocating new datafile ... filling with zeroes...
    [FileAllocator] done allocating datafile ... took ... secs

    我使用的文件系统是ext4(xfs也不错 ),创建数据文件非常快,所以不是这个原因,但如果有人使用ext3,可能会遇到这类问题,所以还是大概介绍一下如何解决:

    MongoDB按需自动生成数据文件:先是<DB>.0,大小是64M,然后是<DB>.1,大小翻番到128M,到了<DB>.5,大小翻番到2G,其后的数据文件就保持在2G大小。为了避免可能出现的问题,可以采用事先手动创建数据文件的策略:

    #!/bin/sh
    
    DB_NAME=$1
    
    cd /path/to/$DB_NAME
    
    for INDEX_NUMBER in {5..50}; do
        FILE_NAME=$DB_NAME.$INDEX_NUMBER
    
        if [ ! -e $FILE_NAME ]; then
            head -c 2146435072 /dev/zero > $FILE_NAME
        fi
    done

    注:数值2146435072并不是标准的2G,这是INT整数范围决定的。

    最后一个求助方式就是官方论坛了,那里的国际友人建议我检查一下是不是索引不佳所致,死马当活马医,我激活了Profiler记录慢操作:

    mongo> use <DB>
    mongo> db.setProfilingLevel(1);

    不过结果显示基本都是insert操作(因为我是导入数据为主),本身就不需要索引:

    mongo> use <DB>
    mongo> db.system.profile.find().sort({$natural:-1})

    问题始终没有得到解决,求人不如求己,我又重复了几次迁移旧数据的过程,结果自然还是老样子,但我发现每当出问题的时候,总有一个名叫irqbalance的进程CPU占用率居高不下,搜索了一下,发现很多介绍irqbalance的文章中都提及了NUMA,让我一下子想起之前在日志中看到的警告信息,我勒个去,竟然绕了这么大一个圈圈!安下心来仔细翻阅文档,发现官方其实已经有了相关介绍,按如下设置搞定:

    shell> echo 0 > /proc/sys/vm/zone_reclaim_mode
    shell> numactl --interleave=all mongod [options]

    关于zone_reclaim_mode内核参数的说明,可以参考官方文档

    注:从MongoDB1.9.2开始:MongoDB会在启动时自动设置zone_reclaim_mode。

    至于NUMA的含义,简单点说,在有多个物理CPU的架构下,NUMA把内存分为本地和远程,每个物理CPU都有属于自己的本地内存,访问本地内存速度快于访问远程内存,缺省情况下,每个物理CPU只能访问属于自己的本地内存。对于MongoDB这种需要大内存的服务来说就可能造成内存不足,NUMA的详细介绍,可以参考老外的文章

    理论上,MySQL、Redis、Memcached等等都可能会受到NUMA的影响,需要留意。

    ***********************************************************************

    对于罪魁祸首,作者留给大家去学习,在这里可以给大家做一个简单的描述,先解释几个概念。

    NUMA:NUMA是多核心CPU架构中的一种,其全称为Non-Uniform Memory Access,简单来说就是在多核心CPU中,机器的物理内存是分配给各个核的,架构简图如下所示:
     
    每个核访问分配给自己的内存会比访问分配给其它核的内存要快,有下面几种访问控制策略:

    1.缺省(default):总是在本地节点分配(分配在当前进程运行的节点上);
    2.绑定(bind):强制分配到指定节点上;
    3.交叉(interleave):在所有节点或者指定的节点上交织分配;
    4.优先(preferred):在指定节点上分配,失败则在其他节点上分配。
    上面文章中最后使用numactl –interleave命令就是指定其为交叉共享模式。

    irqbalance:这是作者在上面提到的一个占用CPU的进程,这个进程的作用是在多核心CPU的操作系统中,分配系统中断信号的。参见:irqbalance.org

    概念说完了,下面是上面问题的简单描述:
    我们知道虚拟内存机制是通过一个中断信号来通过进行内存swap的,所以这个irqbalance进程忙,是一个危险信号,在这里是由于在进行频繁 的内存交换。这种频繁交换现象称为swap insanity,在MySQL中经常提到,也就是在NUMA框架中,采用不合适的策略,导致核心只能从指定内存块节点上分配内存,即使总内存还有富余, 也会由于当前节点内存不足时产生大量的swap操作。


    from: http://blog.163.com/lgh_2002/blog/static/4401752620130152575313/

  • 相关阅读:
    (组件、路由)懒加载
    vue.js实现用户评论、登录、注册、及修改信息功能
    vue 路由传参 params 与 query两种方式的区别
    WebSocket入门教程(五)-- WebSocket实例:简单多人聊天室
    js系列教程11-json、ajax(XMLHttpRequest)、comet、SSE、WebSocket全解
    回忆一下跨域
    Http,Socket,TCP/IP 协议简述
    Vue+WebSocket 实现页面实时刷新长连接
    微信小程序JS导出和导入
    Vue学习之路之登录注册实例代码
  • 原文地址:https://www.cnblogs.com/moqiang02/p/4061133.html
Copyright © 2011-2022 走看看