13-Redis内存和数据淘汰策略

Redis内存和数据淘汰策略

  • 下面是基于Redis 4.0.13版本,不同版本可以略有差异。

一、命令和配置

1.1 命令

  • info memory。该命令可以输出Redis内存相关信息。(info可以输出全部信息)
192.168.13.52:6379> 
192.168.13.52:6379> info  memory
# Memory
used_memory:1917336
used_memory_human:1.83M
used_memory_rss:11747328
used_memory_rss_human:11.20M
used_memory_peak:1918312
used_memory_peak_human:1.83M
used_memory_peak_perc:99.95%
used_memory_overhead:1901848
used_memory_startup:786696
used_memory_dataset:15488
used_memory_dataset_perc:1.37%
total_system_memory:135071277056
total_system_memory_human:125.79G
used_memory_lua:37888
used_memory_lua_human:37.00K
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
mem_fragmentation_ratio:6.13
mem_allocator:jemalloc-4.0.3
active_defrag_running:0
lazyfree_pending_objects:0
  • 参数含义如下:
输出参数含义
used_memory由Redis分配器分配的内存总量,包含了redis进程内部开销和数据占用的内存,以字节(byte)为单位
used_memory_human可读展示used_memory
used_memory_rss向操作系统申请的内存大小。与 top 、 ps等命令的输出一致
used_memory_rss_human可读展示used_memory_rss
used_memory_peakredis的内存消耗峰值(以字节为单位)
used_memory_peak_human可读展示used_memory_peak
used_memory_peak_perc使用内存达到峰值内存的百分比,(used_memory/ used_memory_peak) *100%
used_memory_overheadRedis为了维护数据集的内部机制所需的内存开销,包括所有客户端输出缓冲区、查询缓冲区、AOF重写缓冲区和主从复制的backlog
used_memory_startupRedis服务器启动时消耗的内存
used_memory_dataset数据占用的内存大小,即used_memory-used_memory_overhead
used_memory_dataset_perc数据占用的内存大小的百分比(used_memory_dataset/(used_memory-used_memory_startup))*100%
total_system_memory整个系统内存
total_system_memory_human可读展示total_system_memory
used_memory_luaLua脚本存储占用的内存
used_memory_lua_human可读展示Lua脚本存储占用的内存
maxmemoryRedis实例的最大内存配置
maxmemory_human可读展示Redis实例的最大内存配置
maxmemory_policy当达到maxmemory时的淘汰策略
mem_fragmentation_ratio碎片率,used_memory_rss/ used_memory
mem_allocator内存分配器
active_defrag_running表示是否有内存碎片整理活动 0表示没有,1表示有
lazyfree_pending_objects0表示不存在延迟释放的挂起对象

1.2 配置

  • 阅读redis.conf配置文件,找到MEMORY MANAGEMENT部分,可以看到关于内存管理的配置,默认都没有放开,主要配置是maxmemory,maxmemory-policy和maxmemory-samples。
############################## MEMORY MANAGEMENT ################################

# Set a memory usage limit to the specified amount of bytes.
# When the memory limit is reached Redis will try to remove keys
# according to the eviction policy selected (see maxmemory-policy).
# 限制内存使用,如果使用内存达到了限制会按照eviction策略来移除对应的key
#
# If Redis can't remove keys according to the policy, or if the policy is
# set to 'noeviction', Redis will start to reply with errors to commands
# that would use more memory, like SET, LPUSH, and so on, and will continue
# to reply to read-only commands like GET.
#如果不能移除key或者策略是noeviction,当操作set,lpush这样需要内存的命令时,redis会给命令返回错误提示需要更多的内存,
#如果是get这样的只读命令则会继续响应
#
# This option is usually useful when using Redis as an LRU or LFU cache, or to
# set a hard memory limit for an instance (using the 'noeviction' policy).
# 这个配置在降Redis当做LRU或者LFU缓存的时候非常有用,或者使用noeviction配置给redis限制一个硬性的内存限制
#
# WARNING: If you have slaves attached to an instance with maxmemory on,
# the size of the output buffers needed to feed the slaves are subtracted
# from the used memory count, so that network problems / resyncs will
# not trigger a loop where keys are evicted, and in turn the output
# buffer of slaves is full with DELs of keys evicted triggering the deletion
# of more keys, and so forth until the database is completely emptied.
# 如果你开起了maxmemory并对实例设置了slave,那么为了满足slave而需要的output buffers会从使用内存中扣减,这样
# 网络问题或者resyncs不会触发key的循环驱逐,反过来如果output buffers已满,删除键会触发删除更多的键,直到数据库清空
#
#
# In short... if you have slaves attached it is suggested that you set a lower
# limit for maxmemory so that there is some free RAM on the system for slave
# output buffers (but this is not needed if the policy is 'noeviction').
# 如果你对redis示例设置了slave,那么建议你设置一个小一点的maxmemory来保证系统有足够的RAM作为slave output buffers
#
#
# maxmemory <bytes>

# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
# 内存策略,当maxmemory到达之后,如何选择key来删除,是依据此策略,下面是可选的策略
#
#
# volatile-lru -> Evict using approximated LRU among the keys with an expire set.
#  对过期的key使用最近最少使用的策略淘汰
# allkeys-lru -> Evict any key using approximated LRU.
#  对全部的key使用最近最少使用的策略淘汰
# volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
#  对过期的key使用最近最不常用的策略淘汰
# allkeys-lfu -> Evict any key using approximated LFU.
#  对全部的key使用最近最不常用的策略淘汰
# volatile-random -> Remove a random key among the ones with an expire set.
#  对过期的key使用随机策略淘汰
# allkeys-random -> Remove a random key, any key.
#  对全部的key使用随机策略淘汰
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
#  淘汰ttl最短的key
# noeviction -> Don't evict anything, just return an error on write operations.
#  不淘汰任何key。内存满时,可进行读操作(get等),写操作将返回错误。这是Redis的默认模式,该模式下内存达到最大的时候,Redis就只能读不能写了。
# LRU means Least Recently Used
# LFU means Least Frequently Used
#
# LRU, LFU 和 volatile-ttl都是使用的近似算法
# Both LRU, LFU and volatile-ttl are implemented using approximated randomized algorithms.
#
# Note: with any of the above policies, Redis will return an error on write
#       operations, when there are no suitable keys for eviction.
#
#       At the date of writing these commands are: set setnx setex append
#       incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
#       sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
#       zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
#       getset mset msetnx exec sort
#
# The default is:
#
# maxmemory-policy noeviction

# LRU, LFU and minimal TTL algorithms are not precise algorithms but approximated
# algorithms (in order to save memory), so you can tune it for speed or
# accuracy. For default Redis will check five keys and pick the one that was
# used less recently, you can change the sample size using the following
# configuration directive.
# LRU, LFU 和 volatile-ttl都是使用的近似算法,不是精确算法(为了节约内存),你可以调整它的速度或准确性。默认Redis会选择5个key在其中选一个最近最少使用的,这个样本大小可以通过 maxmemory-samples设置,
#
#
# The default of 5 produces good enough results. 10 Approximates very closely
# true LRU but costs more CPU. 3 is faster but not very accurate.
# 默认值为5会产生足够好的结果, 10非常接近真正的LRU但成本更高的CPU, 3更快但不是很准确。
#
# maxmemory-samples 5
配置解析默认值配置示例
maxmemory最大内存限制0,表示不限制maxmemory 1GB/maxmemory 100MB
maxmemory-policy内存满后淘汰策略noevictionvolatile-lru
maxmemory-samples淘汰算法样本55
  • maxmemory限制的是info mempry中used_memory统计的值,因此Redis实际使用的内存值是可以超过maxmemory限制的内存值的。

1.3 修改配置

  • 方式一:修改配置文件,重启实例
  • 方式二:没有禁用config的情况下,使用config修改
192.168.13.53:6379> config set maxmemory 500MB
192.168.13.53:6379> info memory
maxmemory:524288000
maxmemory_human:500.00M
192.168.13.53:6379> config get maxmemory
1) "maxmemory"
2) "524288000"
192.168.13.53:6379> config  rewrite
OK
192.168.13.53:6379> 

//配置文件已经同步修改,追加在配置文件的最后
intellif@segment2:/opt/redis/redis-4.0.13$ tail -fn 2./redis.conf 
# Generated by CONFIG REWRITE
maxmemory 500mb

二、内存分析

2.1 参数分析

  • 根据info memory的输出,我们通常可以简单的分析。
输出参数含义
used_memory由Redis分配器分配的内存总量,包含了redis进程内部开销和数据占用的内存,以字节(byte)为单位
used_memory_rss向操作系统申请的内存大小。与 top 、 ps等命令的输出一致
mem_fragmentation_ratio碎片率,used_memory_rss/ used_memory

在这里插入图片描述

  • 当mem_fragmentation_ratio稍大于1时,是比较合理的,表示内存碎片率比较低,如果大于1.5说明Redis使用了实际需要内存的1.5倍,碎片接近50%,可以考虑稍微优化。
  • 当mem_fragmentation_ratio稍小于1时,需要的内存大于分配的内存,说明OS存在内存交换,可能会引起明显的延迟。

2.2 内存占用分析

内存占用描述备注
Redis数据占用内存K-V对占用内存used_memory_dataset和used_memory_dataset_perc查看数据内存大小和占比
Redis进程其他开销客户端输出缓冲区、查询缓冲区、AOF重写缓冲区和主从复制的backlogused_memory_overhead查看该部分大小
Redis进程其他开销如:Lua脚本存储占用的内存,子进程开销等used_memory_lua
内存碎片操作系统分配了但是Redis无法利用(碎片会导致mem_fragmentation_ratio>1)

三、内存优化

3.1 redisObject对象

  • Redis中五大数据类型都是使用redisObject来封装的,在其中加入了type、编码、LRU、等信息。
  • 如果对象是String且长度<=39,则只需要进行一次内存操作,过大则会增加内存分配的次数。
//可以清理空闲的key,Object命令可以查看redisObject对象的内部属性,比如idletime,refcount等
192.168.13.53:6379> set  key1 val1
OK
192.168.13.53:6379> OBJECT idletime key1
(integer) 5
192.168.13.53:6379> OBJECT idletime key1
(integer) 6 

3.2 缩短k-v

  • 设计上考虑尽量缩短key的长度
  • 可以考虑使用压缩算法,减小值占用空间

3.3 共享对象池

  • Redis内部对于0-9999的整数维护了整数对象池,(因为创建一个redisObject保存一个整数的开销比一个整数自身还要大)。在可以的情况下尽量使用整数对象,可以节约内存。
PS:共享对象池和与maxmemory+LRU策略冲突,因为在使用了maxmemory+LRU的策略时,Redis需要统计每个key的LRU并保存到redisObject的LRU字段,此时如果对象共享那么该字段也会共享,这样就无法做LRU淘汰,因此该模式下会禁用共享对象池
如果没有设置maxmemory,那么共享对象池是可以使用的,因为此时Redis不会做任何内存限制直到内存用尽,因此共享对象池可以正常工作。

另外只将整数做共享对象池也是性能和开销的折中,其他类型的判断相等复杂度是O(n)甚至O(N2),整数是O(1)

3.4 字符串优化

  • 避免字符串的频繁修改操作,而是直接使用set修改字符串,修改可能会因为预分配机制造成空间的浪费和内存碎片
  • 类似于json这样的键值对可以考虑采用hash存储,效率更高,获取时也支持hmget/hmset,而不是整体存取,性能更好(使用ziplist编码方式)

3.5 编码优化

//查看键值对的内部编码
192.168.13.53:6379> set addr shenzhen
OK
192.168.13.53:6379> OBJECT encoding addr
"embstr"
192.168.13.53:6379> 

在这里插入图片描述

//查看命令平均耗时:info Commandstats
  • 关于编码优化这一块,有很多内容,详情阅读参考文章[3]的8.3小节

3.6 减少键的数量

  • 比如100万个k-v对,可以考虑在业务上控制,使用1000个Hash存储,每一个Hash存储1000个k-v对,这样减少了外层键的数量可以节约内存,具体方法可以考虑散列,或者对key做一些拆分来实现。Hash使用ziplist编码,但是需要将长度控制在1000以内。
ziplist是连续内存结构,比较节约内存,适合存储小对象,查询复杂度是O(N2),因为存储数据比较短,所以性能能够满足。

四、参考

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值