Redis源码阅读之hget命令
June 22, 2024
Redis版本:7.0.2
hget命令对应的函数为hgetCommand,源码如下:
****************t_hash.c*******************
void hgetCommand(client *c) {
robj *o;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == NULL ||
checkType(c,o,OBJ_HASH)) return;
addHashFieldToReply(c, o, c->argv[2]->ptr);
}
static void addHashFieldToReply(client *c, robj *o, sds field) {
if (o == NULL) {
addReplyNull(c);
return;
}
unsigned char *vstr = NULL;
unsigned int vlen = UINT_MAX;
long long vll = LLONG_MAX;
if (hashTypeGetValue(o, field, &vstr, &vlen, &vll) == C_OK) {
if (vstr) {
addReplyBulkCBuffer(c, vstr, vlen);
} else {
addReplyBulkLongLong(c, vll);
}
} else {
addReplyNull(c);
}
}
/* Higher level function of hashTypeGet*() that returns the hash value
* associated with the specified field. If the field is found C_OK
* is returned, otherwise C_ERR. The returned object is returned by
* reference in either *vstr and *vlen if it's returned in string form,
* or stored in *vll if it's returned as a number.
* hashTypGet*()的高级函数,返回与指定字段关联的哈希值。如果找到字段,返回C_OK,否则返回C_ERR。
* 如果返回的是字符串形式,则以*vstr和*vlen的引用的方式返回对象。如果返回的是数字,则存储在*vll中。
*
* If *vll is populated *vstr is set to NULL, so the caller
* can always check the function return by checking the return value
* for C_OK and checking if vll (or vstr) is NULL.
* 如果*vll被填充,*vstr被设置为NULL,调用者就可以通过检查检查返回值是否为C_OK以及vll(或vstr)是否为NULL来检查函数返回。
* */
int hashTypeGetValue(robj *o, sds field, unsigned char **vstr, unsigned int *vlen, long long *vll) {
if (o->encoding == OBJ_ENCODING_LISTPACK) {
*vstr = NULL;
if (hashTypeGetFromListpack(o, field, vstr, vlen, vll) == 0)
return C_OK;
} else if (o->encoding == OBJ_ENCODING_HT) {
sds value;
if ((value = hashTypeGetFromHashTable(o, field)) != NULL) {
*vstr = (unsigned char*) value;
*vlen = sdslen(value);
return C_OK;
}
} else {
serverPanic("Unknown hash encoding");
}
return C_ERR;
}
/* Get the value from a hash table encoded hash, identified by field.
* Returns NULL when the field cannot be found, otherwise the SDS value
* is returned.
* 从一个使用hash表存储的hash中获取值,由field标识。当找不到field时返回NULL,否则返回SDS值。
* */
sds hashTypeGetFromHashTable(robj *o, sds field) {
dictEntry *de;
serverAssert(o->encoding == OBJ_ENCODING_HT);
de = dictFind(o->ptr, field);
if (de == NULL) return NULL;
return dictGetVal(de);
}
**********************dict.c************************
dictEntry *dictFind(dict *d, const void *key)
{
dictEntry *he;
uint64_t h, idx, table;
if (dictSize(d) == 0)
return NULL; /* dict is empty */
if (dictIsRehashing(d))
_dictRehashStep(d);
h = dictHashKey(d, key);
for (table = 0; table <= 1; table++)
{
idx = h & DICTHT_SIZE_MASK(d->ht_size_exp[table]);
he = d->ht_table[table][idx];
while (he)
{
if (key == he->key || dictCompareKeys(d, key, he->key))
return he;
he = he->next;
}
if (!dictIsRehashing(d))
return NULL;
}
return NULL;
}
可以看到,查找的主要逻辑为:
- 调用lookupKeyReadOrReply,在db的全局字典中查找key,如果不存在,或者类型不正确,则返回。
- 调用addHashFieldToReply函数,将hash中的某个字段返回。在这个函数中会调用hashTypeGetValue函数,在hashTypeGetValue中 会根据hash的编码类型(是使用listpack还是hashtable)来调用不同的函数来获取值。
最终,对于hashtable编码的hash,会调用dictFind函数来查找field对应的值。
而dictFind的主要逻辑为:
- 如果dict对象长度为0,则返回NULL。
- 如果正在进行rehash,则进行rehash。
- 遍历hash表。如果找到key后,则遍历链表。