第八章 理解内存

2019-08-12  本文已影响0人  super_pcm

Redis所有的数据都存在内存中,当前内存虽然越来越便宜,但跟廉价的硬盘相比成本还是比较昂贵,因此如何高效利用Redis内存变得非常重要。高效利用Redis内存首先需要理解Redis内存消耗在哪里,如何管理内存,最后才能考虑如何优化内存。掌握这些知识后能够实现用更少的内存存储更多的数据,从而降低成本。本章主要内容如下:

8.1 内存消耗

理解Redis内存,首先需要掌握Redis内存消耗在哪些方面。有些内存消耗是必不可少的,而有些可以通过参数调整和合理使用来规避内存浪费。内存消耗可以分为进程自身消耗和子进程消耗。

8.1.1 内存使用统计

要查看redis自身的内存使用情况,可以使用info memory命令来获取。下面是部分的指标解析:

属性名 属性说明
used_memory_human 以可读的格式返回used_memory
used_memory_rss_human 从操作系统的绝度显示redis进程占用的物理内存总量
used_memory_peak_human 以可读的方式返回used_memory_peak
used_memory_lua_human Lua引擎所消耗的内存大小
mem_fragmentation_ratio used_memory_rss/used_memory的比值,表示内存碎片率
mem_allocator Redis所使用的内存分配器。默认是jemlloc

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

8.1.2 内存消耗划分

Redis进程内存的消耗主要包括:自身内存+对象内存+缓冲内存+内存碎片。
其中Redis自身内存消耗非常小,在空载的时候Redis的自身内存使用不超过1MB。Redis主要内存消耗的图如下所示:

Redis内存消耗划分

8.1.3 子进程内存消耗

子进程内存消耗主要指执行AOF/RDB重写时Redis创建的子进程内存消耗。由于Linux采用写时复制技术,Redis在fork操作的时候消耗的内存实际上很小。但是需要关注的一个参数是:Transparent Huge Pages(THP)。在Linux内核(2.6.38)
中增加这个参数后,写时复制的内存页的单位从4KB变成了2MB,如果父进程有大量写命令,会加重内存拷贝量,从而造成过度内存消耗。建议关闭。

子进程内存消耗总结如下:

8.2 内存管理

8.2.1 设置内存上限

Redis使用maxmemory参数限制最大可用内存。限制内存的目的主要有:

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

8.2.3 内存回收策略

Redis的内存回收机制主要体现在以下两个方面:

1. 删除过期键对象

Redis所有的键都可以设置过期属性,内部保存在过期字典中。由于进程内保存大量的键,维护每个键精准的过期删除机制会导致消耗大量的CPU,对于单线程的Redis来说成本过高,因此Redis采用惰性删除和定时任务删除机制实现过期键的内存回收。

定时任务删除过期键逻辑

流程说明:

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

2. 内存溢出控制策略

当Redis所用内存达到maxmemory上限时会触发相应的溢出控制策略。具体策略受maxmemory-policy参数控制,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策略。

内存溢出控制策略可以采用config set maxmemory-policy{policy}动态配置。Redis支持丰富的内存溢出应对策略,可以根据实际需求灵活定制,比如当设置volatile-lru策略时,保证具有过期属性的键可以根据LRU剔除,而未设置超时的键可以永久保留

8.3 内存优化

内存优化的点包括:

  1. redisObject对象,也就是要捣鼓redis数据结构的对象了,设置一个合适长度的encoding,可能会有一个较大的优化效果。
  2. 缩减键值对象,比如user:uid修改为u:uid;也可以使用序列化效率更高的工具。
  3. 共享对象池,redis内部维护一个[0-9999]的整数对象池。创建整数的对象的时候,直接新增一个共享连接即可,不需要创建新的对象。但是这在设置了maxmemory并启用LRU相关淘汰策略的时候,这个共享池是会被禁用的。
  4. 字符串优化。
  5. 编码的优化,使用ziplist压缩编码优化hash、list等结构,注重效率和空间的平衡。
  6. 控制键的数量,比如可以用hash结构来代替string结构,这样可以降低键的数量。
上一篇 下一篇

猜你喜欢

热点阅读