t the value from a hash table encoded hash, identified by field.
* Returns -1 when the field cannot be found. */
/* 获取哈希字典中的某个值 */
int hashTypeGetFromHashTable(robj *o, robj *field, robj **value) {
dictEntry *de;
redisAssert(o->encoding == REDIS_ENCODING_HT);
//通过robj->ptr里面存的dict总类或ziplist类开始寻找
de = dictFind(o->ptr, field);
if (de == NULL) return -1;
//获取其中的value值
*value = dictGetVal(de);
return 0;
}
所有关于robj的相关结构体操作都会分成为2种情况处理,ZIPLIST和HASH类型就是dict类型,而且操作ziplist类型的时候要进行转码处理,当然在进行ziplist存入robj的时候要进行编码操作,可见,设计者在考虑到命令传输的时候想得还是很周到了,也考虑了安全的问题。?
/* Add an element, discard the old if the key already exists.
* Return 0 on insert and 1 on update.
* This function will take care of incrementing the reference count of the
* retained fields and value objects. */
/* hashType设置操作,分2种情况,ziplist,和字典hashtable */
int hashTypeSet(robj *o, robj *field, robj *value) {
int update = 0;
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *zl, *fptr, *vptr;
//首先对field和value进行解码
field = getDecodedObject(field);
value = getDecodedObject(value);
zl = o->ptr;
fptr = ziplistIndex(zl, ZIPLIST_HEAD);
if (fptr != NULL) {
fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);
if (fptr != NULL) {
/* Grab pointer to the value (fptr points to the field) */
vptr = ziplistNext(zl, fptr);
redisAssert(vptr != NULL);
update = 1;
//设置的操作,其实先删除,再插入语一个新值
/* Delete value */
zl = ziplistDelete(zl, &vptr);
/* Insert new value */
zl = ziplistInsert(zl, vptr, value->ptr, sdslen(value->ptr));
}
}
if (!update) {
/* Push new field/value pair onto the tail of the ziplist */
zl = ziplistPush(zl, field->ptr, sdslen(field->ptr), ZIPLIST_TAIL);
zl = ziplistPush(zl, value->ptr, sdslen(value->ptr), ZIPLIST_TAIL);
}
o->ptr = zl;
//用完之后,引用计数递减
decrRefCount(field);
decrRefCount(value);
/* Check if the ziplist needs to be converted to a hash table */
if (hashTypeLength(o) > server.hash_max_ziplist_entries)
hashTypeConvert(o, REDIS_ENCODING_HT);
} else if (o->encoding == REDIS_ENCODING_HT) {
//如果是字典,直接替换
if (dictReplace(o->ptr, field, value)) { /* Insert */
incrRefCount(field);
} else { /* Update */
update = 1;
}
//用完之后,引用计数递减
incrRefCount(value);
} else {
redisPanic("Unknown hash encoding");
}
return update;
}
在这个过程中,redis代码中还用到了一个引用计数的东西,应该是为了合理的内存释放控制,在很多地方可以看到这样的操作;?
/* Higher level function of hashTypeGet*() that always returns a Redis
* object (either new or with refcount incremented), so that the caller
* can retain a reference or call decrRefCount after the usage.
*
* The lower level function can prevent copy on write so it is
* the preferred way of doing read operations. */
/* 获取某个key的对象 */
robj *hashTypeGetObject(robj *o, robj *field) {
robj *value = NULL;
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *vstr = NULL;
unsigned int vlen = UINT_MAX;
long long vll = LLONG_MAX;
if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0) {
//在ziplist中获取值
if (vstr) {
value = createStringObject((char*)vstr, vlen);
} else {
value = createStringObjectFromLongLong(vll);
}
}
} else if (o->encoding == REDIS_ENCODING_HT) {
robj *aux;
if (hashTypeGetFromHashTable(o, field, &aux) == 0) {
//对象被引用了,计数递增
incrRefCount(aux);
value = aux;
}
} else {
redisPanic("Unknown hash encoding");
}
return v