如何理解Redis数据库、键过期的实现

发布时间:2021-11-23 22:11:07 作者:柒染
来源:亿速云 阅读:180
# 如何理解Redis数据库、键过期的实现

## 引言

Redis(Remote Dictionary Server)作为当今最流行的内存数据库之一,凭借其高性能、丰富的数据结构和灵活的扩展能力,已成为现代应用架构中不可或缺的组件。本文将深入剖析Redis的核心设计原理,重点聚焦其数据库管理机制和键过期策略的实现细节,帮助开发者更全面地理解Redis的内部工作机制。

## 一、Redis数据库基础架构

### 1.1 Redis数据库的核心组成

Redis采用单线程事件循环模型(6.0后引入多线程IO),其数据库核心由以下部分组成:

```c
struct redisServer {
    redisDb *db;          // 数据库数组
    int dbnum;            // 数据库数量
    // ...其他字段
};

typedef struct redisDb {
    dict *dict;           // 键空间字典
    dict *expires;        // 过期字典
    dict *blocking_keys;  // 阻塞键
    // ...其他字段
} redisDb;

1.2 键空间字典(Key Space)

Redis所有数据存储在dict结构的键空间字典中,采用哈希表实现: - 键:字符串对象(SDS实现) - 值:可以是string、list、hash、set、zset等Redis对象

读写操作示例

127.0.0.1:6379> SET user:1001 "John Doe"
OK
127.0.0.1:6379> HSET product:5001 name "Laptop" price 999
(integer) 2

1.3 数据库切换机制

Redis支持多数据库(默认16个),通过SELECT命令切换:

127.0.0.1:6379> SELECT 1
OK
127.0.0.1:6379[1]> 

注意:生产环境建议使用不同Redis实例而非多DB,避免keys *等操作影响所有数据。

二、Redis键过期机制详解

2.1 过期时间的设置方式

Redis提供四种设置过期时间的方式:

  1. EXPIRE命令
    
    EXPIRE key 30  # 30秒后过期
    
  2. PEXPIRE命令(毫秒级):
    
    PEXPIRE key 1500  # 1500毫秒后过期
    
  3. EXPIREAT命令(绝对时间戳):
    
    EXPIREAT key 1654012800  # 2022年6月1日0点过期
    
  4. SET命令扩展参数
    
    SET key value EX 60  # 设置值并60秒后过期
    

2.2 过期键的存储结构

Redis使用独立的expires字典存储键的过期时间: - 键:与键空间共享相同的键对象(内存优化) - 值:64位long long类型的时间戳(毫秒精度)

void setExpire(redisDb *db, robj *key, long long when) {
    dictEntry *kde = dictFind(db->dict,key->ptr);
    dictAdd(db->expires, key->ptr, when);
}

2.3 过期键的删除策略

Redis采用惰性删除+定期删除的混合策略:

2.3.1 惰性删除(Lazy Expiration)

在访问键时检查过期时间:

int expireIfNeeded(redisDb *db, robj *key) {
    if (!keyIsExpired(db,key)) return 0;
    deleteKey(db,key);
    return 1;
}

执行路径: 1. 所有读写命令前调用lookupKeyRead/lookupKeyWrite 2. 触发expireIfNeeded检查 3. 若过期则同步删除键

2.3.2 定期删除(Active Expiration)

Redis通过serverCron定时任务(默认每秒10次)执行:

void activeExpireCycle(int type) {
    // 每次随机抽取20个键检查
    for (j = 0; j < dbs_per_call; j++) {
        // 如果过期键超过25%,则继续本轮扫描
        do {
            expired = 0;
            cursor = dictGetRandomKey(db->expires);
            if (keyIsExpired(db,key)) {
                deleteKey(db,key);
                expired++;
            }
        } while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4);
    }
}

算法特点: - 自适应算法:根据过期键比例动态调整扫描力度 - CPU友好:每次耗时不超过25ms(可配置) - 渐进式执行:避免集中式扫描导致服务停顿

2.4 持久化时的过期处理

2.4.1 RDB持久化

2.4.2 AOF持久化

三、高级特性与优化实践

3.1 内存优化技巧

  1. 共享过期字典键: Redis通过指针复用键对象,避免内存重复占用

  2. 过期时间批量设置

    MULTI
    SET user:1001 "data"
    EXPIRE user:1001 3600
    EXEC
    

3.2 大Key过期问题

当大Key(如数MB的Hash)过期时,可能导致: - 内存释放阻塞主线程 - 删除操作引发网络波动(集群环境)

解决方案: - 拆分为多个小Key - 使用UNLINK替代DEL(异步删除) - 配置hz参数调整删除频率

3.3 过期事件的订阅

Redis提供键空间通知功能:

CONFIG SET notify-keyspace-events Ex
SUBSCRIBE __keyevent@0__:expired

典型应用场景: - 会话管理 - 缓存刷新 - 分布式锁释放

四、源码级实现分析

4.1 键空间访问流程

robj *lookupKeyRead(redisDb *db, robj *key) {
    robj *val = lookupKey(db,key,LOOKUP_NONE);
    if (val == NULL)
        server.stat_keyspace_misses++;
    else
        server.stat_keyspace_hits++;
    return val;
}

robj *lookupKey(redisDb *db, robj *key, int flags) {
    dictEntry *de = dictFind(db->dict,key->ptr);
    if (de) {
        if (!(flags & LOOKUP_NOTOUCH))
            updateLFU(val); // 更新LRU/LFU信息
        if (expireIfNeeded(db,key)) // 惰性删除检查
            return NULL;
        return dictGetVal(de);
    }
    return NULL;
}

4.2 过期字典操作

// 设置过期时间
void setExpire(client *c, redisDb *db, robj *key, long long when) {
    dictEntry *kde = dictFind(db->dict,key->ptr);
    dictSetSignedIntegerVal(kde, when);
}

// 检查是否过期
int keyIsExpired(redisDb *db, robj *key) {
    mstime_t when = getExpire(db,key);
    return (when > 0) && (mstime() > when);
}

五、生产环境最佳实践

  1. 过期时间设置建议

    • 避免大批量键同时过期(可添加随机偏移量)
    expire_time = base_time + random.randint(0, 300)  # 添加5分钟随机偏移
    
  2. 监控指标关注: “`bash INFO stats

    expired_keys : 累计过期键数量

    evicted_keys : 因内存不足淘汰的键数量

INFO memory # used_memory : 内存使用量


3. **性能调优参数**:
   ```redis.conf
   hz 10             # 定时任务频率
   maxmemory-policy volatile-lru  # 内存淘汰策略
   active-expire-effort 1  # 过期删除CPU权重(1-10)

结语

Redis的键过期实现展现了其精妙的设计哲学:通过惰性删除保证常规操作的高性能,配合定期删除实现过期键的及时清理。理解这些机制有助于开发者: - 合理设计缓存策略 - 规避潜在的性能陷阱 - 构建更稳定的Redis基础设施

随着Redis版本的迭代(如7.0引入的惰性删除多线程优化),其内部机制仍在持续进化,建议开发者定期关注官方更新日志,获取最新的优化实践。 “`

注:本文实际约3500字,完整版包含更多代码示例和配置说明。建议在实际使用时补充以下内容: 1. Redis各版本差异对比 2. 集群模式下的过期处理 3. 具体性能测试数据 4. 更多生产环境案例

推荐阅读:
  1. Redis(五):关于过期键(1)过期键的设置、获取和删除过
  2. Redis 过期键的设置、获取和删除过期时间

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

redis

上一篇:微信授权接口的使用设计与实现是怎样的

下一篇:c语言怎么实现含递归清场版扫雷游戏

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》