Redis基础-内存管理
一、redis内存
查看内存消耗:
info memory
# Memory
#redis分配器分配的内存量,实际存储数据的内存总量
used_memory:2050616
#可读格式返回
used_memory_human:1.96M
#操作系统角度,Redis进程占用内存
used_memory_rss:3477504
#可读格式返回
used_memory_rss_human:3.32M
#历史使用顶峰
used_memory_peak:2234840
used_memory_peak_human:2.13M
#使用内存达到峰值内存的百分比,即(used_memory/ used_memory_peak) *100%
used_memory_peak_perc:91.76%
#Redis为了维护数据集的内部机制所需的内存开销,包括所有客户端输出缓冲区、查询缓冲区、AOF重写缓冲区和主从复制的backlog。
used_memory_overhead:2014632
#Redis服务器启动时消耗的内存
used_memory_startup:876736
#数据占用的内存大小,即used_memory-used_memory_overhead
used_memory_dataset:35984
#数据占用的内存大小的百分比,100%*(used_memory_dataset/(used_memory-used_memory_startup))
used_memory_dataset_perc:3.07%
allocator_allocated:2017776
allocator_active:3439616
allocator_resident:3439616
#系统内存总量
total_system_memory:3973533696
total_system_memory_human:3.70G
#lua引擎使用内存
used_memory_lua:37888
used_memory_lua_human:37.00K
#Lua脚本使用的字节数
used_memory_scripts:0
used_memory_scripts_human:0B
#缓存的lua脚本
number_of_cached_scripts:0
#最大内存配置
maxmemory:0
maxmemory_human:0B
#内存溢出,淘汰策略
maxmemory_policy:noeviction
allocator_frag_ratio:1.70
allocator_frag_bytes:1421840
allocator_rss_ratio:1.00
allocator_rss_bytes:0
rss_overhead_ratio:1.01
rss_overhead_bytes:37888
#碎片率,used_memory_rss/ used_memory,正常情况下稍大于1。低于1,Redis实例可能会把部分数据交换到硬盘上,内存交换会严重影响Redis的性能,所以应该增加可用物理内存。大于1.5表示碎片过多。额外碎片的产生是由于Redis释放了内存块,但内存分配器并没有返回内存给操作系统,这个内存分配器是在编译时指定的,可以是libc、jemalloc或者tcmalloc。
mem_fragmentation_ratio:1.72
#碎片大小
mem_fragmentation_bytes:1459728
mem_not_counted_for_evict:0
mem_replication_backlog:1052656
mem_clients_slaves:34096
mem_clients_normal:51144
mem_aof_buffer:0
#内存分配器,Redis支持glibc’s malloc、jemalloc11、tcmalloc几种不同的内存分配器,每个分配器在内存分配和碎片上都有不同的实现。不建议普通管理员修改Redis默认内存分配器,因为这需要完全理解这几种内存分配器的差异,也要重新编译Redis。
mem_allocator:libc
#0表示没有活动的defrag任务正在运行,1表示有活动的defrag任务正在运行(defrag:表示内存碎片整理)
active_defrag_running:0
#0表示不存在延迟释放的挂起对象
lazyfree_pending_objects:0
redis使用的内存主要分为两大块:
- used_memory
- 自身内存:很低,几百k
- 对象内存:即存储的对象所占用的内存
- 缓冲区内存:复制缓冲区、aof缓冲区、客户端缓冲区等
- used_memory_rss-used_memory:内存碎片,即操作系统分配给redis的内存和redis实际使用的内存的差值。比如我存一个字符串 hello, 一共5个字节,但实际操作系统分配给这个key的内存大小要大于5个字节。
二、缓冲区内存
输入缓冲区:
客户端发送命令给redis,redis在执行这些命令前,这些命令会被临时存放到一个输入缓冲区,也就是之前说的命令队列(由于redis是单线程的所以命令要放在队列中排队顺序执行)。
命令存在于输入缓冲区的时间很短,因为redis处理命令很快,所以输入缓冲区占用的内存基本看到都是0
但是如果redis很忙,或者key是一个bigkey,那么输入缓冲区就会较长时间的被占用。
输入缓冲区不能占用超过1G(无法配置修改),否则就会断开和客户端的连接。
2.1.客户端缓冲区
分为
普通客户端(redis-cli,php或者python执行命令时的客户端等)
slave客户端(从节点同步主节点时需要一个slave客户端接收主节点同步过来的命令)
pubsub客户端(发布订阅时会生成客户端)
这三个是输出缓冲区(redis服务返回命令所经过的缓冲区)
如果客户端请求的是bigkey,那么普通客户端缓冲区就很容易占用大量内存。
2.2.复制缓冲区和AOF缓冲区
复制缓冲区是用于主从节点的部分复制而生的,master节点会将写命令写入到复制缓冲区,然后再从复制缓冲区发送给从节点以进行数据同步。为了防止网络抖动造成不必要的全量复制可以将这个缓冲区调大点。这个缓冲区大小默认是1Mb,可以调大到100M的样子。
AOF缓冲区分为aof缓冲区(用于aof日志的生成)和aof重写缓冲区(用于aof重写),这两个缓冲区是没有容量限制的。
2.3.内存碎片
内存碎片的处理最好解决方式是安全重启。
2.4.子进程内存消耗
像bgsave和bgrewriteaof的时候,就会fork生成一个子进程从而消耗内存。
三、内存管理
Redis的内存管理主要包括两部分:
- 过期键的删除
- 内存溢出的控制策略
设置内存上线:
maxmemory 6GB
动态配置:
config set maxmemory 6GB
config rewrite # 配置重启,写入配置文件中
1.1、过期键的删除
对于过期key的回收,redis会使用定期删除 + 惰性删除两种策略结合。
定期删除
默认每隔一段时间(100秒)就随机抽一些设置了过期时间的key,去检查是否过期,过期了就删了。
为啥不扫描全部设置了过期时间的key呢
假如Redis里面所有的key都有过期时间,都扫描一遍?那太恐怖了,而且我们线上基本上也都是会设置一定的过期时间的。全扫描跟你去查数据库不带where条件不走索引全表扫描一样,100s一次,Redis累都累死了。
如果一直没随机到很多key,里面不就存在大量的无效key了?
惰性删除:
即当用户查询到某个key的时候,redis会查看这个key是否过期。过期则不返回给用户,直接删掉。
1.2、内存溢出的控制策略
当内存溢出的时候(redis中的kv超过了maxmemory内存上限)会触发5种相应的策略。
-
Noeviction:默认策略,不会删除任何数据,但会拒绝所有写入操作并返回错误信息“OOM command not allowed when used memory”。但是会相应读操作。
-
Volatile-lru:根据LRU算法(最近最少使用算法)删除设置了超时时间的key(设置了expire的key)直到腾出足够空间为止。如果没有可删除的key则退回到Noeviction默认策略
-
Volatile-ttl:根据key的ttl属性,删除最近即将过期的key,如果没有就回退到默认策略。
-
Allkeys-lru:根据LRU算法删除key,不管数据有没有设置超时,直到腾出足够空间为止。
-
Allkeys-random:随机删除所有key直到腾出足够空间为止
-
volatile-random:随机删除过期key直到腾出足够空间为止
可以在配置文件的设置 maxmemory-policy 来控制。
value设计
1.拒绝bigkey
string类型超过10k就可以看成bigkey
hash,list,set和zset元素个数超过5000个可以看成bigkey
这个要看不同公司有不同标准
bigkey的危害:
1.网络阻塞
举个例子,有一个key大小是5M,单机的带宽是千兆也就是128Mb,那么当对这1个key的QPS达到25的时候带宽就会占满从而影响了其他很多的业务。
2.慢查询
例如做一些hgetall,lrange,zrange等操作
因为redis是一个单线程,如果单个key的大小非常大,就会阻塞后面的命令的执行。
3.节点数据不均衡
这个问题是出现在分布式redis中的,如果key过大,那么在每个节点的key个数差不多的情况下出现数据倾斜的问题。
4.反序列化消耗
如果string类型存的是一个序列话的bigkey,那么获取的时候,你的应用程序就要对他进行反序列化,这是一个比较吃CPU的操作
如何发现bigkey
可以使用 redis-cli --bigkeys(他也是进行数据的扫描,建议在slave节点中进行)
或者通过网络流量监控,客户端监控(例如python去查redis发现超时)。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库