Redis 内存分析

293 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第24天,点击查看活动详情

Redis 内存优化

  • 部分属性的说明:

    属性名属性说明
    used_memoryRedis 分配器分配的内存总量,就是内部存储的所有数据内存占用量。
    used_memory_human以可读的格式返回 used_memory。
    used_memory_rss以操作系统的角度显示 Redis 进程占用的物理内存总量。
    used_memory_rss_human以可读的格式返回 used_memory_rss。
    used_memory_peak内存使用的峰值。
    used_memory_peak_human以可读的格式返回 used_memory_peak。
    used_memory_peak_perc使用内存达到峰值内存的百分比。
    used_memory_overheadRedis 为了维护数据集的内部机制所需的内存开销,包括所有客户端输出缓冲区、查询缓冲区、AOF 重写缓冲区和主从复制的 backlog。
    used_memory_startupRedis 服务器启动时消耗的内存
    used_memory_dataset数据占用的内存大小。
    used_memory_dataset_perc数据占用的内存大小的百分比。
    total_system_memory操作系统内存。
    total_system_memory_human以更直观的格式显示操作系统内存。
    used_memory_luaLua 脚本存储占用的内存。
    used_memory_lua_human以更直观的格式显示 Lua 脚本存储占用的内存。
    used_memory_scriptsLua 脚本使用的字节数。
    used_memory_scripts_human以更直观的格式显示 Lua 脚本使用的字节数。
    maxmemoryRedis 实例的最大内存配置。
    maxmemory_human以更直观的格式显示 Redis 实例的最大内存配置。
    maxmemory_policy当达到 maxmemory 时的淘汰策略。

从上面内存的信息可以发现很多信息,比如当前节点的内存碎片率(used_memory_rss/used_memory)、使用的内存分配器(jemalloc-5.1.0)、Redis 实例的最大内存配置(maxmemory)。了解这些信息之后,就可以在实际开发中有针对性的进行内存优化。

一般会消耗 Redis 内存的有:自身内存、键值对象占用、缓冲区内存占用以及内存碎片的占用。有时候 Redis 中的一些空进程自身也会消耗内存,但是消耗非常少,乃至于忽略不计,所以一般在优化内存的时候,可以不考虑此项。

接下来我们一起来学习一些简单的内存优化的方案。

  • 缩减键值对对象

    如果大家翻看过 Redis 源码可以知道,Redis 使用对象来表示数据库中的键和值,每次在 Redis 的数据库中创建一个新的键值对时(比如 set msg "hello redis"),都会至少创建两个对象:一个对象用作创建键值对的键(键对象),另一个对象用作创建键值对的(值对象)。这一块的内存消耗最大,它存储着用户所有的数据。所以此时我们在进行数据数存储时,我们就可以考虑一些优化 key 和 value 的方案了。

    a. key 和 value 的最直接的方式就是缩短 key 和 value 的长度。key 要在符合业务描述完整性的情况下越短越好; value 的存储,把业务对象序列化成二进制数组放入 Redis 数据库中,在序列化工具上选择更有效的工具来降低字节数组的大小。

    b. 精简业务,避免存储不必要的属性和元素存储。

    我们可以通过以下命令进行大键的搜索,然后可以删除其中的额外字符来缩短它。

    redis-cli --bigkeys
    
  • 检测并删除不用的键

    Redis 有自己的内存管理和内存淘汰策略,可以定期管理淘汰一些过期的 key ,用于保持 Redis 内存中对象的活跃度和释放优化内存。但是内存淘汰策略是内存达到 maxmemory 上限才会触发。所以平时我们可以通过 scan + object idletime key + lua 脚本来删除一些过期的键或者是过期时间很长的键。

    ✨ 说明:scan 命令可以批量的遍历所有的 key,直到返回 0 遍历结束。object idletime key 返回 key 空闲时间。然后使用 lua 脚本来删除这些长时间不访问的键,从而达到降低内存的效果。

  • 内存碎片优化

    在进行内存碎片优化之前,我们需要先弄清楚内存碎片产生的原因,然后给出相应的解决方案。下面是两个简单的优化方案:

    a. 数据对其:比如在条件允许的情况下,我们可以使用数据时才去定长或者固定字符的长度等等。

    b. 安全重启:比如利用高可用架构,将碎片率过高的主节点变成从节点,然后安全重启,重启节点可以做到内存碎片的重新整理。

  • 编码优化

    a. 使用 list、hash、set、zset 时尽可能使用 ziplist 编码,好处是减少内存占用,坏处是操作变慢。比如同样的数据,使用 ziplist 编码的 hash 类型存储比 string 类型节约内存。

    b. 编码类型是在 Redis 内部自动完成,这种转换不可逆,转换只能从小内存向大内存转换,Redis 不支持编码回退,主要是数据增删频繁时,数据向压缩编码转换非常消耗 cpu,得不偿失。

  • 控制键的数量

    当使用 Redis 存储大量数据时,通常会存在大量键,过多的键同样会消耗大量内存。比如如果键很多的话,我们可以将键进行分组,然后映射到多个 hash 结构中以降低键的数量。

以上是一些简单的优化方案,在实际开发中,得根据实际的应用场景进行相应的优化,而且上面都是垂直层面的优化,也可以通过水平扩展来增加,比如增加内存、增加服务器等等。

官网从以下几个方面简单的介绍了内存优化:

  • 小的聚合类型数据的特殊编码处理
  • 使用 32 位的 Redis
  • 位级别和字级别的操作
  • 尽可能使用散列表(hashes)
  • 使用散列结构高效存储抽象的键值对
  • 内存分配