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 选择合适的策略
-
缓存场景:使用
allkeys-lru或allkeys-lfu -
会话存储:使用
volatile-lru配合 TTL -
精确控制:使用
noeviction并手动管理内存
6.2 合理设置参数
- 根据业务特点设置合适的
maxmemory - 根据访问模式选择合适的淘汰算法
- 调整
maxmemory-samples平衡性能和精度
6.3 监控和告警
- 定期监控内存使用情况
- 设置内存使用率告警
- 关注淘汰和过期统计信息
7. 总结
Redis 的过期策略和淘汰策略是两种不同维度的内存管理机制:
- 过期策略:主要处理设置了过期时间的键,通过惰性删除和定期删除保证过期键的及时清理
- 淘汰策略:主要处理内存不足时的数据淘汰,通过各种算法选择合适的键进行删除
合理配置这两种策略对于 Redis 的性能和稳定性至关重要,需要根据具体的业务场景和内存使用情况进行选择和调优。通过深入理解这两种策略的工作原理,可以更好地发挥 Redis 的内存管理能力,构建高性能的应用系统。