redis内存碎片率很低——上篇

mac2024-03-26  33

redis内存碎片率很低——上篇

一、起因二、分析2.1 自己分析2.2 搜索资料2.3 分析进程占用2.4 确认问题 三、寻找问题的根源3.1 通过Address映像起始地址来找3.2 通过redis进程函数系统调用来找 四、后续

一、起因

今天有业务找过来说,说自己就存了几十个string类型的数据,为什么集群上节点的内存使用率就占到了200多M。224MB 按照redis的string类型数据的内存占用来计算的话,怎么都算不到这么多呀,但查看监控之后发现还真是这样。

之后查看 info memory 如下(redis-4.0.12):

10.142.38.35:33423 # Memory used_memory:236490880 used_memory_human:225.54M used_memory_rss:3207168 used_memory_rss_human:3.06M used_memory_peak:236491904 used_memory_peak_human:225.54M used_memory_peak_perc:100.00% used_memory_overhead:236408208 used_memory_startup:1458456 used_memory_dataset:82672 used_memory_dataset_perc:0.04% total_system_memory:270763003904 total_system_memory_human:252.17G used_memory_lua:37888 used_memory_lua_human:37.00K maxmemory:2147483648 maxmemory_human:2.00G maxmemory_policy:noeviction mem_fragmentation_ratio:0.01 mem_allocator:jemalloc-4.0.3 active_defrag_running:0 lazyfree_pending_objects:0

刚开始以为是系统内存错误或者显示错误,就重启了一下从库,发现问题仍然存在,而且这个内存碎片率还更低了!!!

二、分析

2.1 自己分析

used_memory_human:225.54M used_memory_rss_human:3.06M mem_fragmentation_ratio:0.01

used_memory表示redis分配器分配的容量,used_memory_rss 表示从操作系统看,redis进程占用的容量,mem_fragmentation_ratio = used_memory_rss / used_memory,表示内存碎片率

一般情况下used_memory_rss 比used_memory 大一些,也就是mem_fragmentation_ratio比1大一些

又看了两个 # Keyspace 为空的redis-4.0.12版本的redis集群节点:

10.142.94.35:34424(第二个集群的节点) # Memory used_memory:236566592 used_memory_human:225.61M used_memory_rss:5611520 used_memory_rss_human:5.35M maxmemory_policy:allkeys-lru mem_fragmentation_ratio:0.02 mem_allocator:jemalloc-4.0.3 10.114.33.8:34381 (第三个集群的节点) # Memory used_memory:1575224 used_memory_human:1.50M used_memory_rss:6922240 used_memory_rss_human:6.60M maxmemory_policy:allkeys-lru mem_fragmentation_ratio:4.39 mem_allocator:jemalloc-4.0.3

现在更迷了,,,,

初步怀疑是redis进程运行时存的其他的一些数据,让业务试试多刷一些数据进去,这些200M的数据占比应该会随着实际存储数据的增大而减少,可能这个问题就不足为奇了。

2.2 搜索资料

再次百度查看这个信息的含义:

used_memory是Redis使用的内存总量,它包含了实际缓存占用的内存和Redis自身运行所占用的内存(如元数据、lua)。它是由Redis使用内存分配器分配的内存,所以这个数据并没有把内存碎片浪费掉的内存给统计进去。

used_memory_rss的rss是Resident Set Size的缩写,表示该进程所占物理内存的大小,是操作系统分配给Redis实例的内存大小。除了用户定义的数据和内部开销以外,used_memory_rss指标还包含了内存碎片的开销,内存碎片是由操作系统低效的分配/回收物理内存导致的。

若是内存碎片率低于1的话,说明Redis内存分配超出了物理内存,操作系统正在进行内存交换。内存交换会引起非常明显的响应延迟。

根据上述定义来说的话,这个 used_memory_rss 应该就是包含 used_memory 的呀,所以这个 used_memory_rss 应该始终比 used_memory 大才对呀。但是为什么我们使用 info memory 看到的信息却是 used_memory 比 used_memory_rss 大200多M(集群一和集群二)??

百度无果之后决定上机器一探究竟…

2.3 分析进程占用

登录运行redis实例的机器,使用 top -c查看

13734 work 20 0 366m 15m 1100 S 0.0 0.0 156:30.36 /home/work/redis/redis-4.0.12/bin/redis-server *:33420 [cluster]

进程为13734

使用pmap分析内存

pmap 使用:https://blog.csdn.net/sunny05296/article/details/65975710

[work@redis05 ~]$ pmap -x 13734 13734: /home/work/redis/redis-4.0.12/bin/redis-server *:33420 [cluster] Address Kbytes RSS Dirty Mode Mapping 0000000000400000 1280 452 0 r-x-- redis-server 000000000073f000 24 24 24 rw--- redis-server 0000000000745000 92 88 88 rw--- [ anon ] 000000000080a000 132 56 56 rw--- [ anon ] 0000003926a00000 128 40 0 r-x-- ld-2.12.so 0000003926c1f000 4 4 4 r---- ld-2.12.so 0000003926c20000 4 4 4 rw--- ld-2.12.so 0000003926c21000 4 4 4 rw--- [ anon ] 0000003926e00000 8 4 0 r-x-- libdl-2.12.so 0000003926e02000 2048 0 0 ----- libdl-2.12.so 0000003927002000 4 4 4 r---- libdl-2.12.so 0000003927003000 4 4 4 rw--- libdl-2.12.so 0000003927200000 1576 500 0 r-x-- libc-2.12.so 000000392738a000 2048 0 0 ----- libc-2.12.so 000000392758a000 16 16 8 r---- libc-2.12.so 000000392758e000 4 4 4 rw--- libc-2.12.so 000000392758f000 20 16 16 rw--- [ anon ] 0000003927600000 92 72 0 r-x-- libpthread-2.12.so 0000003927617000 2048 0 0 ----- libpthread-2.12.so 0000003927817000 4 4 4 r---- libpthread-2.12.so 0000003927818000 4 4 4 rw--- libpthread-2.12.so 0000003927819000 16 4 4 rw--- [ anon ] 0000003928200000 524 20 0 r-x-- libm-2.12.so 0000003928283000 2044 0 0 ----- libm-2.12.so 0000003928482000 4 4 4 r---- libm-2.12.so 0000003928483000 4 4 4 rw--- libm-2.12.so 00007f546f000000 229376 11600 11600 rw--- [ anon ] 00007f547d1fd000 4 0 0 ----- [ anon ] 00007f547d1fe000 10240 8 8 rw--- [ anon ] 00007f547dbfe000 4 0 0 ----- [ anon ] 00007f547dbff000 10240 8 8 rw--- [ anon ] 00007f547e5ff000 4 0 0 ----- [ anon ] 00007f547e600000 12288 1056 1044 rw--- [ anon ] 00007f547f200000 2048 1588 1588 rw--- [ anon ] 00007f547f56f000 96836 0 0 r---- locale-archive 00007f5485400000 2048 28 28 rw--- [ anon ] 00007f5485748000 16 16 16 rw--- [ anon ] 00007f5485761000 4 4 4 rw--- [ anon ] 00007fffe2c4c000 84 28 28 rw--- [ stack ] 00007fffe2d88000 4 4 0 r-x-- [ anon ] ffffffffff600000 4 0 0 r-x-- [ anon ] ---------------- ------ ------ ------ total kB 375336 15672 14560

观察到其中的一行

Address Kbytes RSS Dirty Mode Mapping 00007f546f000000 229376 11592 11592 rw--- [ anon ]

各字段的意思:

Kbytes: size of map in kilobytes 映像大小(KB) RSS: resident set size in kilobytes 驻留集大小(保留内存大小)(KB) Dirty: dirty pages (both shared and private) in kilobytes 脏页大小(KB) Mode: permissions on map 映像权限: r=read, w=write, x=execute, s=shared, p=private (copy on write) Mapping: file backing the map , or '[ anon ]' for allocated memory, or '[ stack ]' for the program stack. 占用内存的文件,[anon]为已分配内存,[stack]为程序堆栈 Offset: offset into the file 文件偏移 Device: device name (major:minor) 设备名

再从不同的角度查看

[work@redis05 ~]$ pmap -d 13734 13734: /home/work/redis/redis-4.0.12/bin/redis-server *:33420 [cluster] Address Kbytes Mode Offset Device Mapping 0000000000400000 1280 r-x-- 0000000000000000 0fd:00002 redis-server 000000000073f000 24 rw--- 000000000013f000 0fd:00002 redis-server 0000000000745000 92 rw--- 0000000000083000 0fd:00000 libm-2.12.so ... 00007f546f000000 229376 rw--- 0000000000000000 000:00000 [ anon ] 00007f547e600000 12288 rw--- 0000000000000000 000:00000 [ anon ] 00007f547f200000 2048 rw--- 0000000000000000 000:00000 [ anon ] 00007f547f56f000 96836 r---- 0000000000000000 0fd:00000 locale-archive 00007f5485400000 2048 rw--- 00007fffe2c4c000 84 rw--- 0000000000000000 000:00000 [ stack ] mapped: 375336K writeable/private: 266652K shared: 0K

最后一行的值

mapped:表示该进程映射的虚拟地址空间大小,也就是该进程预先分配的虚拟内存大小,即ps出的vsz writeable/private:表示进程所占用的私有地址空间大小,也就是该进程实际使用的内存大小 shared:表示进程和其他进程共享的内存大小

其中Kbytes: size of map in kilobytes 映像大小(KB) 为229376KB,也就是224MB,跟之前那个节点的used_memory_human很接近。所以接下来分析一下这个里面是些啥,应该就能知道内存被啥占用了。

2.4 确认问题

在分析之前,想确定一下是不是因为这个文件导致占用那么大内存,所以想对比看一下之前看的第三个集群的节点的内存占用

[work@redis04 ~]$ pmap -x 24971 24971: /home/work/redis/redis-4.0.12/bin/redis-server *:34381 [cluster] Address Kbytes RSS Dirty Mode Mapping 0000000000400000 1280 380 0 r-x-- redis-server 000000000073f000 24 24 24 rw--- redis-server 0000000000745000 92 92 92 rw--- [ anon ] 000000000138a000 132 56 56 rw--- [ anon ] 000000334ca00000 128 36 0 r-x-- ld-2.12.so 000000334cc1f000 4 4 4 r---- ld-2.12.so 000000334cc20000 4 4 4 rw--- ld-2.12.so 000000334cc21000 4 4 4 rw--- [ anon ] 000000334ce00000 8 4 0 r-x-- libdl-2.12.so 000000334ce02000 2048 0 0 ----- libdl-2.12.so 000000334d002000 4 4 4 r---- libdl-2.12.so 000000334d003000 4 4 4 rw--- libdl-2.12.so 000000334d200000 1572 520 0 r-x-- libc-2.12.so 000000334d389000 2048 0 0 ----- libc-2.12.so 000000334d589000 16 16 8 r---- libc-2.12.so 000000334d58d000 4 4 4 rw--- libc-2.12.so 000000334d58e000 20 16 16 rw--- [ anon ] 000000334d600000 92 72 0 r-x-- libpthread-2.12.so 000000334d617000 2048 0 0 ----- libpthread-2.12.so 000000334d817000 4 4 4 r---- libpthread-2.12.so 000000334d818000 4 4 4 rw--- libpthread-2.12.so 000000334d819000 16 4 4 rw--- [ anon ] 000000334de00000 524 20 0 r-x-- libm-2.12.so 000000334de83000 2044 0 0 ----- libm-2.12.so 000000334e082000 4 4 4 r---- libm-2.12.so 000000334e083000 4 4 4 rw--- libm-2.12.so 00007f3dea7fd000 4 0 0 ----- [ anon ] 00007f3dea7fe000 10240 8 8 rw--- [ anon ] 00007f3deb1fe000 4 0 0 ----- [ anon ] 00007f3deb1ff000 10240 8 8 rw--- [ anon ] 00007f3debbff000 4 0 0 ----- [ anon ] 00007f3debc00000 12288 4096 4096 rw--- [ anon ] 00007f3dec800000 2048 1292 1292 rw--- [ anon ] 00007f3decb6f000 96836 0 0 r---- locale-archive 00007f3df2a00000 2048 28 28 rw--- [ anon ] 00007f3df2dc0000 16 16 16 rw--- [ anon ] 00007f3df2dd5000 4 4 4 rw--- [ anon ] 00007fffd567c000 84 24 24 rw--- [ stack ] 00007fffd57f6000 4 4 0 r-x-- [ anon ] ffffffffff600000 4 0 0 r-x-- [ anon ] ---------------- ------ ------ ------ total kB 145956 6760 5716

通过观察发现,真的只多了下面这行数据

00007f546f000000 229376 11600 11600 rw--- [ anon ]

再查看之前第二个集群的节点,也找到了这行数据

[work@redis31 ~]$ pmap -x 1709 1709: /home/work/redis/redis-4.0.12/bin/redis-server *:34424 [cluster] Address Kbytes RSS Dirty Mode Mapping 0000000000400000 1280 1152 0 r-x-- redis-server 000000000073f000 24 24 24 rw--- redis-server 0000000000745000 92 92 92 rw--- [ anon ] 00000000024ec000 132 56 56 rw--- [ anon ] 00007fe1dda00000 229376 476 476 rw--- [ anon ]

现在可以确定

0000000000400000 1280 1152 0 r-x-- redis-server 000000000073f000 24 24 24 rw--- redis-server

redis-server和其他占用内存的文件都是redis进程运行所必备的,但是会不会多这200多MB,就是因为

00007fe1dda00000 229376 476 476 rw--- [ anon ]

这里的数据导致的。

那怎么知道这里的数据存的是啥呢?

三、寻找问题的根源

3.1 通过Address映像起始地址来找

后面的 Mapping 显示的是 [ anon ] ,表示已分配内存。 Address表示映像起始地址,看样子应该从这里下手。

但是不知道这个要怎么分析,先来看一下其他占用内存的文件的是啥,在哪里?

locale-archive

语言和地区定义文件放在/usr/share/i18n/locales,字符集定义在/usr/share/i18n/charmaps。组合之后的locale信息放在/usr/lib/locale/locale-archive里,是二进制文件。

libc-2.17.so libpthread-2.17.so ld-2.17.so

libc泛指C函数库,而用的最广,功能最强的当然是GNU LibC,简称glibc,各发行版Linux用的就是glibc。 libc的动态库版本叫做是libc.so,通常是/usr/lib/libc.so,glibc的libc.so其实不是.so文件,而是一个ld Script,这没有关系,gnu ld 会正确处理的。

在网上找了好久资料,发现并没有讲怎么根据这个映像起始地址Address来找文件数据的资料。问了导师,他说这个应该找不到吧。

仔细想想,这个映像起始地址应该是虚拟内存用来作标识用的,可能只是系统分配在那里的一些内存,要进程真正用到的时候才会使用。而且应该是找不到对应的内存的,就算找到了,也无法知晓这块内存存储的是什么数据。

所以,想通过Address找内存存储的数据应该是行不通的。

接下来想通过redis进程运行时的函数调用看看有没有新发现。

3.2 通过redis进程函数系统调用来找

strace常用来跟踪进程执行时的系统调用和所接收的信号。

perf,可以分析程序运行期间发生的硬件事件,比如instructions retired、processor clock cycles等;也可以分析软件时间,比如page fault和进程切换。 perf是一款综合性分析工具,大到系统全局性性能,再小到进程线程级别,甚至到函数及汇编级别。 perf提供了十八般武器,可以拿大刀大卸八块,也可以拿起手术刀细致分析。

pstack 命令可显示每个进程的栈跟踪,主要用于排查程序死循环、死锁。 执行之后看到如下的信息:

[work@c3-ai-all-store-i2p-redis03 ~]$ pstack 175291 Thread 4 (Thread 0x7feeb2bff700 (LWP 175297)): #0 0x000000392760b5bc in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0 #1 0x000000000047b026 in bioProcessBackgroundJobs () #2 0x00000039276079d1 in start_thread () from /lib64/libpthread.so.0 #3 0x00000039272e88fd in clone () from /lib64/libc.so.6 Thread 3 (Thread 0x7feeb21fe700 (LWP 175298)): #0 0x000000392760b5bc in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0 #1 0x000000000047b026 in bioProcessBackgroundJobs () #2 0x00000039276079d1 in start_thread () from /lib64/libpthread.so.0 #3 0x00000039272e88fd in clone () from /lib64/libc.so.6 Thread 2 (Thread 0x7feeb17fd700 (LWP 175299)): #0 0x000000392760b5bc in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0 #1 0x000000000047b026 in bioProcessBackgroundJobs () #2 0x00000039276079d1 in start_thread () from /lib64/libpthread.so.0 #3 0x00000039272e88fd in clone () from /lib64/libc.so.6 Thread 1 (Thread 0x7feeb9375740 (LWP 175291)): #0 0x00000039272e8ef3 in epoll_wait () from /lib64/libc.so.6 #1 0x0000000000424c0e in aeProcessEvents () #2 0x00000000004250ab in aeMain () #3 0x000000000042d760 in main ()

四、后续

尝试了好久都没找到这块内存存的到底是啥东西,,(好尴尬 >_< )

就先把它当做redis-4.0.12版启动后必须分配的redis进程内存占用吧。

等以后有机会再请教一下大佬这个内存要怎么找,后者看看redis官网有没有解释这224M的系统占用是怎么用的。

最新回复(0)