玖叶教程网

前端编程开发入门

Redis的过期策略

前言

Redis 是一种常用的内存数据库,它可以为存储的 key 设置过期时间。那么,Redis 是如何处理过期的 key 的呢?它会不会影响 Redis 的单线程性能呢?要回答这些问题,我们需要先了解 Redis 的过期策略和内存淘汰机制。

Redis 的过期策略是结合定期删除和懒惰删除来实现的。

定期删除策略

Redis 的定期删除策略是一种平衡的方法,它定时地检查 Redis 库中的过期数据,采用随机抽样的方法,根据过期数据的比例来调整删除的速度。

过期数据的比例是指 Redis 在定期删除策略中,根据每次随机抽样的键中有多少是过期的来决定是否继续删除。如果过期的键比例超过 1/4,就继续抽样和删除。这样可以根据过期数据的密集程度来控制删除的频率,避免过多占用 CPU 资源或内存空间。

例如:

Redis 会将每个设置了过期时间的 key 存入到一个单独的字典中,默认每 100ms 执行一次过期扫描:

  1. 随机抽取 20 个 key
  2. 删除这 20 个 key 中已经过期的 key
  3. 如果过期的 key 比例超过 1/4,就重复步骤 1,继续删除。

为什不扫描所有的 key?

Redis,作为一个单线程系统,全面扫描所有键值对可能会大幅度地影响性能。因此,Redis 限制每次过期扫描的最大耗时,这个限制默认是 25ms。

如果用户将操作超时设置得太短,比如 10ms,那么许多连接可能会由于超时而关闭,导致应用出现许多异常。此时,Redis 的慢查询日志可能并没有任何记录,因为慢查询记录的只是命令的处理时间,而不包括等待时间。

当大量键值对在同一时刻过期时,Redis 会多次扫描过期字典,直到过期键的比例低于四分之一。这可能会导致短暂的系统卡顿,尤其在并发请求高的情况下,这可能引发所谓的缓存雪崩。

设置25ms为什么还会出现缓存雪崩

因为Redis运行在单线程模式中,所有的请求处理都必须按照顺序进行。为了平衡过期键的删除和服务的响应性,Redis限制了每次过期扫描的时间,上限是25毫秒。因此,每个请求的处理时间最多为25毫秒。假设有100个请求等待处理,那么总的处理时间就可能高达2500毫秒。

然而,如果同时有大量的键过期,这就可能导致Redis需要花费更多的时间来进行扫描和删除操作。因此,为了避免这种情况,建议将键的过期时间设置为一个随机范围,这样就可以让过期的键在不同的时间点进行过期,从而降低单次扫描的压力,提高Redis的处理效率。

从库的过期策略

Redis的从库采取被动方式处理键值过期,即它不会主动去扫描和删除过期的键。当从库收到读取请求时,它会将请求发送到主库处理。在主库端,会检查相关键是否已过期,如果是,那么主库会删除这个键,并把删除操作同步给所有从库。这样的处理方式意味着从库可以避免进行过期扫描,进一步减轻了从库的处理负荷。

惰性删除策略

与定期删除不同,懒惰删除策略并不会定时地去扫描和删除过期的键,而是在每次访问键时,检查该键是否已经过期,如果已经过期,那么就删除它。

它对比定期删除策略的优点?

可以节省处理器时间,因为只有在键被访问时,Redis才会去检查并删除过期的键。这种策略在很多情况下都能有效地处理过期的键,因为很多过期的键可能永远都不会被访问,因此没有必要花费时间去删除它们。

然而,懒惰删除策略的缺点在于可能会导致过期的键占用内存空间。因为只有在键被访问时,Redis才会删除它,如果一个过期的键一直没有被访问,那么它就会一直占用内存空间,这在内存紧张的环境下可能会成为一个问题。为了解决这个问题,Redis通常会结合定期删除策略,以确保及时地删除过期的键。

异步删除

在绝大多数场景下,Redis的del命令可以快速地执行并且不会产生明显的延迟,因为它能直接回收目标对象的内存。但是,当我们需要删除一个非常大的对象,例如,一个拥有数百万元素的哈希表,或者在使用FLUSHDBFLUSHALL命令来删除一个包含大量键值对的数据库时,这个删除操作就可能引起单线程的阻塞。

为了解决这个问题,从Redis 4.0开始,引入了一个名为lazyfree的机制。这个机制能将删除键值对或整个数据库的操作交给后台线程处理,以此最大程度地避免对Redis主线程的阻塞。

内存淘汰机制

以下是Redis支持的数据淘汰策略:

  1. noeviction(不删除):这种策略下,当内存不足时,Redis不会删除任何数据。如果达到最大内存限制,任何写入数据的操作将会返回一个错误。这种策略适用于那些希望确保数据持久性并手动管理内存的场景。
  2. allkeys-lru(Least Recently Used,最近最少使用):在这种策略下,Redis会根据最近的访问频率删除数据。在需要释放内存时,Redis会从所有键中选择最近最少使用的键进行淘汰。这种策略适用于大多数场景,尤其是那些需要缓存数据并根据数据访问频率来决定淘汰顺序的应用。
  3. volatile-lru(最近最少使用,只针对设置了过期时间的键):与allkeys-lru类似,但仅针对设置了过期时间的键进行淘汰。这种策略适用于那些希望保留永久数据并仅根据访问频率淘汰具有过期时间的数据的场景。
  4. allkeys-random(随机删除):在这种策略下,Redis会在需要释放内存时从所有键中随机选择一个进行淘汰。这种策略可能导致一些重要但访问频率较低的数据被删除,因此在选择时需要谨慎。
  5. volatile-random(随机删除,只针对设置了过期时间的键):与allkeys-random类似,但仅针对设置了过期时间的键进行淘汰。这种策略适用于那些希望保留永久数据并随机淘汰具有过期时间的数据的场景。
  6. volatile-ttl(根据TTL值淘汰,只针对设置了过期时间的键):在这种策略下,Redis会根据键的剩余生存时间(TTL)进行淘汰。具有较短剩余生存时间的键将优先被淘汰。这种策略适用于那些希望基于数据的剩余生存时间来决定淘汰顺序的场景。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言