您好,登录后才能下订单哦!
# Redis中过期策略是怎么样的
## 引言
Redis作为高性能的键值存储系统,其内存管理机制直接影响着系统性能和资源利用率。其中,过期键的处理策略是Redis内存管理的核心组成部分。本文将深入剖析Redis的过期策略实现原理,包括被动删除、主动删除等机制,并结合源码分析其底层实现细节,最后通过实际案例展示不同策略的应用场景。
## 一、Redis过期键基础概念
### 1.1 什么是过期键
Redis允许为键设置生存时间(TTL),当达到指定时间后,该键会自动被服务器删除。这种带有生存时间限制的键称为"过期键"。
```bash
# 设置键值并指定10秒后过期
SET mykey "hello" EX 10
Redis在内部使用redisDb结构存储数据库数据,其中包含两个关键字典:
typedef struct redisDb {
    dict *dict;                 // 键空间(保存所有键值对)
    dict *expires;              // 过期字典(保存键的过期时间)
    // ...其他字段
} redisDb;
dict:保存所有键值对,称为”键空间”expires:专门记录键的过期时间,键为字符串对象,值为long long类型的UNIX时间戳Redis采用多策略组合的方式处理过期键,主要包括三种机制:
当客户端尝试访问某个键时,Redis会首先检查该键是否已过期:
int expireIfNeeded(redisDb *db, robj *key) {
    if (!keyIsExpired(db,key)) return 0;
    
    // 从键空间和过期字典中删除
    db->expires--;
    return dbDelete(db,key);
}
Redis通过serverCron时间事件定期执行过期扫描:
void activeExpireCycle(int type) {
    // 每次扫描的数据库数量
    for (j = 0; j < dbs_per_call; j++) {
        // 随机选择20个键检查
        for (i = 0; i < 20; i++) {
            // 检查并删除过期键
        }
        
        // 如果过期键比例>25%,继续扫描
    }
}
hz:默认10,控制每秒执行多少次serverCronACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP:每次扫描的键数量当内存达到maxmemory限制时,Redis会根据配置的淘汰策略删除键:
| 策略 | 说明 | 
|---|---|
| volatile-lru | 从已设置过期时间的键中使用LRU算法淘汰 | 
| allkeys-lru | 从所有键中使用LRU算法淘汰 | 
| volatile-random | 随机淘汰有过期时间的键 | 
| allkeys-random | 随机淘汰任意键 | 
| volatile-ttl | 优先淘汰剩余生存时间短的键 | 
过期字典使用哈希表存储,键与主字典共享对象引用:
void setExpire(redisDb *db, robj *key, long long when) {
    // 共享键对象(不重复创建)
    if (dictAdd(db->expires, key, (void*)when) == DICT_ERR) {
        // 更新现有过期时间
        dictReplace(db->expires, key, (void*)when);
    }
    // 增加引用计数
    incrRefCount(key);
}
Redis使用两种时间表示方式:
1. 秒级精度:EXPIRE/TTL命令使用
2. 毫秒级精度:PEXPIRE/PTTL命令使用
内部统一转换为毫秒级UNIX时间戳存储。
DEL命令hz值提高主动删除频率volatile-ttl策略active-expire-cycle参数从节点不会主动删除过期键,而是:
1. 等待主节点发来DEL命令
2. 读取已过期的键时返回空值(被动删除)
关键监控项:
- expired_keys:累计删除的过期键数量
- evicted_keys:因内存不足淘汰的键数量
- keyspace_hits/misses:键访问命中率
# 提高定期删除频率(1-500)
config set hz 100
# 增加每次扫描的键数量
config set active-expire-cycle-lookups-per-loop 50
db.c中的关键函数:
robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
    robj *val = lookupKey(db,key,flags);
    if (val == NULL)
        server.stat_keyspace_misses++;
    else
        server.stat_keyspace_hits++;
    return val;
}
robj *lookupKey(redisDb *db, robj *key, int flags) {
    // 检查过期键
    if (expireIfNeeded(db,key) == 1) {
        // 键已过期被删除
        return NULL;
    }
    return dictFind(db->dict,key->ptr);
}
expire.c中的核心逻辑:
void databasesCron(void) {
    // 每次cron调用时执行主动过期
    if (server.active_expire_enabled) {
        if (server.masterhost == NULL) {
            activeExpireCycle(ACTIVE_EXPIRE_CYCLE_SLOW);
        } else {
            expireSlaveKeys();
        }
    }
}
| 系统 | 过期策略 | 特点 | 
|---|---|---|
| Memcached | 惰性删除 | 简单但内存利用率低 | 
| RocksDB | TTL+Compaction | 基于SST文件级别删除 | 
| MySQL | 事件调度 | 需要显式设置定时任务 | 
Redis通过组合多种过期策略,在CPU消耗和内存利用率之间取得了良好平衡。理解这些机制有助于开发者根据实际业务场景做出合理的架构决策和参数调优。建议在关键业务环境中密切监控过期键的处理情况,并根据负载特征调整相关参数。
本文基于Redis 7.0版本分析,部分实现细节可能随版本变化而调整。 “`
这篇文章共计约3400字,详细涵盖了Redis过期策略的各个方面,包括: 1. 基础概念和存储结构 2. 三种核心删除策略 3. 底层实现机制 4. 不同场景下的表现 5. 性能优化实践 6. 源码级分析 7. 与其他系统的对比
文章采用技术深度与实践建议相结合的方式,既适合开发者理解原理,也能帮助运维人员优化实际系统。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。