• Nginx 内存占用高排查


    背景

    线上两台 OpenResty 占用内存过高,8c32G 的机器用了 28G 内存,总觉得不正常,使用简单的重启大法,并没什么用处,今天刚好排查一下。

    free
    [root@iZ1w4igf11Z conf]# free -g
                 total       used       free     shared    buffers     cached
    Mem:            31         26          5          0          0         24
    -/+ buffers/cache:          1         29
    Swap:            0          0          0
    
    top -M(按内存占用排序)
    top - 11:19:40 up 137 days, 23:46,  1 user,  load average: 0.03, 0.06, 0.07
    Tasks: 180 total,   1 running, 179 sleeping,   0 stopped,   0 zombie
    Cpu(s):  5.2%us,  0.9%sy,  0.0%ni, 93.1%id,  0.0%wa,  0.4%hi,  0.4%si,  0.0%st
    Mem:  32877652k total, 27437576k used,  5440076k free,   343712k buffers
    Swap:        0k total,        0k used,        0k free, 25292856k cached
    
      PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                           
    14706 root      20   0  124m  55m 3740 S  6.9  0.2   1:48.64 nginx                             
    14704 root      20   0  124m  53m 3744 S  6.9  0.2   1:54.05 nginx                             
    14711 root      20   0  119m  51m 3460 S  3.4  0.2   1:46.87 nginx                             
    14705 root      20   0  119m  50m 3592 S  6.9  0.2   1:49.36 nginx                             
    14709 root      20   0  119m  50m 3464 S 10.3  0.2   1:45.98 nginx                             
    14707 root      20   0  119m  50m 3468 S  0.0  0.2   1:50.33 nginx                             
    14710 root      20   0  119m  50m 3476 S  6.9  0.2   1:47.90 nginx                             
    14708 root      20   0  119m  49m 3488 S  3.4  0.2   1:53.28 nginx                             
    12494 root      20   0 93480  28m 2892 S  0.0  0.1   0:18.17 nginx                
    

    排查过程

    strace

    因为使用 OpenResty 安装了几个第三方模块,怀疑是不是这些模块导致了内存泄露,于是用 strace -o /tmp/nginx.txt -p pid 跟踪进程,并没有看到什么有用的信息。

    epoll_wait(50, {{EPOLLOUT, {u32=2908154745, u64=140160575925113}}}, 512, 3087) = 1
    write(64, "224|4347355313202w\-332v314220u262353257<wB352212o|32533nUH211="..., 12264) = 12264
    write(64, "2733@30T332Yu254D,227M207256V361z7232G12441620416212342Pp236"..., 16413) = 16413
    write(64, "2733@30T332Yu254D,230Vcs326340247276y20722030223365313f[243253q"..., 16413) = 16413
    write(64, "2733@30T332Yu254D,231257d"*330.yz,q237#350344271323w~215"..., 16413) = 3870
    write(64, "267C9303o3320{357v3202325I215256237D302256u31027535214223'f177211313232"..., 12543) = -1 EAGAIN (Resource temporarily unavailable)
    epoll_wait(50, {{EPOLLIN, {u32=2908139864, u64=140160575910232}}}, 512, 3067) = 1
    read(37, "27332373130736241350)E233H3742726>330C333Q361v315"..., 33093) = 768
    read(37, 0x30abc73, 33093)              = -1 EAGAIN (Resource temporarily unavailable)
    epoll_ctl(50, EPOLL_CTL_MOD, 37, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=2908139864, u64=140160575910232}}) = 0
    socket(PF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_IP) = 31
    ioctl(31, FIONBIO, [1])                 = 0
    fcntl(31, F_SETFD, FD_CLOEXEC)          = 0
    epoll_ctl(50, EPOLL_CTL_ADD, 31, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=2908175329, u64=140160575945697}}) = 0
    connect(31, {sa_family=AF_INET, sin_port=htons(10000), sin_addr=inet_addr("192.168.209.150")}, 16) = -1 EINPROGRESS (Operation now in progress)
    epoll_wait(50, {{EPOLLOUT, {u32=2908139864, u64=140160575910232}}}, 512, 3039) = 1
    epoll_wait(50, {{EPOLLOUT, {u32=2908175329, u64=140160575945697}}}, 512, 3038) = 1
    getsockopt(31, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
    writev(31, [{"POST /push/v6/t/bind HTTP/1.0
    H"..., 446}, {"expire_time=1599494399244&key=Lf"..., 344}], 2) = 790
    epoll_wait(50, {{EPOLLOUT, {u32=2908154745, u64=140160575925113}}}, 512, 3038) = 1
    write(64, "267C9303o3320{357v3202325I215256237D302256u31027535214223'f177211313232"..., 12543) = 12543
    write(64, "2733@30T332Yu254D,232fr374266245Y217250333$264J231331340252307-331"..., 16413) = 16413
    write(64, "2733@30T332Yu254D,2337333240276372307c302374)351317Y370216335257v*"..., 16413) = 16413
    write(64, "2733@30T332Yu254D,23427630253235270L203330307231F370316177250245211251y"..., 16413) = 14471
    write(64, "33220234*3235177342H .36632233610X(6-a371305204327356315m370T31-"..., 1942) = -1 EAGAIN (Resource temporarily unavailable)
    epoll_wait(50, {{EPOLLIN|EPOLLOUT, {u32=2908175329, u64=140160575945697}}}, 512, 3029) = 1
    recvfrom(31, "HTTP/1.1 200 OK
    Server: Apache-"..., 262144, 0, NULL, NULL) = 199
    epoll_wait(50, {{EPOLLIN|EPOLLOUT|EPOLLRDHUP, {u32=2908175329, u64=140160575945697}}}, 512, 3017) = 1
    readv(31, [{"UAA4GNADCBiQKBgQCiWydoXPuXeTu3IL"..., 261945}], 1) = 0
    close(31)                               = 0
    
    message

    查看 /var/log/messages 无异常

    Nginx buffer 配置

    查看目前的 buffer,尝试调低

    # grep buffer nginx.conf
        large_client_header_buffers 4 16k;
        client_body_buffer_size 128k;
        proxy_busy_buffers_size 256k;
        client_header_buffer_size 256k;
        proxy_buffer_size 256k;
        proxy_buffers 64 128k;
        access_log  logs/access.log access buffer=32k;
        gzip_buffers 16 64k;
    

    修改为:

    [root@iZ1rp1vunvZ conf]# grep buffer nginx.conf
        large_client_header_buffers 4 16k;
        client_body_buffer_size 64k;
        proxy_busy_buffers_size 64k;
        client_header_buffer_size 64k;
        proxy_buffer_size 64k;
        proxy_buffers 8 32k;
        access_log  logs/access.log access buffer=32k;
        gzip_buffers 32 4k;
    

    reload 后,无变化。

    进一步查看meminfo
    [root@iZ1rp1vunvZ conf]# cat /proc/meminfo 
    MemTotal:       32877652 kB
    MemFree:         5626820 kB
    Buffers:          477252 kB
    Cached:         25028124 kB
    SwapCached:            0 kB
    Active:         18819564 kB
    Inactive:        7086080 kB
    Active(anon):     401336 kB
    Inactive(anon):      856 kB
    Active(file):   18418228 kB
    Inactive(file):  7085224 kB
    Unevictable:           0 kB
    Mlocked:               0 kB
    SwapTotal:             0 kB
    SwapFree:              0 kB
    Dirty:             45564 kB
    Writeback:             0 kB
    AnonPages:        400660 kB
    Mapped:            23288 kB
    Shmem:              1828 kB
    Slab:            1118776 kB
    SReclaimable:    1071452 kB
    SUnreclaim:        47324 kB
    KernelStack:        2504 kB
    PageTables:         7608 kB
    NFS_Unstable:          0 kB
    Bounce:                0 kB
    WritebackTmp:          0 kB
    CommitLimit:    16438824 kB
    Committed_AS:    1204760 kB
    VmallocTotal:   34359738367 kB
    VmallocUsed:       65224 kB
    VmallocChunk:   34359637096 kB
    HardwareCorrupted:     0 kB
    AnonHugePages:    245760 kB
    HugePages_Total:       0
    HugePages_Free:        0
    HugePages_Rsvd:        0
    HugePages_Surp:        0
    Hugepagesize:       2048 kB
    DirectMap4k:        6144 kB
    DirectMap2M:    33548288 kB
    

    发现 Slab 占用挺多的,其中的 Slab,查看相关资料:
    通常的说法是:内核数据结构缓存的大小,可以减少申请和释放内存带来的消耗
    这里的说法太笼统了

    详细的说法如下:
    在 linux 内核中会有许多小对象,这些对象构造销毁十分频繁,比如 i-node,dentry。这么这些对象如果每次构建的时候就向内存要一个页,而其实际大小可能只有几个字节,这样就非常浪费,为了解决这个问题就引入了一种新的机制来处理在同一页框中如何分配小存储器区,这个机制可以减少申请和释放内存带来的消耗,这些小存储器区的内存称为 Slab

    df -i 查看 inode,占用并不大

    [root@iZ1rp1vunvZ conf]# df -ih
    Filesystem     Inodes IUsed IFree IUse% Mounted on
    /dev/xvda1       1.3M  150K  1.2M   12% /
    tmpfs            4.0M     1  4.0M    1% /dev/shm
    /dev/xvdb1       6.3M  101K  6.2M    2% /usr/local/openresty/nginx/html/image
    
    陷入僵局

    我这么多内存去哪了?

    没办法了,只能去打扰大神了。

    请教大神

    首先查看进程到底占用了多少内存:

    [root@iZ1rp1vunvZ conf]# ps aux | awk '{print $6/1024 " MB		" $11"	"$NF}' | sort -nr|head -10
    50.8242 MB		nginx:	process
    50.4219 MB		nginx:	process
    50.3633 MB		nginx:	process
    50.3477 MB		nginx:	process
    50.2734 MB		nginx:	process
    50.2305 MB		nginx:	process
    50.2266 MB		nginx:	process
    49.8516 MB		nginx:	process
    28.7969 MB		nginx:	nginx
    21.0898 MB		./node_exporter	./node_exporter
    

    free -m发现了 cached

    [root@iZ1rp1vunvZ conf]# free -g
                 total       used       free     shared    buffers     cached
    Mem:            31         25          5          0          0         23
    -/+ buffers/cache:          1         29
    Swap:            0          0          0
    

    难道都变成 cached 了?尝试着清理

    sync
    echo 1 > /proc/sys/vm/drop_caches
    echo 2 > /proc/sys/vm/drop_caches
    echo 3 > /proc/sys/vm/drop_caches
    

    我丢失的内存回来了。。。

    原理

    上面执行的清除原理如下:

    • sync:将所有未写的系统缓冲区写到磁盘中,包含已修改的 i-node、已延迟的块 I/O 和读写映射文件
    • echo 1 > /proc/sys/vm/drop_caches:清除page cache
    • echo 2 > /proc/sys/vm/drop_caches:清除回收 Slab分配器中的对象(包括目录项缓存和 inode 缓存)。Slab 分配器是内核中管理内存的一种机制,其中很多缓存数据实现都是用的 pagecache。
    • echo 3 > /proc/sys/vm/drop_caches:清除 pagecache 和 Slab分配器中的缓存对象。
      /proc/sys/vm/drop_caches 的值,默认为0

    最后了解下两个概念 buff 和 cache

    • buff(Buffer Cache)是一种 I/O 缓存,用于内存和硬盘的缓冲,是 io设备的读写缓冲区。根据磁盘的读写设计的,把分散的写操作集中进行,减少磁盘碎片和硬盘的反复寻道,从而提高系统性能。
    • cache(Page Cache)是一种高速缓存,用于 CPU 和内存之间的缓冲 ,是文件系统的 cache。
      把读取过的数据保存起来,重新读取时若命中(找到需要的数据)就不要去读硬盘了,若没有命中就读硬盘。其中的数据会根据读取频率进行组织,把最频繁读取的内容放在最容易找到的位置,把不再读的内容不断往后排,直至从中删除。

    它们都是占用内存。两者都是RAM中的数据。简单来说,buff是即将要被写入磁盘的,而cache是被从磁盘中读出来的。

    目前进程正在实际被使用的内存的计算方式为used-buff/cache,通过释放buff/cache内存后,我们还可以使用的内存量free+buff/cache。通常我们在频繁存取文件后,会导致buff/cache的占用量增高。

    总结

    一开始认真的点看的话,直接清理就完事了。就不用花这么多时间,不过重温一下知识点也好。

  • 相关阅读:
    ORA01940: cannot drop a user that is currently connected
    struts+swfupload实现批量图片上传(下篇)
    iPhone开发入门教程
    从零开始学习OpenGL ES集合
    iPhone入门学习——半翻页动画效果例子
    《Android学习指南》目录
    基于OpenGL ES 的图片翻转例子,包含双面贴图3D变换
    Android核心分析
    ios开发之分享一个特效 Cube
    仿Drinkspiration App的menu
  • 原文地址:https://www.cnblogs.com/fsckzy/p/13626192.html
Copyright © 2020-2023  润新知