hashTypeTryConversion:对传入的参数进行检查是否需要从ziplist转换成hashtable
void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {
int i;
// 如果对象不是 ziplist 编码,那么直接返回
if (o->encoding != REDIS_ENCODING_ZIPLIST) return;
// 检查所有输入对象,看它们的字符串值是否超过了指定长度
for (i = start; i <= end; i++) {
if (sdsEncodedObject(argv[i]) &&
sdslen(argv[i]->ptr) > server.hash_max_ziplist_value)
{
// 将对象的编码转换成 REDIS_ENCODING_HT
hashTypeConvert(o, REDIS_ENCODING_HT);
break;
}
}
}
hashTypeTryObjectEncoding:当subject为hashtable编码时,尝试将o1,o2进行编码
void hashTypeTryObjectEncoding(robj *subject, robj **o1, robj **o2) {
if (subject->encoding == REDIS_ENCODING_HT) {
if (o1) *o1 = tryObjectEncoding(*o1);
if (o2) *o2 = tryObjectEncoding(*o2);
}
}
hashTypeGetFromZiplist:从ziplist找到field对应的值
int hashTypeGetFromZiplist(robj *o, robj *field,
unsigned char **vstr,
unsigned int *vlen,
long long *vll)
{
unsigned char *zl, *fptr = NULL, *vptr = NULL;
int ret;
// 确保编码正确
redisAssert(o->encoding == REDIS_ENCODING_ZIPLIST);
// 取出未编码的域
field = getDecodedObject(field);
// 遍历 ziplist ,查找域的位置
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);
}
}
decrRefCount(field);
// 从 ziplist 节点中取出值
if (vptr != NULL) {
ret = ziplistGet(vptr, vstr, vlen, vll);
redisAssert(ret);
return 0;
}
// 没找到
return -1;
}
hashTypeGetFromHashTable:从hashtable中找到键对应的值
int hashTypeGetFromHashTable(robj *o, robj *field, robj **value) {
dictEntry *de;
// 确保编码正确
redisAssert(o->encoding == REDIS_ENCODING_HT);
// 在字典中查找域(键)
de = dictFind(o->ptr, field);
// 键不存在
if (de == NULL) return -1;
// 取出域(键)的值
*value = dictGetVal(de);
// 成功找到
return 0;
}
hashTypeGetObject:这是一个多态函数,根据哈希对象是ziplist还是hashtable编码采用不同的获取方式
robj *hashTypeGetObject(robj *o, robj *field) {
robj *value = NULL;
// 从 ziplist 中取出值
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) {
// 创建值对象
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");
}
// 返回值对象,或者 NULL
return value;
}
hashTypeExists:检查给定的键是否在当前哈希对象中
int hashTypeExists(robj *o, robj *field) {
// 检查 ziplist
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) return 1;
// 检查字典
} else if (o->encoding == REDIS_ENCODING_HT) {
robj *aux;
if (hashTypeGetFromHashTable(o, field, &aux) == 0) return 1;
// 未知编码
} else {
redisPanic("Unknown hash encoding");
}
// 不存在
return 0;
}
hashTypeSet:将指定的键值对添加到哈希对象中
int hashTypeSet(robj *o, robj *field, robj *value) {
int update = 0;
// 添加到 ziplist
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *zl, *fptr, *vptr;
// 解码成字符串或者数字
field = getDecodedObject(field);
value = getDecodedObject(value);
// 遍历整个 ziplist ,尝试查找并更新 field (如果它已经存在的话)
zl = o->ptr;
fptr = ziplistIndex(zl, ZIPLIST_HEAD);
if (fptr != NULL) {
// 定位到域 field
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 */
// 将新的 field-value 对推入到 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 */
// 检查在添加操作完成之后,是否需要将 ZIPLIST 编码转换成 HT 编码
if (hashTypeLength(o) > server.hash_max_ziplist_entries)
hashTypeConvert(o, REDIS_ENCODING_HT);
// 添加到字典
} else if (o->encoding == REDIS_ENCODING_HT) {
// 添加或替换键值对到字典
// 添加返回 1 ,替换返回 0
if (dictReplace(o->ptr, field, value)) { /* Insert */
incrRefCount(field);
} else { /* Update */
update = 1;
}
incrRefCount(value);
} else {
redisPanic("Unknown hash encoding");
}
// 更新/添加指示变量
return update;
}
hashTypeDelete:从哈希对象中删除指定的键值对
int hashTypeDelete(robj *o, robj *field) {
int deleted = 0;
// 从 ziplist 中删除
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *zl, *fptr;
field = getDecodedObject(field);
zl = o->ptr;
fptr = ziplistIndex(zl, ZIPLIST_HEAD);
if (fptr != NULL) {
// 定位到域
fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);
if (fptr != NULL) {
// 删除域和值
zl = ziplistDelete(zl,&fptr);
zl = ziplistDelete(zl,&fptr);
o->ptr = zl;
deleted = 1;
}
}
decrRefCount(field);
// 从字典中删除
} else if (o->encoding == REDIS_ENCODING_HT) {
if (dictDelete((dict*)o->ptr, field) == REDIS_OK) {
deleted = 1;
/* Always check if the dictionary needs a resize after a delete. */
// 删除成功时,看字典是否需要收缩
if (htNeedsResize(o->ptr)) dictResize(o->ptr);
}
} else {
redisPanic("Unknown hash encoding");
}
return deleted;
}
hsetCommand:hset命令
void hsetCommand(redisClient *c) {
int update;
robj *o;
// 取出或新创建哈希对象
if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
// 如果需要的话,转换哈希对象的编码
hashTypeTryConversion(o,c->argv,2,3);
// 编码 field 和 value 对象以节约空间
hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
// 设置 field 和 value 到 hash
update = hashTypeSet(o,c->argv[2],c->argv[3]);
// 返回状态:显示 field-value 对是新添加还是更新
addReply(c, update ? shared.czero : shared.cone);
// 发送键修改信号
signalModifiedKey(c->db,c->argv[1]);
// 发送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hset",c->argv[1],c->db->id);
// 将服务器设为脏
server.dirty++;
}
hsetnxCommand:hsetnx命令
void hsetnxCommand(redisClient *c) {
robj *o;
// 取出或新创建哈希对象
if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
// 如果需要的话,转换哈希对象的编码
hashTypeTryConversion(o,c->argv,2,3);
// 如果 field-value 对已经存在
// 那么回复 0
if (hashTypeExists(o, c->argv[2])) {
addReply(c, shared.czero);
// 否则,设置 field-value 对
} else {
// 对 field 和 value 对象编码,以节省空间
hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
// 设置
hashTypeSet(o,c->argv[2],c->argv[3]);
// 回复 1 ,表示设置成功
addReply(c, shared.cone);
// 发送键修改信号
signalModifiedKey(c->db,c->argv[1]);
// 发送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hset",c->argv[1],c->db->id);
// 将数据库设为脏
server.dirty++;
}
}
hmsetCommand:添加多个键值对的命令
void hmsetCommand(redisClient *c) {
int i;
robj *o;
// field-value 参数必须成对出现
if ((c->argc % 2) == 1) {
addReplyError(c,"wrong number of arguments for HMSET");
return;
}
// 取出或新创建哈希对象
if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
// 如果需要的话,转换哈希对象的编码
hashTypeTryConversion(o,c->argv,2,c->argc-1);
// 遍历并设置所有 field-value 对
for (i = 2; i < c->argc; i += 2) {
// 编码 field-value 对,以节约空间
hashTypeTryObjectEncoding(o,&c->argv[i], &c->argv[i+1]);
// 设置
hashTypeSet(o,c->argv[i],c->argv[i+1]);
}
// 向客户端发送回复
addReply(c, shared.ok);
// 发送键修改信号
signalModifiedKey(c->db,c->argv[1]);
// 发送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hset",c->argv[1],c->db->id);
// 将数据库设为脏
server.dirty++;
}
hincrbyCommand:为指定键的值进行增
void hincrbyCommand(redisClient *c) {
long long value, incr, oldvalue;
robj *o, *current, *new;
// 取出 incr 参数的值,并创建对象
if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != REDIS_OK) return;
// 取出或新创建哈希对象
if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
// 取出 field 的当前值
if ((current = hashTypeGetObject(o,c->argv[2])) != NULL) {
// 取出值的整数表示
if (getLongLongFromObjectOrReply(c,current,&value,
"hash value is not an integer") != REDIS_OK) {
decrRefCount(current);
return;
}
decrRefCount(current);
} else {
// 如果值当前不存在,那么默认为 0
value = 0;
}
// 检查计算是否会造成溢出
oldvalue = value;
if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
(incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
addReplyError(c,"increment or decrement would overflow");
return;
}
// 计算结果
value += incr;
// 为结果创建新的值对象
new = createStringObjectFromLongLong(value);
// 编码值对象
hashTypeTryObjectEncoding(o,&c->argv[2],NULL);
// 关联键和新的值对象,如果已经有对象存在,那么用新对象替换它
hashTypeSet(o,c->argv[2],new);
decrRefCount(new);
// 将计算结果用作回复
addReplyLongLong(c,value);
// 发送键修改信号
signalModifiedKey(c->db,c->argv[1]);
// 发送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hincrby",c->argv[1],c->db->id);
// 将数据库设为脏
server.dirty++;
}
hgetCommand:hget命令
void hgetCommand(redisClient *c) {
robj *o;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
checkType(c,o,REDIS_HASH)) return;
// 取出并返回域的值
addHashFieldToReply(c, o, c->argv[2]);
}
hdelCommand:hdel方法
void hdelCommand(redisClient *c) {
robj *o;
int j, deleted = 0, keyremoved = 0;
// 取出对象
if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,REDIS_HASH)) return;
// 删除指定域值对
for (j = 2; j < c->argc; j++) {
if (hashTypeDelete(o,c->argv[j])) {
// 成功删除一个域值对时进行计数
deleted++;
// 如果哈希已经为空,那么删除这个对象
if (hashTypeLength(o) == 0) {
dbDelete(c->db,c->argv[1]);
keyremoved = 1;
break;
}
}
}
// 只要有至少一个域值对被修改了,那么执行以下代码
if (deleted) {
// 发送键修改信号
signalModifiedKey(c->db,c->argv[1]);
// 发送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hdel",c->argv[1],c->db->id);
// 发送事件通知
if (keyremoved)
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],
c->db->id);
// 将数据库设为脏
server.dirty += deleted;
}
// 将成功删除的域值对数量作为结果返回给客户端
addReplyLongLong(c,deleted);
}