操作系统分配给Redis的内存为6GB,指标used_memory_human发现只有4GB用于存储数据。为什么会这样?为什么不能保存数据?使用CONFIGSETmaxmemory100mb或者在redis.conf配置文件中设置maxmemory100mbRedis内存使用限制。当达到最大内存值时,会触发内存淘汰策略删除数据。另外,当key到达过期时间,Redis会有以下两种删除过期数据的策略:后台定时任务选择部分数据删除;懒惰删除。具体原理请移步《??Redis 的过期数据删除那些事??》。假设Redis实例保存了5GB的数据,现在删除了2GB的数据,那么Redis进程占用的内存会不会减少呢?(也称为RSS,进程消耗的内存页数)。答案是:它可能仍然占用大约5GB内存,即使Redis数据只占用大约3GB。必须设置maxmemory,否则Redis会继续为新写入的数据分配内存,分配失败会导致应用程序报错,当然不会造成宕机。释放的内存去了哪里?明明删除了数据,用top命令查看,为什么还是占用那么多内存?所有的记忆都去哪儿了?使用infomemory命令获取Redis内存相关指标。我列出了几个重要的数据:127.0.0.1:6379>infomemory#memoryused_memory:1132832//Redis存储数据所占用的内存量used_memory_human:1.08M//人类可读形式返回内存使用总量_memory_rss:2977792//从操作系统的角度来看,进程占用的物理内存总量used_memory_rss_human:2.84M//used_memory_rss的可读性模式显示used_memory_peak:1183808//内存使用的最大值,表示used_memory的峰值used_memory_peak_human:1.13M//以可读格式返回used_memory_peak的值used_memory_lua:37888//Lua引擎消耗的内存量。used_memory_lua_human:37.00Kmaxmemory:2147483648//可以使用的最大内存值,以字节为单位。maxmemory_human:2.00G//可读形式maxmemory_policy:noeviction//内存淘汰策略//used_memory_rss/used_memory的比值,代表内存碎片率mem_fragmentation_ratio:2.79Redis进程内存消耗主要由以下部分组成:Redis的占用内存自己的创业公司。存储对象数据内存。缓冲区内存:主要由client-output-buffer-limit客户端输出缓冲区、copybacklog缓冲区、AOF缓冲区组成。内存碎片。内存占用Redis自身的空进程占用的内存很小,可以忽略不计,对象内存是最大的一部分,里面存放了所有的数据。如果缓冲区有大流量的场景很容易失控,导致Redis内存不稳定,需要注意。内存碎片过多导致有可用空间,但无法存储数据。Fragmentation=used_memory_rss实际使用的物理内存(RSS值)除以used_memory实际存储的数据内存。什么是内存碎片?内存碎片会导致内存空间空闲,但不能存储数据。比如和一个漂亮的小姐姐去电影院看电影,肯定是要在一起的。假设现在有8个座位,已售出4张票,还有4张可供购买。不过巧合的是,买票的人很奇怪,都是隔一个座位买票。虽然还有4个位子,但是两个位子连起来的票是买不到的,礼物蟹!什么导致内存碎片?主要原因有两个:内存分配器的分配策略。键值对的大小不同以及删除操作:Redis频繁进行更新操作,删除大量过期数据,释放的空间(不够连续)无法重复使用,导致碎片率增加.接下来,我将讨论实际发生的原因……内存分配器的分配策略Redis使用jemalloc作为默认的内存分配器,还有其他可选的分配器:glibc和tcmalloc。内存分配器不能按需分配,而是使用固定范围的内存块进行分配。比如8字节、16字节……、2KB、4KB,当申请内存最接近固定值时,jemalloc会分配最接近固定值的空间。这样就会出现内存碎片。比如程序只需要1.5KB,而内存分配器会分配2KB空间,那么这0.5KB就是碎片。这样做的目的是减少内存分配的次数。比如你申请22字节的空间来存放数据,jemalloc会分配32字节。如果后面需要写入10个字节,则不需要向操作系统申请空间。可以使用先前请求的32个字节。当一个键被删除时,Redis不会立即将内存归还给操作系统。发生这种情况是因为底层内存分配器的管理。例如,大多数已删除的键仍与其他有效键分配在同一内存页中。此外,为了重用空闲内存块,分配器删除了原始5GB数据中的2GB。再次向实例添加数据时,Redis的RSS会保持稳定,不会增加太多。因为内存分配器基本上重新使用了之前删除释放的2GB内存。键值对大小不同和删除操作由于内存分配器是按照固定大小分配内存,所以分配的内存空间通常会大于实际数据占用的大小,会造成碎片,减少存储器的存储效率。另外,键值对的频繁修改和删除导致内存空间的扩大和释放。比如一个原本占用32个字节的字符串,现在修改为一个占用20个字节的字符串,那么释放出来的12个字节就是freespace。如果下一次数据存储请求需要申请一个13字节的字符串,刚刚释放的12字节空间就不能使用,造成碎片。分片最大的问题:空间总量足够大,但是这些内存并不连续,可能存不下数据。内存碎片怎么解决?首先要判断是否发生了内存碎片。关注前面INFO内存命令提示的mem_fragmentation_ratio指标,表示内存碎片率:mem_fragmentation_ratio=used_memory_rss/used_memory。如果1<碎片率<1.5,可以认为是合理的,如果大于1.5,说明发生了碎片。超过50%,我们需要采取一些措施来解决过度碎片化的问题。重启大法最简单粗暴的方式就是重启。如果未启用持久性,数据将丢失。如果开启持久化,需要使用RDB或者AOF来恢复数据。如果只有一个实例,数据量大会导致恢复阶段长时间无法提供服务,高可用性会大大降低。我应该怎么办?码哥漂亮男孩。自动清理内存碎片既然你叫我漂亮,我就给你所有的帮助,告诉你终极杀招:Redis4.0版本之后,提供了清理内存碎片的机制。如何清洗?很简单,还是上面的例子,我要买两张连在一起的电影票。与人交流,换位,便可实现。对于Redis来说,当一个连续的内存空间被分割成若干个不连续的空间时,操作系统首先将数据一个一个移动拼接在一起,释放掉原来数据占用的空间,形成一个连续的空闲内存空间。.如下图所示:DebrisCleanup自动清理内存碎片的代价。自动清理固然好,但也不要乱来。操作系统需要消耗资源将数据移动到新的位置,然后释放原来的空间。Redis操作数据的指令是单线程的,所以在数据复制和移动时,只有清理碎片后才能处理请求,造成性能损失。如何避免清洗杂物对性能的影响,实现自动化清洗?问得好,用下面两个参数来控制内存碎片清理和结束的时机,避免占用过多CPU,减少清理碎片对Redis处理请求的性能影响。启用自动内存碎片整理CONFIGSETactivedefragyes这只是为了启用自动碎片整理。清洗时,必须同时满足以下两个条件才能触发清洗操作。active-defrag-ignore-bytes200mb清理条件:当内存碎片占用内存达到200MB时,开始清理。active-defrag-threshold-lower20:内存碎片空间超过系统分配给Redis空间的20%,开始清理。避免对性能的影响有了清理时间,还需要控制清理对性能的影响。一二设置先分配清理碎片占用的CPU资源,保证碎片可以正常清理,避免对Redis处理请求造成性能影响。active-defrag-cycle-min20:自动碎片整理过程中占用CPU时间的比例不低于20%,以保证清理任务能够正常进行。active-defrag-cycle-max50:自动清理进程占用CPU时间比例不能高于75%。如果超过,会立即停止清理,避免阻塞Redis造成高延迟。小结如果发现Redis存储数据占用的内存比操作系统分配给Redis的内存小很多,但是数据却存不下,可能是内存碎片很多。使用infomemory命令查看内存碎片mem_fragmentation_ratio指标是否正常。然后我们启用自动清理并合理设置清理时间和CPU资源占用。该机制涉及内存复制,这对Redis性能构成潜在风险。如果Redis性能变慢,检查是否是清理碎片导致的。如果是这样,请降低active-defrag-cycle-max的值。本文转载自微信公众号“码哥字节”,可通过以下二维码关注。转载本文请联系码哥字节公众号。
