Redis 内存消耗

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

Redis 将所有的数据都保存在内存中, 内存跟硬盘相比成本昂贵, 如何高效利用 Redis 内存变得非常重要.

高效利用 Redis 内存 :

  • ✔️内存消耗在哪里?
  • 内存怎么管理?
  • 如何优化内存使用?

内存消耗在哪里

有些内存消耗是必须的, 有些则可以通过参数调整和合理使用来规避内存浪费.

内存消耗可以分为主进程自身消耗子进程消耗.

内存使用统计

通过info memory 命令查看内存统计数据.

该命令返回的具体参数值含义 : info 命令

重点关注三个参数 :

  • used_memory_rss 表示 Redis 占用的总内存大小. 这个值和 top , ps 等命令的输出一致. 在理想情况下, used_memory_rss 的值应该只比 used_memory 稍微高一点儿.
  • used_memory_rss_human 同 used_memory, 不过是带单位的, 可读性好
  • used_memory : 表示 Redis 中数据占据的内存大小. 单位 byte
  • used_memory_human 同 used_memory, 不过是带单位的, 可读性好
  • mem_fragmentation_ratio 表示内存碎片率, 是 used_memory_rss 和 used_memory 的比值. 正常值为 1.03.
    • 当 比值 >1 时, 说明 used_memory_rss - used_memory 多出的部分内存并没有用于数据存储, 而是被内存碎片所消耗. 如果该值 > 1.5, 说明碎片率严重,需要及时清理.
    • 当 它们的比值 <1 时, 这种情况一般出现在操作系统把 Redis 内存交换到硬盘导致, 需要格外关注, 由于硬盘速度远远慢于内存, Redis 性能会变得很差, 甚至僵死.

系统 qa 环境的 redis mem_fragmentation_ratio = 0.98

关于这个比值的说明:

www.monseng.com/2370967.htm…

www.jianshu.com/p/cd1a70d00…

cloud.tencent.com/developer/a…

www.modb.pro/db/48654

进程内存消耗

Redis 进程内消耗主要包括:自身内存+对象内存+缓冲内存+内存碎片.

自身内存

Redis 空进程自身内存消耗非常少, 通常 used_memory_rss 在 3MB 左右, used_memory 在 800KB 左右, 所以一个空的 Redis 消耗的内存可以忽略不计.

对象内存

对象内存是 Redis 内存占用最大的一块, 对象内存存储着用户所有的数据. 这些数据都是键值对类型.

每次创建键值对时, 至少创建两个类型的对象key 对象value 对象. 对象内存消耗可以简单理解为 sizeof(keys)+sizeof(values).

key 对象都是字符串, 所以避免使用过长的键可以节约内存.

value 对象比较复杂, 每种 value 对象类型根据使用规模不同, 占用内存不同. 在使用时一定要合理预估并监控 value 对象占用情况, 避免内存溢出.

缓冲内存

缓冲内存主要包括:客户端缓冲, 复制积压缓冲区, AOF 缓冲区.

  • 客户端缓冲 : 所有接入到 Redis 服务器 TCP 连接的输入输出缓冲. 详见 Redis 客户端
  • 复制积压缓冲区:一个可重用的固定大小缓冲区用于实现部分复制功能. 详见 Redis 主从复制
  • AOF 缓冲区:这部分空间用于在 Redis 重写期间保存最近的写入命令. 详见 Redis 的持久化

内存碎片

内存分配策略

Redis 默认的内存分配器采用 jemalloc, 可选的分配器还有:glibc, tcmalloc.

内存分配器为了更好地管理和重复利用内存, 分配内存策略一般采用固定范围的内存块进行分配.

例如 jemalloc 在 64 位系统中将内存空间划分为:小, 大, 巨大三个范围. 每个范围内又划分为多个小的内存块单位, 默认 64 位系统 jemalloc 的划分方式如下:

  • 小:[8byte], [16byte, 32byte, 48byte, ..., 128byte], [192byte, 256byte, ..., 512byte], [768byte, 1024byte, ..., 3840byte]
  • 大:[4KB, 8KB, 12KB, ..., 4072KB]
  • 巨大:[4MB, 8MB, 12MB, ...]

比如当保存 5KB 对象时, jemalloc 可能会采用 8KB 的块存储, 而剩下的 3KB 空间变为了内存碎片, 不能再分配给其他对象存储. 内存碎片问题虽然是所有内存服务的通病, 但是 jemalloc 针对碎片化问题专门做了优化, 一般不会存在过度碎片化的问题, 正常的碎片率(mem_fragmentation_ratio)在 1.03 左右.

内存碎片的产生

除了分配时导致内存碎片,以下场景也容易内存碎片问题:

频繁做更新操作, 例如频繁对已存在的键执行 append, setrange 等更新操作. key 在扩容时为了保证内存的连续性,可能会发生内存位置迁移,而这个过程中,很容易出现内存碎片.

键值对进行修改时,可能会变大也会变小,相应的就会占用额外空间或者释放不用的空间。

\

大量键被删除或者因过期删除, 释放的空间无法得到充分利用, 导致碎片率上升.

为什么删除了很多 key,但是内存却没有变小呢?

Redis有自己的内存分配器,当数据删除后,释放的内存空间由 Redis 自己的内存分配器管理,并没有立即将内存返回给操作系统,所以对于操作系统而言,仍然认为 Redis 占用了内存。这样的好处是,减少 Redis 向系统申请内存分配的次数,提升 Redis 自身性能。

内存碎片的解决

出现高内存碎片问题时常见的解决方式:

  • 数据对齐:在条件允许的情况下尽量做数据对齐, 比如数据尽量采用数字类型或者固定长度字符串等.
  • 重启:重启节点可以做到内存碎片重新整理, 因此可以利用高可用架构,将碎片率过高的主节点转换为从节点, 然后重启.
  • memory purge 手动碎片整理,该命令会阻塞主进程,生产环境慎用.
  • activedefrag开启自动碎片整理功能, 默认关闭,计划清理碎片时需手动开启. 参考:my.oschina.net/zxiaofan/bl…

猜你喜欢

转载自juejin.im/post/7132099854397865991