Redis学习--理解内存

内存消耗
  • 内存使用统计
    info memory
info memory详细解释

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

  • 内存消耗划分
    自身内存+对象内存(内存占用最大的一块)+缓冲内存+内存碎片(数据对齐,安全重启)


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

1.用于缓存场景,当超出内存上限maxmemory时使用LRU等删除策略释放空间。
2.防止所用内存超过服务器物理内存。

服务器分配4个4GB的Redis进程
  • 动态调整内存上限
    Redis的内存上限可以通过config set maxmemory进行动态修改,即修改最大可用内存。
  • 内存回收策略

删除到达过期时间的键对象。
内存使用达到maxmemory上限时触发内存溢出控制策略。

1.删除过期键对象
惰性删除:惰性删除用于当客户端读取带有超时属性的键时,如果已经超过键设置的过期时间,会执行删除操作并返回空
定时任务删除:Redis内部维护一个定时任务,默认每秒运行10次(通过配置hz控制),采用了自适应算法,根据键的过期比例、使用快慢两种速率模式回收键.


定时任务删除过期键逻辑
  • 内存溢出控制策略(Redis支持6种策略)
    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策略。
内存优化
  • redisObject对象


    redisObject内部结构
  • 缩减键值对象
    key长度:如在设计键时,在完整描述业务情况下,键值越短越好。如user:{uid}:friends:notify:{fid}可以简化为u:{uid}:fs:nt:{fid}。
    value长度:值对象缩减比较复杂,常见需求是把业务对象序列化成二进制数组放入Redis。


    Java常见序列化组件占用内存空间对比

    使用GZIP压缩后的json可降低约60%的空间。推荐使用Google的Snappy压缩工具,在特定的压缩率情况下效率远远高于GZIP等传统压缩工具

  • 共享对象池
    共享对象池是指Redis内部维护[0-9999]的整数对象池。创建大量的整数类型redisObject存在内存开销,每个redisObject内部结构至少占16字节,甚至超过了整数自身空间消耗。所以Redis内存维护一个[0-9999]的整数对象池,用于节约内存。
    注:当设置maxmemory并启用LRU相关淘汰策略如:volatile-lru,allkeys-lru时,Redis禁止使用共享对象池
  • 字符串优化
    1.字符串结构


    字符串结构体SDS

    2.预分配机制


    字符串内存预分配测试

    尽量减少字符串频繁修改操作如append、setrange,改为直接使用set修改字符串,降低预分配带来的内存浪费和内存碎片化。
    3.字符串重构
    使用字符串和hash结构测试内存表现
  • 编码优化
    type和encoding对应关系表

    hash、list、set、zset内部编码可以按照需要进行配置
    ziplist编码:
    ziplist内部结构

    使用ziplist压缩编码的原则:追求空间和时间的平衡。(降低内存占用)
    intset编码:
    intset编码是集合(set)类型编码的一种,内部表现为存储有序、不重复的整数集。
    intset内部结构
  • 控制键的数量


    客户端维护哈希分组降低键规模

    hash的field可用于记录原始key字符串,方便哈希查找
    hash的value保存原始值对象,确保不要超过hash-max-ziplist-value限制。

总结

1)Redis实际内存消耗主要包括:键值对象、缓冲区内存、内存碎片。
2)通过调整maxmemory控制Redis最大可用内存。当内存使用超出时,根据maxmemory-policy控制内存回收策略。
3)内存是相对宝贵的资源,通过合理的优化可以有效地降低内存的使用量,内存优化的思路包括:

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

你可能感兴趣的:(Redis学习--理解内存)