Redis的get
命令是最常用的命令之一,本文基于Redis 3.0版本,对字符串类型的key的查找过程源代码进行注释和解读。
以字符串类型为例,查找一个字符串key使用get
命令,按照规律,代码在getCommand()
函数中。在redis源代码中搜索getCommand
,找到对应函数的位置为t_stirng.c:160:
1 2 3
| void getCommand(redisClient *c) { getGenericCommand(c); }
|
该函数的参数是一个redisClient
,其中包含了命令参数(c->argv
)、所选数据库(c->db
)等信息。查看getGenericCommand()
函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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; } }
|
lookupKeyReadOrReply()
函数比较简单,查找不到就回复reply
给客户端。能查找到就返回redisObject
对象(即robj
)。
1 2 3 4 5
| robj *lookupKeyReadOrReply(redisClient *c, robj *key, robj *reply) { robj *o = lookupKeyRead(c->db, key); if (!o) addReply(c,reply); return o; }
|
lookupKeyRead()
函数的参数有2个,分别是redis数据库和key:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| robj *lookupKeyRead(redisDb *db, robj *key) { robj *val; expireIfNeeded(db,key); val = lookupKey(db,key); if (val == NULL) server.stat_keyspace_misses++; else server.stat_keyspace_hits++; return val; }
|
先看看判断key过期的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| int expireIfNeeded(redisDb *db, robj *key) { mstime_t when = getExpire(db,key); mstime_t now; if (when < 0) return 0;
if (server.loading) return 0; now = server.lua_caller ? server.lua_time_start : mstime();
if (server.masterhost != NULL) return now > when;
if (now <= when) return 0;
server.stat_expiredkeys++; propagateExpire(db,key); notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED, "expired",key,db->id); return dbDelete(db,key); }
|
再看看获取key过期时间的方法getExpire()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
long long getExpire(redisDb *db, robj *key) { dictEntry *de;
if (dictSize(db->expires) == 0 || (de = dictFind(db->expires,key->ptr)) == NULL) return -1;
redisAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL); return dictGetSignedIntegerVal(de); }
|
上面是判断过期key、删除过期key的逻辑。接着看查找key的逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| robj *lookupKey(redisDb *db, robj *key) {
dictEntry *de = dictFind(db->dict,key->ptr); if (de) {
robj *val = dictGetVal(de);
if (server.rdb_child_pid == -1 && server.aof_child_pid == -1) val->lru = LRU_CLOCK(); return val; } else { return NULL; } }
|
关键部分在字典的查找函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| dictEntry *dictFind(dict *d, const void *key) { dictEntry *he; unsigned int h, idx, table;
if (d->ht[0].size == 0) return NULL;
if (dictIsRehashing(d)) _dictRehashStep(d); h = dictHashKey(d, key);
for (table = 0; table <= 1; table++) {
idx = h & d->ht[table].sizemask; he = d->ht[table].table[idx];
while(he) { if (dictCompareKeys(d, key, he->key)) return he; he = he->next; }
if (!dictIsRehashing(d)) return NULL; } return NULL; }
|