写于:2019-05-19

更新:2020-04-25

# 前置问题

# Q1、设置的 key 明明已经过期了,为啥 仍然占用内存?

# Q2、设置的 key 明明还没有过期,为啥 这个 key 就不见了?

这两个问题,通过 Redis 内存回收机制能够得到完美的解答。

# Redis 内存回收机制

Redis 在两种情况下会回收 key 占用的内存:

  • 用户主动设置过期时间,时间到了,被回收
  • redis 中key达到了 redis 设置的 max_memory ,内存溢出。Redis 通过算法(如 LRU )进行Redis 内存回收。

# 1、删除达到过期时间的键对象

在 Redis 进程内保存了大量用户存入的 key ,针对设置了过期时间的 key ,如果每一个 key 进行精准的控制-当key过期立即回收空间,对于单线程的Reids来说成本太高。所以 Redis 中的 key 过期了,占用的内存空间并不会马上被回收。针对 key 的回收,Redis 官方给出了回答。

官方文档

Redis keys are expired in two ways: a passive way, and an active way.

Redis key 的过期有两种方式:

  • 被动方式
  • 主动方式

# 被动方式

A key is passively expired simply when some client tries to access it, and the key is found to be timed out.

当客户端进行某个 key 的get 访问,该key被设置了过期时间,如果此时 get 操作的时候 key 过期了,此时 Redis 将会针对该 key 占用的空间进行回收。

优点:该方式采用用户访问的方式进行空间回收,无需维护 key 的 TTL 链表数据。

缺点:如果存在大量已过期的 key 但是长时间内用户一直没有进行 get 方法,会导致过期 key 堆积在内存中,产生内存泄漏。

# 主动方式

Of course this is not enough as there are expired keys that will never be accessed again. These keys should be expired anyway, so periodically Redis tests a few keys at random among keys with an expire set. All the keys that are already expired are deleted from the keyspace.

Specifically this is what Redis does 10 times per second

1、Test 20 random keys from the set of keys with an associated expire.

2、Delete all the keys found expired.

3、If more than 25% of keys were expired, start again from step 1.

大意如下:

单纯的靠被动方式会导致有些已经过期的数据永远无法被回收。所以 Redis 定时随机获取一些设置了过期时间的数据进行过期判断,如果过期了就回收。

针对该操作 Redis 设置了一个周期为 10s 的定时任务,定时任务完成工作如下:

  • 1、随机获取20个设置了过期时间的key
  • 2、删除这20个key中已经过期的key
  • 3、如果被删除的key 占总的20个key的 25% ,就在一次执行 step1

小贴士

定时任务周期 10 s 可以在配置文件 redis.conf 中修改 hz 10hz 任意值

# 2、内存使用达到 max memory 上限时触发内存溢出控制策略

官方文档

The maxmemory configuration directive is used in order to configure Redis to use a specified amount of memory for the data set。

For example in order to configure a memory limit of 100 megabytes, the following directive can be used inside the redis.conf file.

maxmemory 100mb

Setting maxmemory to zero results into no memory limits. This is the default behavior for 64 bit systems, while 32 bit systems use an implicit memory limit of 3GB.

在 Redis 中可以通过在 redis.conf 中配置 maxmemory xxx 来配置 Redis 能够使用的最大内存空间。

在 64 位系统中 maxmemory 默认为 0 ,而 maxmemory 为 0时表示不对 Rredis 使用内存空间设限,其能够使用的内存空间为当前机器的最大内存空间。(32 位系统默认为3G)

对 maxmemory 设置了上限,当Redis 内存空间达到设置的上限时 Redis 需要有相应的策略进行处理,对此 Redis 提供了 6 种策略支持,如下:官方说明

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

小贴士

内存溢出控制策略 在 redis.conf 中 maxmemory-policy 进行配置,默认: maxmemory-policy noeviction

策略使用建议:

  • 建议1:策略使用 volatile-lru 。保证服务正常提供的前提下,优先删除不常用的待过期的 key 。

  • 建议2:频繁的策略回收会导致 Redis 性能下降设置阻塞,所以在设置 maxmemory 时,评估系统需要多大内存的 Redis 提供服务,尽量确保使用的内存 < max memory。

  • 建议3:能够 通过 confg 命令进行动态配置策略。如:confg set maxmeory-policy {policy}

# 问题解答

# Q1、设置的 key 明明已经过期了,为啥 仍然占用内存?

过期 key 需要通过 被动方式 或者 主动方式,才会真正的被回收。在此之前,过期数据仍然存在内存中。

# Q2、设置的 key 明明还没有过期,为啥 这个 key 就不见了?

Redis max memory 达到上限。刚好设置策略为 非noeviction ,即使 key 未过期 仍然存在被回收的可能。

反之,如果当 Redis 明明没有挂掉,但是总是写操作失败,就得看看报错信息,看看 max memory ,对比 内存使用量,加上 max memory 策略来判定,是否设置了 noeviction 策略,导致的问题。

精彩内容推送,请关注公众号!
最近更新时间: 4/25/2020, 10:24:06 PM