本文已参与「新人创作礼」活动,一起开启掘金创作之路。
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 中数据占据的内存大小
. 单位 byteused_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 性能会变得很差, 甚至僵死.
- 当 比值 >1 时, 说明 used_memory_rss - used_memory 多出的部分内存并没有用于数据存储, 而是被内存碎片所消耗.
系统 qa 环境的 redis mem_fragmentation_ratio = 0.98
关于这个比值的说明:
cloud.tencent.com/developer/a…
进程内存消耗
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…