Redis的内存

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

  1. 配置文件

使用 maxmemory 配置参数设置内存上限

  1. 命令
config set maxmemory bytes

2.2 内存回收策略

2.2.1 删除过期键对象

key 的过期属性(若设置过)保存在过期字典中,维护每个 key 精准的过期删除机制会导致消耗大量 CPU,因此 Redis 采用惰性删除和定时任务删除机制实现过期 key 的内存回收。

  1. 惰性删除

在客户端读取带有超时属性的键时,如果已经超过键值设置的过期时间,则删除并返回空,存在内存泄漏问题。

  1. 定时任务删除

Redis内部维护了一个定时任务,默认是每秒运行10次,采用自适应算法,根据 key 的过期比例、使用快慢两种速率模式回收 key,流程如下:

Redis的内存

  • 定时任务在每个数据库空间采用慢模式随机检查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 禁止使用共享对象池

3.4 字符串优化

3.5 编码优化

上一篇:Cache 与Memory架构及数据交互


下一篇:spark性能优化(一)