hget如何获取多个value_Redis(五):hash/hset/hget 命令源码解析

本文介绍了Redis中哈希(Hash)数据结构的相关操作,包括hset、hmset、hget等命令的用途、格式和实现原理。哈希表在Redis中用于存储对象,当字段数量或字段长度超过一定阈值时,会从ziplist转换为hashtable。hset用于设置单个字段值,hmset用于批量设置,而hget用于获取指定字段的值,这些操作的时间复杂度为O(1)。
摘要由CSDN通过智能技术生成

Redis作为nosql数据库,kv string型数据的支持是最基础的,但是如果仅有kv的操作,也不至于有redis的成功。(memcache就是个例子)

Redis除了string, 还有hash,list,set,zset。

所以,我们就来看看hash的相关操作实现吧。

首先,我们从作用上理解hash存在的意义:Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。从另一个方面来说是,hash可以聚合很多类似的属性,这是string中难以实现的。

所以,总体来说,hash的命令与string的命令差不太多。其操作手册如下:

1> hdel 命令:删除一个或多个哈希表字段

格式:HDEL key field2 [field2]

返回值:被成功删除字段的数量,不包括被忽略的字段。

2> hexists 命令:查看哈希表 key 中,指定的字段是否存在

格式:HEXISTS key field

返回值:如果哈希表含有给定字段,返回 1 。 如果哈希表不含有给定字段,或 key 不存在,返回 0 。

3> hget 命令:获取存储在哈希表中指定字段的值

格式:HGET key field

返回值:返回给定字段的值。如果给定的字段或 key 不存在时,返回 nil 。

4> hgetall 命令:获取在哈希表中指定 key 的所有字段和值

格式:HGETALL key

返回值:以列表形式返回哈希表的字段及字段值。 若 key 不存在,返回空列表。

5> hincrby 命令:为哈希表 key 中的指定字段的整数值加上增量 increment

格式:HINCRBY key field increment

返回值:执行 HINCRBY 命令之后,哈希表中字段的值。

6> hincrbyfloat 命令:为哈希表 key 中的指定字段的浮点数值加上增量 increment

格式:HINCRBYFLOAT key field increment

返回值:执行 Hincrbyfloat 命令之后,哈希表中字段的值。

7> hkeys 命令:获取所有哈希表中的字段

格式:HKEYS key

返回值:包含哈希表中所有字段的列表。 当 key 不存在时,返回一个空列表。

8> hlen 命令:获取哈希表中字段的数量

格式:HLEN key

返回值:哈希表中字段的数量。 当 key 不存在时,返回 0 。

9> hmget 命令:获取所有给定字段的值

格式:HMGET key field1 [field2]

返回值:一个包含多个给定字段关联值的表,表值的排列顺序和指定字段的请求顺序一样。

10> hmset 命令:同时将多个 field-value (域-值)对设置到哈希表 key 中

格式:HMSET key field1 value1 [field2 value2 ]

返回值:如果命令执行成功,返回 OK 。

11> hset 命令:将哈希表 key 中的字段 field 的值设为 value

格式:HSET key field value

返回值:如果字段是哈希表中的一个新建字段,并且值设置成功,返回 1 。 如果哈希表中域字段已经存在且旧值已被新值覆盖,返回 0 。

12> hsetnx 命令:只有在字段 field 不存在时,设置哈希表字段的值

格式:HSETNX key field value

返回值:设置成功,返回 1 。 如果给定字段已经存在且没有操作被执行,返回 0 。

13> hvals 命令:获取哈希表中所有值

格式:HVALS key

返回值:一个包含哈希表中所有值的表。 当 key 不存在时,返回一个空表。

14> hscan 命令:迭代哈希表中的键值对

格式:HSCAN key cursor [MATCH pattern] [COUNT count]

其中,有的是单kv操作有的是指量操作,有的是写操作有的是读操作。从实现上看,大体上很多命令是类似的:

比如: hset/hmset/hincrbyXXX 可以是一类的

比如:hget/hgetall/hexists/hkeys/hmget 可以是一类

注意:以上分法仅是为了让我们看清本质,对实际使用并无实际参考意义。

所以,我们就挑几个方法来解析下 hash 的操作实现吧。

零、hash数据结构

hash相关的命令定义如下:

{"hset",hsetCommand,4,"wmF",0,NULL,1,1,1,0,0},

{"hsetnx",hsetnxCommand,4,"wmF",0,NULL,1,1,1,0,0},

{"hget",hgetCommand,3,"rF",0,NULL,1,1,1,0,0},

{"hmset",hmsetCommand,-4,"wm",0,NULL,1,1,1,0,0},

{"hmget",hmgetCommand,-3,"r",0,NULL,1,1,1,0,0},

{"hincrby",hincrbyCommand,4,"wmF",0,NULL,1,1,1,0,0},

{"hincrbyfloat",hincrbyfloatCommand,4,"wmF",0,NULL,1,1,1,0,0},

{"hdel",hdelCommand,-3,"wF",0,NULL,1,1,1,0,0},

{"hlen",hlenCommand,2,"rF",0,NULL,1,1,1,0,0},

{"hstrlen",hstrlenCommand,3,"rF",0,NULL,1,1,1,0,0},

{"hkeys",hkeysCommand,2,"rS",0,NULL,1,1,1,0,0},

{"hvals",hvalsCommand,2,"rS",0,NULL,1,1,1,0,0},

{"hgetall",hgetallCommand,2,"r",0,NULL,1,1,1,0,0},

{"hexists",hexistsCommand,3,"rF",0,NULL,1,1,1,0,0},

{"hscan",hscanCommand,-3,"rR",0,NULL,1,1,1,0,0},

ziplist 数据结构

typedef structzlentry {

unsignedintprevrawlensize, prevrawlen;

unsignedintlensize, len;

unsignedintheadersize;

unsignedcharencoding;

unsignedchar *p;

} zlentry;#define ZIPLIST_BYTES(zl) (*((uint32_t*)(zl)))

#define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))

#define ZIPLIST_LENGTH(zl) (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))

#define ZIPLIST_HEADER_SIZE (sizeof(uint32_t)*2+sizeof(uint16_t))

#define ZIPLIST_END_SIZE (sizeof(uint8_t))

#define ZIPLIST_ENTRY_HEAD(zl) ((zl)+ZIPLIST_HEADER_SIZE)

#define ZIPLIST_ENTRY_TAIL(zl) ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)))

#define ZIPLIST_ENTRY_END(zl) ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-1)

hashtable 数据结构:

typedef structdict {

dictType*type;void *privdata;

dictht ht[2];long rehashidx; /*rehashing not in progress if rehashidx == -1*/unsignedlong iterators; /*number of iterators currently running*/} dict;

typedefstructdictht {

dictEntry**table;

unsignedlongsize;

unsignedlongsizemask;

unsignedlongused;

} dictht;

typedefstructdictEntry {void *key;void *val;struct dictEntry *next;

} dictEntry;

一、hset 设置单个 field -> value

“增删改查”中的“增改” 就是它了。

//t_hash.c, set key field value

void hsetCommand(client *c) {intupdate;

robj*o;//1. 查找hash的key是否存在,不存在则新建一个,然后在其上进行数据操作

if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;//2. 检查2-3个参数是否需要将简单版(ziplist)hash表转换为复杂的hash表,转换后的表通过 o->ptr 体现

hashTypeTryConversion(o,c->argv,2,3);//3. 添加kv到 o 的hash表中

update = hashTypeSet(o,c->argv[2]->ptr,c->argv[3]->ptr,HASH_SET_COPY);

addReply(c, update?shared.czero : shared.cone);//变更命令传播

signalModifiedKey(c->db,c->argv[1]);

notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);

server.dirty++;

}//1. 获取db外部的key, 即整体hash数据实例//t_hash.c

robj *hashTypeLookupWriteOrCreate(client *c, robj *key) {

robj*o = lookupKeyWrite(c->db,key);if (o ==NULL) {//此处创建的hashObject是以 ziplist 形式的

o =createHashObject();

dbAdd(c->db,key,o);

}else{//不是hash类型的键已存在,不可覆盖,返回错误

if (o->type !=OBJ_HASH) {

addReply(c,shared.wrongtypeerr);returnNULL;

}

}returno;

}//object.c, 创建hashObject, 以 ziplist 形式创建

robj *createHashObject(void) {

unsignedchar *zl =ziplistNew();

robj*o =createObject(OBJ_HASH, zl);

o->encoding =OBJ_ENCODING_ZIPLIST;returno;

}//ziplist.c

static unsigned char *createList() {

unsignedchar *zl =ziplistNew();

zl= ziplistPush(zl, (unsigned char*)"foo", 3, ZIPLIST_TAIL);

zl= ziplistPush(zl, (unsigned char*)"quux", 4, ZIPLIST_TAIL);

zl= ziplistPush(zl, (unsigned char*)"hello", 5, ZIPLIST_HEAD);

zl= ziplistPush(zl, (unsigned char*)"1024", 4, ZIPLIST_TAIL);returnzl;

}//2. 检查参数,是否需要将 ziplist 形式的hash表转换为真正的hash表/*Check the length of a number of objects to see if we need to convert a

* ziplist to a real hash. Note that we only check string encoded objects

* as their string length can be queried in constant time.*/

void hashTypeTryConversion(robj *o, robj **argv, int start, intend) {inti;if (o->encoding != OBJ_ENCODING_ZIPLIST) return;for (i = start; i <= end; i++) {//参数大于设置的 hash_max_ziplist_value (默认: 64)时,会直接将 ziplist 转换为 ht//OBJ_ENCODING_RAW, OBJ_ENCODING_EMBSTR//循环检查参数,只要发生了一次转换就结束检查(没必要继续了)

if (sdsEncodedObject(argv[i]) &&sdslen(argv[i]->ptr) >server.hash_max_ziplist_value)

{//这个转换过程很有意思,我们深入看看

hashTypeConvert(o, OBJ_ENCODING_HT);break;

}

}

}//t_hash.c, 转换编码方式 (如上, ziplist -> ht)

void hashTypeConvert(robj *o, intenc) {if (o->encoding ==OBJ_ENCODING_ZIPLIST) {//此处我们只处理这种情况

ha

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值