Redis 是一种常用的内存数据库,它可以为存储的 key 设置过期时间。那么,Redis 是如何处理过期的 key 的呢?它会不会影响 Redis 的单线程性能呢?要回答这些问题,我们需要先了解 Redis 的过期策略和内存淘汰机制。 Redis 的过期策略是结合定期删除和懒惰删除来实现的。 Redis 的定期删除策略是一种平衡的方法,它定时地检查 Redis 库中的过期数据,采用随机抽样的方法,根据过期数据的比例来调整删除的速度。 过期数据的比例是指 Redis 在定期删除策略中,根据每次随机抽样的键中有多少是过期的来决定是否继续删除。如果过期的键比例超过 1/4,就继续抽样和删除。这样可以根据过期数据的密集程度来控制删除的频率,避免过多占用 CPU 资源或内存空间。 例如: Redis 会将每个设置了过期时间的 key 存入到一个单独的字典中,默认每 100ms 执行一次过期扫描: Redis,作为一个单线程系统,全面扫描所有键值对可能会大幅度地影响性能。因此,Redis 限制每次过期扫描的最大耗时,这个限制默认是 25ms。 如果用户将操作超时设置得太短,比如 10ms,那么许多连接可能会由于超时而关闭,导致应用出现许多异常。此时,Redis 的慢查询日志可能并没有任何记录,因为慢查询记录的只是命令的处理时间,而不包括等待时间。 当大量键值对在同一时刻过期时,Redis 会多次扫描过期字典,直到过期键的比例低于四分之一。这可能会导致短暂的系统卡顿,尤其在并发请求高的情况下,这可能引发所谓的缓存雪崩。 因为Redis运行在单线程模式中,所有的请求处理都必须按照顺序进行。为了平衡过期键的删除和服务的响应性,Redis限制了每次过期扫描的时间,上限是25毫秒。因此,每个请求的处理时间最多为25毫秒。假设有100个请求等待处理,那么总的处理时间就可能高达2500毫秒。 然而,如果同时有大量的键过期,这就可能导致Redis需要花费更多的时间来进行扫描和删除操作。因此,为了避免这种情况,建议将键的过期时间设置为一个随机范围,这样就可以让过期的键在不同的时间点进行过期,从而降低单次扫描的压力,提高Redis的处理效率。 Redis的从库采取被动方式处理键值过期,即它不会主动去扫描和删除过期的键。当从库收到读取请求时,它会将请求发送到主库处理。在主库端,会检查相关键是否已过期,如果是,那么主库会删除这个键,并把删除操作同步给所有从库。这样的处理方式意味着从库可以避免进行过期扫描,进一步减轻了从库的处理负荷。 与定期删除不同,懒惰删除策略并不会定时地去扫描和删除过期的键,而是在每次访问键时,检查该键是否已经过期,如果已经过期,那么就删除它。 可以节省处理器时间,因为只有在键被访问时,Redis才会去检查并删除过期的键。这种策略在很多情况下都能有效地处理过期的键,因为很多过期的键可能永远都不会被访问,因此没有必要花费时间去删除它们。 然而,懒惰删除策略的缺点在于可能会导致过期的键占用内存空间。因为只有在键被访问时,Redis才会删除它,如果一个过期的键一直没有被访问,那么它就会一直占用内存空间,这在内存紧张的环境下可能会成为一个问题。为了解决这个问题,Redis通常会结合定期删除策略,以确保及时地删除过期的键。 在绝大多数场景下,Redis的del命令可以快速地执行并且不会产生明显的延迟,因为它能直接回收目标对象的内存。但是,当我们需要删除一个非常大的对象,例如,一个拥有数百万元素的哈希表,或者在使用FLUSHDB和FLUSHALL命令来删除一个包含大量键值对的数据库时,这个删除操作就可能引起单线程的阻塞。 为了解决这个问题,从Redis 4.0开始,引入了一个名为lazyfree的机制。这个机制能将删除键值对或整个数据库的操作交给后台线程处理,以此最大程度地避免对Redis主线程的阻塞。 以下是Redis支持的数据淘汰策略:前言
定期删除策略
为什不扫描所有的 key?
设置25ms为什么还会出现缓存雪崩
从库的过期策略
惰性删除策略
它对比定期删除策略的优点?
异步删除
内存淘汰机制