Redis内存相关知识

18 篇文章 1 订阅

内存使用统计

使用info memory命令

127.0.0.1:6380> info memory
# Memory
used_memory:841672
used_memory_human:821.95K
used_memory_rss:9965568
used_memory_rss_human:9.50M
used_memory_peak:1869392
used_memory_peak_human:1.78M
total_system_memory:1019572224
total_system_memory_human:972.34M
used_memory_lua:37888
used_memory_lua_human:37.00K
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
mem_fragmentation_ratio:11.84
mem_allocator:jemalloc-4.0.3

重点指标

  • used_memory_rss:9965568
    used_memory_rss_human:9.50M

    从操作系统角度显示Redis进行战胜的物理内在总量

  • used_memory:841672
    used_memory_human:821.95K

    Redis分配器分配的内存总量,也就是内部存储所有的数内存占用量

  • mem_fragmentation_ratio:11.84

    mem_fragmentation_ratio是used_memory_rss和used_memory的比值,

    当mem_fragmentation_ratio>1时, 说明used_memory_rss-used_memory多出的部分内存并没有用于数据存储, 而是被内存碎片所消耗, 如果两者相差很大, 说明碎片率严重。
    当mem_fragmentation_ratio<1时, 这种情况一般出现在操作系统把Redis内存交换(Swap) 到硬盘导致, 出现这种情况时要格外关注, 由于硬盘速度远远慢于内存, Redis性能会变得很差, 甚至僵死。

内存消耗划分

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

对象内存

对象内存是Redis内存占用最大的一块, 存储着用户所有的数据。

缓冲内存

客户端缓冲区

指所有接入到Redis服务器TCP连接的输入输出缓冲,包括

  • 普通客户端

    一般普通客户端的内存消耗可以忽略不计, 但是当有大量慢连接客户端接入时这部分内存消耗就不能忽略了, 可以设置maxclients做限制。 特别是当使用大量数据输出的命令且数据无法及时推送给客户端时,如monitor命令, 容易造成Redis服务器内存突然飙升。

  • 从客户端

    当主从节点之间网络延迟较高或主节点挂载大量从节点时这部分内存消耗将占用很大一部分, 建议主节点挂载的从节点不要多于2个, 主从节点不要部署在较差的网络环境下, 如异地跨机房环境, 防止复制客户端连接缓慢造成溢出。

  • 订阅客户端

    当使用发布订阅功能时, 连接客户端使用单独的输出缓冲区,当订阅服务的消息生产快于消费速度时, 输出缓冲区会产生积压造成输出缓冲区空间溢出。

复制积压缓冲区

Redis在2.8版本之后提供了一个可重用的固定大小缓冲区用于实现部分复制功能,

AOF缓冲区

这部分空间用于在Redis重写期间保存最近的写入命令,AOF缓冲区空间消耗用户无法控制, 消耗的内存取决于
AOF重写时间和写入命令量, 这部分空间占用通常很小。

内存碎片

jemalloc针对碎片化问题专门做了优化, 一般不会存在过度碎片化的问题, 正常的碎片率( mem_fragmentation_ratio) 在1.03左右。 但是当存储的数据长短差异较大时, 以下场景容易出现高内存碎片问题:

  • 频繁做更新操作, 例如频繁对已存在的键执行append、 setrange等更新
    操作。
  • 大量过期键删除, 键对象过期删除后, 释放的空间无法得到充分利
    用, 导致碎片率上升。

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

  • 数据对齐: 在条件允许的情况下尽量做数据对齐, 比如数据尽量采用数字类型或者固定长度字符串等, 但是这要视具体的业务而定, 有些场景无法做到。
  • 安全重启: 重启节点可以做到内存碎片重新整理, 因此可以利用高可用架构, 如Sentinel或Cluster, 将碎片率过高的主节点转换为从节点, 进行安全重启。

子进程内存消耗

子进程内存消耗主要指执行AOF/RDB重写时Redis创建的子进程内存消耗。

Linux具有写时复制技术(copy-on-write), 父子进程会共享相同的物理内存页,所以不需要多一倍的物理内存,当父进程处理写请求时会对需要修改的页复制出一份副本完成写操作, 而子进程依然读取fork时整个父进程的内存快照。

Linux Kernel在2.6.38内核增加了Transparent Huge Pages(THP) 机制,然开启THP可以降低fork子进程的速度, 但之后copy-on-write期间复制内存页的单位从4KB变为2MB, 如果父进程有大量写命令, 会加重内存拷贝量, 从而造成过度内存消耗。

建议:

  • Redis产生的子进程并不需要消耗1倍的父进程内存, 实际消耗根据期间写入命令量决定, 但是依然要预留出一些内存防止溢出。
  • 需要设置sysctl vm.overcommit_memory=1允许内核可以分配所有的物理内存, 防止Redis进程执行fork时因系统剩余内存不足而失败。
  • 排查当前系统是否支持并开启THP, 如果开启建议关闭, 防止copy-onwrite期间内存过度消耗。

内存管理

设置内存上限

使用maxmemory参数限制最大可用内存。maxmemory限制的是Redis实际使用的内存量, 也就是used_memory统计项对应的内存。由于内存碎片率的存在, 实际消耗的内存可能会比maxmemory设置的更大, 实际使用时要小心这部分内存溢出。

目的:

  • 缓存场景,内存超出maxmemory时使用LRU等删除策略释放空间。
  • 防止内存超过服务器物理内存。

Redis默认无限使用服务器内存, 为防止极端情况下导致系统内存耗尽, 建议所有的Redis进程都要配置maxmemory。

动态调整内存上限
127.0.0.1:6379> config set maxmemory 100mb
OK
127.0.0.1:6379> info memory
# Memory
maxmemory:104857600
maxmemory_human:100.00M

在保证物理内存可用的情况下, 系统中所有Redis实例可以调整maxmemory参数来达到自由伸缩内存的目的。

内存回收策略
删除过期键对象
  • 惰性删除

    当客户端读取带有超时属性的键时, 如果已经超过键设置的过期时间, 会执行删除操作并返回空。但是单独用这种方式存在内存泄露的问题。

  • 定时删除

    1) 定时任务在每个数据库空间随机检查20个键, 当发现过期时删除对应的键。

    2) 如果超过检查数25%的键过期, 循环执行回收逻辑直到不足25%或运行超时为止, 慢模式下超时时间为25毫秒。
    3) 如果之前回收键逻辑超时, 则在Redis触发内部事件之前再次以快模式运行回收过期键任务, 快模式下超时时间为1毫秒且2秒内只能运行1次。
    4) 快慢两种模式内部删除逻辑相同, 只是执行的超时时间不同。

内存溢出控制策略

内存使用到达maxmemory上限触发

1) noeviction: 默认策略, 不会删除任何数据, 拒绝所有写入操作并返回客户端错误信息( error) OOM command not allowed when used memory, 此时Redis只响应读操作。
2) volatile-lru: 根据LRU算法删除设置了超时属性( expire) 的键, 直到腾出足够空间为止。 如果没有可删除的键对象, 回退到noeviction策略。
3) allkeys-lru: 根据LRU算法删除键, 不管数据有没有设置超时属性,直到腾出足够空间为止。
4) allkeys-random: 随机删除所有键, 直到腾出足够空间为止。
5) volatile-random: 随机删除过期键, 直到腾出足够空间为止。
6) volatile-ttl: 根据键值对象的ttl属性, 删除最近将要过期数据。 如果没有, 回退到noeviction策略。

内存溢出控制策略可以采用config set maxmemory-policy {policy}动态配置。

每次Redis执行命令时如果设置了maxmemory参数, 都会尝试执行回收内存操作。 当Redis一直工作在内存溢出(used_memory>maxmemory) 的状态下且设置非noeviction策略时, 会频繁地触发回收内存的操作, 影响Redis服务器的性能。频繁执行回收内存成本很高, 主要包括查找可回收键和删除键的开销, 如果当前Redis有从节点, 回收内存操作对应的删除命令会同步到从节点, 导致写放大的问题。

线上建议:

建议线上Redis内存工作在maxmemory>used_memory状态下, 避免频繁内存回收开销。

对于需要收缩Redis内存的场景, 可以通过调小maxmemory来实现快速回收。注意, 此操作会导致数据丢失
和短暂的阻塞问题, 一般在缓存场景下使用。

内存优化

  • 可以使用scan+object idletime命令批量查询哪些键长时间未被访问, 找出长时间不访问的键进行清理, 可降低内存占用。

  • 高并发写入场景中, 在条件允许的情况下, 建议字符串长度控制在39字节以内, 减少创建redisObject内存分配次数, 从而提高性能。

  • 优化字符串:尽量减少字符串频繁修改操作如append、 setrange, 改为直接使用set修改字符串, 降低预分配带来的内存浪费和内存碎片化。

内存优化的思路概括:

  • 精简键值对大小, 键值字面量精简, 使用高效二进制序列化工具。
  • 使用对象共享池优化小整数对象。
  • 数据优先使用整数, 比字符串类型更节省空间。
  • 优化字符串使用, 避免预分配造成的内存浪费。
  • 使用ziplist压缩编码优化hash、 list等结构, 注重效率和空间的平衡。
  • 使用intset编码优化整数集合。
  • 使用ziplist编码的hash结构降低小对象链规模
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值