Redis 的过期策略和淘汰策略详解

Redis 作为高性能的内存数据库,在内存管理方面提供了两种重要的机制:过期策略(Expiration Policy)和淘汰策略(Eviction Policy)。这两种策略分别解决不同的问题,对于 Redis 的性能和稳定性至关重要。

1. 过期策略(Expiration Policy)

过期策略用于处理设置了过期时间的键,当键过期时自动删除,确保数据的时效性。

1.1 设置过期时间

Redis 提供了多种方式来设置键的过期时间:

设置键的过期时间(秒)
EXPIRE key 3600 PEXPIRE key 3600000 # 毫秒
设置键值的同时设置过期时间
SETEX key 3600 "value" PSETEX key 3600000 "value"
设置绝对过期时间
EXPIREAT key timestamp PEXPIREAT key timestamp_ms

1.2 查看过期时间

查看剩余过期时间(秒)
TTL key
查看剩余过期时间(毫秒)
PTTL key
移除过期时间
PERSIST key

1.3 过期键的删除策略

Redis 采用惰性删除 + 定期删除的组合策略来处理过期键:

惰性删除(Lazy Deletion)

在执行任何对该键的操作(如写入、删除或读取)时,Redis 都会检查该键是否过期,如果过期则立即删除:

伪代码示例
def get_key(key): if key_expired(key): delete_key(key) return None return get_value(key)

这种方式的优点是只有在访问时才检查过期,对 CPU 资源消耗较小。

定期删除(Periodic Deletion)

Redis 会定期随机检查一部分键,删除其中过期的键:

伪代码示例
def active_expire_cycle(): # 每次检查最多 20 个数据库 
for db in databases[:20]: 
expired_count = 0 # 每个数据库检查最多 20 个键 
for i in range(20): 
key = random_key_from_db(db) 
if key_expired(key): 
delete_key(key) 
expired_count += 1
    # 如果过期键比例较高,继续检查
    if expired_count > 10:
        continue_checking(db)

这种方式可以及时清理过期键,避免内存浪费。

1.4 过期策略配置

hz 参数:默认值为 10,表示 Redis 每秒尝试执行 10 次定期删除扫描,但这是“尽力而为”的尝试,而非硬性保证
Redis在每次定期删除扫描时,会随机抽取一定数量的key(通常为20个)进行检查
当单次扫描中过期key比例≤25%时,本轮扫描终止;否则持续循环扫描
25%的过期key比例阈值是不可配置的

redis.conf 配置
每秒执行 10 次定期删除操作
hz 10
每次 active expire cycle 检查的键数量
active-expire-effort 20

2. 淘汰策略(Eviction Policy)

当 Redis 内存使用达到 maxmemory 限制时,根据淘汰策略删除键以释放内存,防止内存溢出。

2.1 配置淘汰策略

redis.conf 配置
maxmemory 2gb 
maxmemory-policy allkeys-lru

2.2 淘汰策略类型

2.2.1 不淘汰策略

策略 描述
noeviction 默认策略,不淘汰数据,内存满时写操作返回错误

2.2.2 基于 TTL 的淘汰策略

策略 描述
allkeys-lru 在所有键中使用 LRU 算法淘汰
allkeys-lfu 在所有键中使用 LFU 算法淘汰
allkeys-random 在所有键中随机淘汰

2.2.3 基于过期时间的淘汰策略

策略 描述
volatile-lru 在设置了过期时间的键中使用 LRU 算法淘汰
volatile-lfu 在设置了过期时间的键中使用 LFU 算法淘汰
volatile-random 在设置了过期时间的键中随机淘汰
volatile-ttl 优先淘汰 TTL 较小的键

2.3 淘汰算法详解

LRU(Least Recently Used)最近最少使用

LRU 算法淘汰最久未被访问的键:

LRU 算法示例
class LRUCache: def init(self, capacity): self.capacity = capacity self.cache = {} # 存储键值对 self.access_order = [] # 访问顺序列表
def get(self, key):
    if key in self.cache:
        # 更新访问顺序
        self.access_order.remove(key)
        self.access_order.append(key)
        return self.cache[key]
    return None

def put(self, key, value):
    if len(self.cache) >= self.capacity:
        # 淘汰最久未使用的键
        lru_key = self.access_order.pop(0)
        del self.cache[lru_key]
    
    self.cache[key] = value
    self.access_order.append(key)

LFU(Least Frequently Used)最不经常使用

LFU 算法淘汰访问频率最低的键:

LFU 算法示例
class LFUCache: def init(self, capacity): self.capacity = capacity self.cache = {} # 键值存储 self.frequency = {} # 访问频率统计 self.access_time = {} # 访问时间记录
def get(self, key):
    if key in self.cache:
        self.frequency[key] += 1
        self.access_time[key] = time.time()
        return self.cache[key]
    return None

def put(self, key, value):
    if len(self.cache) >= self.capacity:
        # 找到访问频率最低且最久未访问的键
        lfu_key = min(self.frequency.keys(), 
                     key=lambda k: (self.frequency[k], self.access_time[k]))
        del self.cache[lfu_key]
        del self.frequency[lfu_key]
        del self.access_time[lfu_key]
    
    self.cache[key] = value
    self.frequency[key] = 1
    self.access_time[key] = time.time()

2.4 淘汰策略配置示例

redis.conf 配置示例
设置最大内存
maxmemory 2gb
选择淘汰策略
maxmemory-policy allkeys-lru
LRU 算法近似采样数量(提高性能)
maxmemory-samples 5
LFU 计数器衰减因子
lfu-log-factor 10
LFU 计数器衰减时间(分钟)
lfu-decay-time 1

3. 过期策略 vs 淘汰策略

3.1 主要区别

特性 过期策略 淘汰策略
触发条件 键过期时间到达 内存使用达到上限
作用对象 设置了过期时间的键 所有键(根据策略)
删除时机 访问时检查或定期检查 内存不足时立即执行
目的 实现键的自动过期 控制内存使用量

3.2 执行流程

mermaid 
graph TD 
A[Redis 接收写请求] --> B{是否超过 maxmemory?} 
B -->|是| C[执行淘汰策略] 
B -->|否| D[正常写入] 
C --> E[根据策略淘汰键] 
E --> D 
D --> F{键是否过期?} 
F -->|是| G[执行过期策略] 
F -->|否| H[返回结果] 
G --> H

4. 实际应用场景

4.1 缓存场景

会话存储配置示例
maxmemory 2gb 
maxmemory-policy volatile-lru
设置会话过期时间
SETEX session:user123 1800 "session_data"

适用于存储用户会话且需要自动过期的场景。

4.3 严格内存控制场景

严格内存控制配置
maxmemory 1gb 
maxmemory-policy noeviction

适用于内存严格受限且不允许自动淘汰数据的场景。

5. 性能优化建议

5.1 过期策略优化

调整定期删除频率
hz 10
调整 active expire cycle 的努力程度
active-expire-effort 1

5.2 淘汰策略优化

增加采样数量提高精度(但会影响性能)
maxmemory-samples 10
合理设置 LFU 参数
lfu-log-factor 10 
lfu-decay-time 1

5.3 监控和调优

监控内存使用情况
INFO memory
查看淘汰统计
INFO stats
查看键空间统计
INFO keyspace

6. 最佳实践

6.1 选择合适的策略

  1. 缓存场景:使用 allkeys-lruallkeys-lfu
  2. 会话存储:使用 volatile-lru 配合 TTL
  3. 精确控制:使用 noeviction 并手动管理内存

6.2 合理设置参数

  1. 根据业务特点设置合适的 maxmemory
  2. 根据访问模式选择合适的淘汰算法
  3. 调整 maxmemory-samples 平衡性能和精度

6.3 监控和告警

  1. 定期监控内存使用情况
  2. 设置内存使用率告警
  3. 关注淘汰和过期统计信息

7. 总结

Redis 的过期策略和淘汰策略是两种不同维度的内存管理机制:

  1. 过期策略:主要处理设置了过期时间的键,通过惰性删除和定期删除保证过期键的及时清理
  2. 淘汰策略:主要处理内存不足时的数据淘汰,通过各种算法选择合适的键进行删除

合理配置这两种策略对于 Redis 的性能和稳定性至关重要,需要根据具体的业务场景和内存使用情况进行选择和调优。通过深入理解这两种策略的工作原理,可以更好地发挥 Redis 的内存管理能力,构建高性能的应用系统。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容