Redis学习--理解内存

作者: 何何与呵呵呵 | 来源:发表于2019-04-09 08:56 被阅读17次
    内存消耗
    • 内存使用统计
      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学习--理解内存

        本文链接:https://www.haomeiwen.com/subject/sijmbqtx.html