已索引

问题分析

OA 系统达梦数据库主机内存使用率超过 85%,使用 top 命令查看到信息如下:

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                        
2454351 dmdba     20   0   55.6g  50.2g  14820 S  17.9  81.4 112773:29 dmserver 

查看达梦数据库内存相关设置,可见其最大可用内存被控制在 33108MB 左右。

LINEID     PARA_NAME            PARA_VALUE
---------- -------------------- ----------
1          MEMORY_TARGET        6000
2          BUFFER               20000
3          RECYCLE              2000
4          SORT_BUF_SIZE        10
5          SORT_BUF_GLOBAL_SIZE 2000
6          HJ_BUF_GLOBAL_SIZE   3000
7          HAGR_BUF_GLOBAL_SIZE 3000
8          DICT_BUF_SIZE        50
9          CACHE_POOL_SIZE      1024
10         RLOG_POOL_SIZE       1024
11         总量               33108

11 rows got

查看内存缓存区的实际使用量为 33779MB。

LINEID     DATA_BUFFER MEM_POOL TOTAL_SIZE
---------- ----------- -------- ----------
1          22008MB     11856MB  33864MB

可见内存已经使用到最大值了。

但从操作系统层面来看,DM Server 用了 50G 左右的内存,还有近 17G 的内存去哪儿了呢?

检查 MALLOC_ARENA_MAX 环境变量设置:

# strings /proc/2454351/environ | grep MALL
MALLOC_ARENA_MAX=4

# grep MALL /home/dmdba/dmdbms/bin/DmServiceOA
export MALLOC_ARENA_MAX=4

检查 dmserver 进程的内存映射:

pmap -x 2454351 | sort -nrk3
......
00007f3a77fff000   65540   65540   65540 rw---   [ anon ]
00007f3a5bfff000   65540   65540   65540 rw---   [ anon ]
00007f382ffff000   65540   65540   65540 rw---   [ anon ]
00007f376bfff000   65540   65540   65540 rw---   [ anon ]
00007f37cc000000   65532   65532   65532 rw---   [ anon ]
00007f3b38000000   65488   65488   65488 rw---   [ anon ]
00007f3b20000000   65488   65488   65488 rw---   [ anon ]
00007f39ec000000   65488   65488   65488 rw---   [ anon ]
00007f37a4000000   65432   65432   65432 rw---   [ anon ]
00007f3b28000000   65412   65412   65412 rw---   [ anon ]
00007f3b58000000   65240   65240   65240 rw---   [ anon ]
00007f41cc000000   65192   65172   65172 rw---   [ anon ]
00007f3a0c000000   65160   65160   65160 rw---   [ anon ]
00007f3b68000000   65048   65048   65048 rw---   [ anon ]
00007f3668000000   64976   64976   64976 rw---   [ anon ]
00007f3b90000000   64808   64808   64808 rw---   [ anon ]
00007f3b98000000   64716   64716   64716 rw---   [ anon ]
00007f3b94000000   64688   64688   64688 rw---   [ anon ]
......

可以看到大量这种64M左右的内存,总共约 290 多个,对第三列(RSS)求和结果为 17G。

原因分析

(参考 https://www.easyice.cn/archives/341

如果应用程序每次分配内存的时候都通过操作系统调用 mmap,sbrk 等来分配,效率会很低,所以应用程序使用内存的时候通过 glibc 自己的内存池来提供,早期的 glibc 版本中,只有一个内存池,称为 main arena,在多线程场景中,每次分配和释放需要进行加锁。后来为了降低锁的粒度,从 glibc 2.10 版本开始引入了 thread arena,线程在申请内存的时候,glibc 为他创建一个 thread arena,这个内存池的最大大小一般是 64M。

环境变量 MALLOC_ARENA_MAX 用来控制进程可以创建的 thread arena 数量上限,如果 MALLOC_ARENA_MAX 设置为非 1 值 n,当 n 个 thread arena 的内存空间全被占满的时候,线程新申请内存的时候优先创建新的 arena 而不是从 main arena 分配,不受 n 的限制,并且是无上限的。
注意,如果线程申请的总内存量达到 64M,不能直接 malloc 64M,因为 glibc 对于大块内存的申请直接 mmap。

只有在 thread arena 没有被占满的情况下。为 MALLOC_ARENA_MAX 设置的阈值才可以控制住设定的数量。

当 MALLOC_ARENA_MAX 设置为 1,相当于禁用了 thread arena,不会创建任何 thread arena。

达梦工程师:达梦数据库针对从操作系统申请的内存资源进行内存池管理,所有的数据库线程从自己的内存池中进行处理,但是操作系统glibc版本大于2.10的话会给数据库线程再分配一个内存池,这个内存池数据库是无法管理和释放的。

据此,我们还可以得到一个结论,如果达梦数据库不繁忙,初始内存总是够用的,线程就不会向操作系统申请,就不会有此问题。
如果达梦数据库需要扩展内存,线程就需要向操作系统申请,初始内存配置越不充足,申请的次数就越多,该问题就越明显。

优化建议

根据现在内存池的情况,调大内存设置的初始值,以减少后期的内存申请操作,如此一来:
1).如果数据库内存已经动态稳定了,即使将 MALLOC_ARENA_MAX 设置为4,后期因为不会有太多的操作系统内存申请,该问题也不会出现
2).如果将 MALLOC_ARENA_MAX 设置为1,因为向操作系统申请内存的次数大大减少,可最大化降低向 main arena 申请内存时加锁产生的性能影响

-- By 许望(RHCA、OCM、VCP)
最后修改:2025 年 05 月 23 日 02 : 49 PM
如果觉得我的文章对你有用,请随意赞赏