HSET命令
void hsetCommand(redisClient *c) {
int update;
robj *o;
if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
//检查是否需要类型转换
hashTypeTryConversion(o,c->argv,2,3);
//将参数进行编码
hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
//更新值:Return 0 on insert and 1 on update.
update = hashTypeSet(o,c->argv[2],c->argv[3]);
addReply(c, update ? shared.czero : shared.cone);
/* "Touch" a key, so that if this key is being WATCHed by
some client the next EXEC will fail. */
signalModifiedKey(c->db,c->argv[1]);
//The API provided to the rest of the Redis core is a simple function
notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hset",c->argv[1],c->db->id);
// dirty:Changes to DB from the last save
server.dirty++;
}
1、类型转换处理ziplist-->hashtable
void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {
int i;
if (o->encoding != REDIS_ENCODING_ZIPLIST) return;
for (i = start; i <= end; i++) { //对ziplist中的每个节点都进行转换
if (sdsEncodedObject(argv[i]) &&
sdslen(argv[i]->ptr) > server.hash_max_ziplist_value)
{
hashTypeConvert(o, REDIS_ENCODING_HT);
break;
}
}
}
hashTypeConvert:
void hashTypeConvertZiplist(robj *o, int enc) { //被hashTypeConvert函数又封装了一层
redisAssert(o->encoding == REDIS_ENCODING_ZIPLIST);
if (enc == REDIS_ENCODING_ZIPLIST) {
/* Nothing to do... */
} else if (enc == REDIS_ENCODING_HT) {
hashTypeIterator *hi;
dict *dict;
int ret;
hi = hashTypeInitIterator(o);
dict = dictCreate(&hashDictType, NULL);//新创建字典
while (hashTypeNext(hi) != REDIS_ERR) {
robj *field, *value;
field = hashTypeCurrentObject(hi, REDIS_HASH_KEY);//键对象
field = tryObjectEncoding(field);
value = hashTypeCurrentObject(hi, REDIS_HASH_VALUE);//值对象
value = tryObjectEncoding(value);
ret = dictAdd(dict, field, value);//字典的API:新键值对添加到字典
if (ret != DICT_OK) {
redisLogHexDump(REDIS_WARNING,"ziplist with dup elements dump",
o->ptr,ziplistBlobLen(o->ptr));
redisAssert(ret == DICT_OK);
}
}
//释放内存
hashTypeReleaseIterator(hi);
zfree(o->ptr);
o->encoding = REDIS_ENCODING_HT;
o->ptr = dict;
} else {
redisPanic("Unknown hash encoding");
}
}
hashTypeSet:对已有的更新,没有就插入。 返回值:insert:0,update:1
int hashTypeSet(robj *o, robj *field, robj *value) {
int update = 0;
if (o->encoding == REDIS_ENCODING_ZIPLIST) {//对ziplist处理
unsigned char *zl, *fptr, *vptr;
field = getDecodedObject(field); //该操作会让refcount+1
value = getDecodedObject(value);
zl = o->ptr;
//判断是update还是insert
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); //value在key后面
redisAssert(vptr != NULL);
update = 1;
//删除value
zl = ziplistDelete(zl, &vptr);
//新增value
zl = ziplistInsert(zl, vptr, value->ptr, sdslen(value->ptr));
}
}
if (!update) {//原ziplist中没有的话push
/* 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;
//由于前面有操作让recount+1,而ziplist实际并没有共享该对象,而只是使用了对应的值,故-1
decrRefCount(field);
decrRefCount(value);
//判断是否需要类型转换
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;
}
ZRANK / ZREVRANK命令
//ZRANKE
void zrankCommand(redisClient *c) {
zrankGenericCommand(c, 0);
}
//ZREVRANK
void zrevrankCommand(redisClient *c) {
zrankGenericCommand(c, 1);
}
void zrankGenericCommand(redisClient *c, int reverse) {
robj *key = c->argv[1];//键
robj *ele = c->argv[2];//需要计算排名的成员
robj *zobj;
unsigned long llen;
unsigned long rank;
//...
llen = zsetLength(zobj);
if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {
//ziplist编码的情况
unsigned char *zl = zobj->ptr;
unsigned char *eptr, *sptr;
eptr = ziplistIndex(zl,0);
sptr = ziplistNext(zl,eptr);
rank = 1;
while(eptr != NULL) {
if (ziplistCompare(eptr,ele->ptr,sdslen(ele->ptr))) //相同返回1
break;
rank++; //每往后一个节点,rank+1
zzlNext(zl,&eptr,&sptr);
//返回值
if (eptr != NULL) {
if (reverse)
addReplyLongLong(c,llen-rank);
else
addReplyLongLong(c,rank-1);
} else {
addReply(c,shared.nullbulk);
}
} else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
//skiplist编码的情况
zset *zs = zobj->ptr;
zskiplist *zsl = zs->zsl;
dictEntry *de;
double score;
ele = c->argv[2] = tryObjectEncoding(c->argv[2]);
//先在字典中找出该成员对应的分值
de = dictFind(zs->dict,ele);
if (de != NULL) {
score = *(double*)dictGetVal(de);
//在跳跃表中计算对应的分值的排名
rank = zslGetRank(zsl,score,ele);
redisAssertWithInfo(c,ele,rank); /* Existing elements always have a rank. */
if (reverse)
addReplyLongLong(c,llen-rank);
else
addReplyLongLong(c,rank-1);
} else {
addReply(c,shared.nullbulk);
}
} else {
redisPanic("Unknown sorted set encoding");
}
}
zslGetRank:是跳跃表的一个API
unsigned long zslGetRank(zskiplist *zsl, double score, robj *o) {
zskiplistNode *x;
unsigned long rank = 0;
int i;
x = zsl->header;
for (i = zsl->level-1; i >= 0; i--) { //在头节点中逐层往下找
while (x->level[i].forward &&
(x->level[i].forward->score < score || //该节点的forward的分值小于目标分值
(x->level[i].forward->score == score && //或目标与该节点的forward相同
compareStringObjects(x->level[i].forward->obj,o) <= 0))) {
rank += x->level[i].span; //rank += span
x = x->level[i].forward; //继续由forward向前移动
}
/* x might be equal to zsl->header, so test if obj is non-NULL */
if (x->obj && equalStringObjects(x->obj,o)) {
return rank;
}
}
return 0;
}