IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> redis的key过期了还能取出来? -> 正文阅读

[大数据]redis的key过期了还能取出来?

我记得在2016年,2017年的时候,我们使用2.8的集群。当时业务有个需求,要求某个接口一天调用不能超过1000次,当时开发使用一个key: biz:total 来限制。

?当时出现的问题是,第二天,接口实际调用量为0,但是从redis里获取到的值还是1000。?

当时直接问的阿里云技术支持,反馈这种情况有两种,一种是定期删除,没有达到删除条件,一种是cpu压力过大,不会执行删除策略。

当时也没有深究这个问题,就想了个解决方案,直接把key改为 每天一个的 key: biz20170125:total 来控量,算是解决了这个问题。

最近在看redis的源码,顺带翻了下2.8和3.2的源码,真实的了解下问题的根本愿意。

redis2.8中 redis.c文件中

struct redisCommand redisCommandTable[] = {
    {"get",getCommand,2,"rF",0,NULL,1,1,1,0,0},
    ....
}

string.c文件中

void getCommand(redisClient *c) {
    getGenericCommand(c);
}

int getGenericCommand(redisClient *c) {
    robj *o;

    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
        return REDIS_OK;

    if (o->type != REDIS_STRING) {
        addReply(c,shared.wrongtypeerr);
        return REDIS_ERR;
    } else {
        addReplyBulk(c,o);
        return REDIS_OK;
    }
}

db.c

robj *lookupKeyReadOrReply(redisClient *c, robj *key, robj *reply) {
    robj *o = lookupKeyRead(c->db, key);
    if (!o) addReply(c,reply);
    return o;
}
robj *lookupKeyRead(redisDb *db, robj *key) {
    robj *val;
    //过期处理
    expireIfNeeded(db,key);
    //不管结果,直接查,查到就返回
    val = lookupKey(db,key);
    //为空,miss计数
    if (val == NULL)
        server.stat_keyspace_misses++;
    else
        //命中计数
        server.stat_keyspace_hits++;
    return val;
}

int expireIfNeeded(redisDb *db, robj *key) {
    // 返回key的过期时间,如果这个key没有设置过期时间返回-1
    mstime_t when = getExpire(db,key);
    mstime_t now;

    //key没有设置过期时间,返回0
    if (when < 0) return 0; /* No expire for this key */

    //持久化机制如果在loading,返回0
    if (server.loading) return 0;

    //获取当前时间
    now = server.lua_caller ? server.lua_time_start : mstime();

    //如果不是master节点,过期返回1,未过期返回0
    if (server.masterhost != NULL) return now > when;

    //未过期返回0
    if (now <= when) return 0;

    /* Delete the key */
    // 过期key计数
    server.stat_expiredkeys++;
    //将过期机制同步到从库
    propagateExpire(db,key);
    //发布过期事件
    notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,
        "expired",key,db->id);
    //返回删除结果
    return dbDelete(db,key);
}

到3.2 移除了redis.c文件 转到了server.c文件

struct redisCommand redisCommandTable[] = {
    {"get",getCommand,2,"rF",0,NULL,1,1,1,0,0},
    ......
}

string.c中

void getCommand(client *c) {
    getGenericCommand(c);
}

void getsetCommand(client *c) {
    if (getGenericCommand(c) == C_ERR) return;
    c->argv[2] = tryObjectEncoding(c->argv[2]);
    setKey(c->db,c->argv[1],c->argv[2]);
    notifyKeyspaceEvent(NOTIFY_STRING,"set",c->argv[1],c->db->id);
    server.dirty++;
}
int getGenericCommand(client *c) {
    robj *o;

    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
        return C_OK;

    if (o->type != OBJ_STRING) {
        addReply(c,shared.wrongtypeerr);
        return C_ERR;
    } else {
        addReplyBulk(c,o);
        return C_OK;
    }
}

db.c 文件中

robj *lookupKeyReadOrReply(client *c, robj *key, robj *reply) {
    robj *o = lookupKeyRead(c->db, key);
    if (!o) addReply(c,reply);
    return o;
}

robj *lookupKeyRead(redisDb *db, robj *key) {
    return lookupKeyReadWithFlags(db,key,LOOKUP_NONE);
}

robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
    robj *val;

    //2.8 是直接放过,这块是有逻辑处理
    if (expireIfNeeded(db,key) == 1) {       
        //不是master节点,直接返回null
        if (server.masterhost == NULL) return NULL;
        //只读模式也返回null
        if (server.current_client &&
            server.current_client != server.master &&
            server.current_client->cmd &&
            server.current_client->cmd->flags & CMD_READONLY)
        {
            return NULL;
        }
    }
    //查找key
    val = lookupKey(db,key,flags);
    if (val == NULL)
        server.stat_keyspace_misses++;
    else
        server.stat_keyspace_hits++;
    return val;
}

int expireIfNeeded(redisDb *db, robj *key) {
    mstime_t when = getExpire(db,key);
    mstime_t now;
    //没有设置过期时间
    if (when < 0) return 0; /* No expire for this key */
    //持久化的 loading时,直接返回0
    if (server.loading) return 0;

    //获取当前时间
    now = server.lua_caller ? server.lua_time_start : mstime();

    // 从库,已过期返回1,未过期返回0
    if (server.masterhost != NULL) return now > when;

    //未过期返回0
    if (now <= when) return 0;

    //删除逻辑
    server.stat_expiredkeys++;
    propagateExpire(db,key);
    notifyKeyspaceEvent(NOTIFY_EXPIRED,
        "expired",key,db->id);
    //4.0以及以后的删除增加了异步删除   
    return dbDelete(db,key);
}

int dbDelete(redisDb *db, robj *key) {
    //在过期集合中,删除
    if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);
    //直接删除
    if (dictDelete(db->dict,key->ptr) == DICT_OK) {
        //同步从
        if (server.cluster_enabled) slotToKeyDel(key);
        return 1;
    } else {
        return 0;
    }
}

最终将对应的逻辑梳理下了,同时也将redis的过期策略整理了下。3.2以下的版本,没有对过期处理的结果进行处理。3.2在过期的情况下,又对expireifNeeded进行了处理,如果不是主库就返回null

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2021-12-07 12:05:45  更:2021-12-07 12:05:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:48:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码