当前位置: > 财经>正文

redis的key过期了还能取出来? 信托时间到了还能取吗

2023-09-09 23:13:29 互联网 未知 财经

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 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 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以下的版本,没有对过期处理的结果进行处理,就会导致在从库上返回值;4.0以后过期的情况下,又对expireifNeeded进行了处理(豆沙绿那块),如果不是主库就返回null周期性的删除每次轮训一个周期(具体多久,看中间其他的执行),过期处理最多占用25毫秒(4.0以后的源码)

后续

版权声明: 本站仅提供信息存储空间服务,旨在传递更多信息,不拥有所有权,不承担相关法律责任,不代表本网赞同其观点和对其真实性负责。如因作品内容、版权和其它问题需要同本网联系的,请发送邮件至 举报,一经查实,本站将立刻删除。