一、Redis内存碎片统计
其中,info命令可以显示redis服务器的许多信息,包括服务器基本信息、CPU、内存、持久化、客户端连接信息等等;memory是参数,表示只显示内存相关的信息。 返回结果中比较重要的几个说明如下:
(1)used_memory:Redis分配器分配的内存总量(单位是字节),包括使用的虚拟内存(即swap);Redis分配器后面会介绍。used_memory_human只是显示更友好。
(2)used_memory_rss:Redis进程占据操作系统的内存(单位是字节),与top及ps命令看到的值是一致的;除了分配器分配的内存之外,used_memory_rss还包括进程运行本身需要的内存、内存碎片等,但是不包括虚拟内存。
因此,used_memory和used_memory_rss,前者是从Redis角度得到的量,后者是从操作系统角度得到的量。二者之所以有所不同,一方面是因为内存碎片和Redis进程运行需要占用内存,使得前者可能比后者小,另一方面虚拟内存的存在,使得前者可能比后者大。
由于在实际应用中,Redis的数据量会比较大,此时进程运行占用的内存与Redis数据量和内存碎片相比,都会小得多;因此used_memory_rss和used_memory的比例,便成了衡量Redis内存碎片率的参数;这个参数就是mem_fragmentation_ratio。
(3)mem_fragmentation_ratio:内存碎片比率,该值是used_memory_rss / used_memory的比值。 mem_fragmentation_ratio一般大于1,且该值越大,内存碎片比例越大。mem_fragmentation_ratio<1,说明Redis使用了虚拟内存,由于虚拟内存的媒介是磁盘,比内存速度要慢很多,当这种情况出现时,应该及时排查,如果内存不足应该及时处理,如增加Redis节点、增加Redis服务器的内存、优化应用等。
一般来说,mem_fragmentation_ratio在1.03左右是比较健康的状态(对于jemalloc来说);上面截图中的mem_fragmentation_ratio值很大,是因为还没有向Redis中存入数据,Redis进程本身运行的内存使得used_memory_rss 比used_memory大得多。
(4)mem_allocator:Redis使用的内存分配器,在编译时指定;可以是 libc 、jemalloc或者tcmalloc,默认是jemalloc;截图中使用的便是默认的jemalloc。
二、内存碎片处理
内存碎片是Redis在分配、回收物理内存过程中产生的。例如,如果对数据的更改频繁,而且数据之间的大小相差很大,可能导致redis释放的空间在物理内存中并没有释放,但redis又无法有效利用,这就形成了内存碎片。内存碎片不会统计在used_memory中。
内存碎片的产生与对数据进行的操作、数据的特点等都有关;此外,与使用的内存分配器也有关系:如果内存分配器设计合理,可以尽可能的减少内存碎片的产生。
分配器分配内存的规则
jemalloc作为Redis的默认内存分配器,在减小内存碎片方面做的相对比较好。jemalloc在64位系统中,将内存空间划分为小、大、巨大三个范围;每个范围内又划分了许多小的内存块单位;当Redis存储数据时,会选择大小最合适的内存块进行存储。
jemalloc划分的内存单元如下图所示:
内存碎片形成原因
1.内存分配策略引发:redis默认使用jemalloc分配器分配内存。jemalloc每次分配固定空间,例如写入数据申请10K空间,jemalloc会为其分配16K空间。这样分配优点是下次写入>=6K数据不用再申请内存空间,缺点是造成内存浪费。
2.修改内存中数据:修改包括增加和减少内存占用。例如原来占用16K空间的数据,增加或减少1K都会造成内存碎片
内存碎片通常分为:内部碎片和外部碎片(另一种描述)
- 内部碎片是由于采用固定大小的内存分区,当一个进程不能完全使用分给它的固定内存区域时就产生了内部碎片,通常内部碎片难以完全避免;
- 外部碎片是由于某些未分配的连续内存区域太小,以至于不能满足任意进程的内存分配请求,从而不能被进程利用的内存区域。
(解决思想)现在普遍采用的段页式内存分配方式就是将进程的内存区域分为不同的段,然后将每一段由多个固定大小的页组成。通过页表机制,使段内的页可以不必连续处于同一内存区域,从而减少了外部碎片,然而同一页内仍然可能存在少量的内部碎片,只是一页的内存空间本就较小,从而使可能存在的内部碎片也较少。
出现高内存碎片问题的情况:当存储的数据长短差异较大的时候,以下场景容易出现高内存碎片问题
1)、频繁更新,对已经存在的key进行append setrange操作
2)、大量过期键删除,键对象过期删除后,释放的 空间无法得到拆分利用
这些操作可能导致redis释放的空间在物理内存中并没有释放,但redis又无法有效利用,这就形成了内存碎片。内存碎片不会统计在used_memory中。
解决办法:
如果Redis服务器中的内存碎片已经很大,可以通过安全重启的方式减小内存碎片;重启之后,Redis重新从RDB,AOF中读取数据,在内存中进行重排,为每个数据重新选择合适的内存单元,减小内存碎片(简单暴力,不推荐)。
redis4.0以上可以使用新增指令来手动回收内存碎片。
config set activedefrag yes
具体什么时候清理需要通过下面两个参数控制:
#内存碎片占用空间达到500mb的时候开始清理
config set active-defrag-ignore-bytes 500mb
#内存碎片率大于1.5的时候开始清理
config set active-defrag-threshold-lower 50
通过 Redis 自动内存碎片清理机制可能会对 Redis 的性能产生影响,我们可以通过下面两个参数来减少对 Redis 性能的影响:
#内存碎片清理所占用CPU 时间的比例不低于20%
config set active-defrag-cycle-min 20
#内存碎片清理所占用CPU 时间的比例不高于50%
config set active-defrag-cycle-max 50