Redis 所有数据都存在内存中,所以高效利用 Redis 内存至关重要
1. 内存消耗
1.1 内存使用统计
使用以下命令查看内存相关指标:
info memory
-
used_memory:存储数据所占用的内存总量
used_memory_human:人类可读的used_memory
-
used_memory_rss:Redis进程占用的物理内存总量
-
used_memory_peak:内存使用的最大值,used_memory的峰值
used_memory_peak_human:人类可读的used_memory_peak
-
used_memory_lua:lua引擎所占用的内存大小
-
mem_fragmentation_ratio:内存碎片率
-
mem_allocator:Redis使用的内存分配器,默认是jemalloc
1.2 内存消耗划分
自身内存 + 对象内存 + 缓冲内存 + 内存碎片,其中自身内存消耗很少。
1.2.1 对象内存
对象内存是 Redis 内存中占用最大一块,存储着所有的用户的数据。Redis 所有的数据都采用的是 key-value 型数据类型,每次创建键值对的时候,都要创建两个对象,key 对象和 value 对象。key 对象都是字符串,value 对象的存储方式,五种数据类型 String、List、Hash、Set、Zset。每种存储方式在使用的时候长度、数据类型不同,则占用的内存就不同。
1.2.2 缓冲内存
主要包括:客户端缓冲、复制积压缓冲区、AOF缓冲区
-
客户端缓冲:
客户端缓冲指的是所有接入到 Redis 服务器 TCP 连接的输入输出缓冲。输入缓冲无法控制,最大为1G;输出缓冲通过
client-output-buffer-limit
参数进行控制 -
复制积压缓冲:2.8版本之后提供的可重用的固定大小缓冲区用于实现部分复制功能,默认1MB,可通过
repl-backlog-size
参数进行控制,主要是在主从同步时用到 -
AOF缓冲区:AOF 持久化用的,会先写入到缓冲区,然后根据响应的策略向磁盘进行同步,消耗的内存取决于写入的命令量和重写时间,通常很小
1.2.3 内存碎片
类似于内部碎片,分配出去的内存没有被充分利用,多出来的那一部分。
出现高内存碎片问题的情况:大量的更新操作,比如append、setrange;大量的过期键删除,释放的空间无法得到有效利用
解决办法:数据对齐,安全重启(高可用/主从切换)
1.3 子进程内存消耗
AOF / RDB 重写时 fork 出的子进程会占用内存
2. 内存管理
2.1 设置内存上限
Redis默认是无限使用内存。所以在使用的时候尽量的去配置
maxmemory
- 配置文件
使用 maxmemory
配置参数设置内存上限
- 命令
config set maxmemory bytes
2.2 内存回收策略
2.2.1 删除过期键对象
key 的过期属性(若设置过)保存在过期字典中,维护每个 key 精准的过期删除机制会导致消耗大量 CPU,因此 Redis 采用惰性删除和定时任务删除机制实现过期 key 的内存回收。
- 惰性删除
在客户端读取带有超时属性的键时,如果已经超过键值设置的过期时间,则删除并返回空,存在内存泄漏问题。
- 定时任务删除
Redis内部维护了一个定时任务,默认是每秒运行10次,采用自适应算法,根据 key 的过期比例、使用快慢两种速率模式回收 key,流程如下:
- 定时任务在每个数据库空间采用慢模式随机检查20个键,当发现过期时,删除对应的键。
- 如果超过检查数25%的键过期,循环执行回收逻辑直到不足25%或 运行超时为止,慢模式下超时时间为25毫秒。
- 如果之前回收键逻辑超时,则在Redis触发内部事件之前再次以快模式运行回收过期键任务,快模式下超时时间为1毫秒且2秒内只能运行1次。
- 快慢两种模式内部删除逻辑相同,只是执行的超时时间不同。
2.2.2 内存溢出控制策略
Redis 所用内存达到 maxmemory
上限时会触发相应的溢出控制策略,由 maxmemory-policy
配置文件参数控制:
- noeviction:默认策略,不删除任何数据,写操作时返回错误信息
- volatile-lru:使用 LRU 算法删除设置了 expire 的 key
- allkeys-lru:使用 LRU 算法删除 key
- volatile-lfu:使用 LFU 算法删除设置了 expire 的 key
- allkeys-lfu:使用 LFU 算法删除 key
- volatile-random:随机删除删除设置了 expire 的 key
- allkeys-random:随机删除 key
- volatile-ttl:根据键值对象的 tll,删除最近要过期的数据
3. 优化内存
3.1 redisObject对象
Redis 存储的所有 value 对象在内部定义为 redisObject 结构体
typedef struct redisObject {
unsigned type:4; //保存信息的类型
unsigned encoding:4;//保存信息的编码方式
unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) */
int refcount;//引用次数
void *ptr;//保存的指针
} robj
3.2 缩减键值对象
缩减 key 和 value 的长度:
- key:能够完整描述业务的情况下,key 越短越好
- value:常见需求是把业务对象序列化成二进制放入 Redis,因此要先在业务上精简对象,还要选择高效的序列化工具,最后再压缩一下
3.3 共享对象池
Redis 内部维护一个[0-9999]的整数对象池,可以复用,节约内存。
注意:当开启 maxmemory
和 LRU 淘汰策略后,Redis 禁止使用共享对象池