欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

redis哈希类型_动力节点Java学院整理

程序员文章站 2022-03-11 15:35:07
redis中的hash也是我们使用中的高频数据结构,它的构造基本上和编程语言中的hashtable,dictionary大同小异,如果大家往后有什么逻辑需要用diction...

redis中的hash也是我们使用中的高频数据结构,它的构造基本上和编程语言中的hashtable,dictionary大同小异,如果大家往后有什么逻辑需要用dictionary存放的话,可以根据场景优先考虑下redis哦。

一:常用方法

  只要是一个数据结构,最基础的永远是curd,redis中的insert和update,永远只需要set来替代,比如下面的hset,如下图:

redis哈希类型_动力节点Java学院整理

就好像java中的类和方法,知道传递一些啥参数就ok了,就比如要说的hset,它的格式如下:

redis哈希类型_动力节点Java学院整理

接下来我在centos里面操作一下,

[administrator@localhost redis-3.0.5]$ src/redis-cli
.0.0.1:6379> clear
.0.0.1:6379> hset person name jack
(integer) 1
.0.0.1:6379> hset person age 20
(integer) 1
.0.0.1:6379> hset person sex famale
(integer) 1
.0.0.1:6379> hgetall person
) "name"
) "jack"
) "age"
) "20"
) "sex"
) "famale"
.0.0.1:6379> hkeys person
) "name"
) "age"
) "sex"
.0.0.1:6379> hvals person
) "jack"
) "20"
) "famale"
.0.0.1:6379>

或许有人看了上面的console有一点疑惑,那就是前面有几个参数,比如person,name啦,然后才是value,其实在redis的这个层面,它永远只有一个键,一个值,这个键永远都是字符串对象,也就是sds对象,而值的种类就多了,有字符串对象,有队列对象,还有这篇的hash对象,往后的有序集合对象等等,如果你还不明白的话,转化为java语言就是。

 map<string,string> person=new hashmap<string,string>();
 person.add("name","jack");
 ....

调用方法就是这么的简单,关键在于时不时的需要你看一看手册,其实最重要的是了解下它在redis源码中的原理就好了。

二:探索原理

  hash的源代码是在dict.h源代码里面,枚举如下:

typedef struct dictentry {
 void *key;
 union {
 void *val;
 uint64_t u64;
 int64_t s64;
 double d;
 } v;
 struct dictentry *next;
} dictentry;
typedef struct dicttype {
 unsigned int (*hashfunction)(const void *key);
 void *(*keydup)(void *privdata, const void *key);
 void *(*valdup)(void *privdata, const void *obj);
 int (*keycompare)(void *privdata, const void *key1, const void *key2);
 void (*keydestructor)(void *privdata, void *key);
 void (*valdestructor)(void *privdata, void *obj);
} dicttype;
/* this is our hash table structure. every dictionary has two of this as we
 * implement incremental rehashing, for the old to the new 0. */
typedef struct dictht {
 dictentry **table;
 unsigned long size;
 unsigned long sizemask;
 unsigned long used;
} dictht;
typedef struct dict {
 dicttype *type;
 void *privdata;
 dictht ht[2];
 long rehashidx; /* rehashing not in progress if rehashidx == -1 */
 int iterators; /* number of iterators currently running */
} dict;
/* if safe is set to 1 this is a safe iterator, that means, you can call
 * dictadd, dictfind, and other functions against the dictionary even while
 * iterating. otherwise it is a non safe iterator, and only dictnext()
 * should be called while iterating. */
typedef struct dictiterator {
 dict *d;
 long index;
 int table, safe;
 dictentry *entry, *nextentry;
 /* unsafe iterator fingerprint for misuse detection. */
 long long fingerprint;
} dictiterator;

上面就是我们使用hash的源代码数据结构,接下来我来撸一撸其中的逻辑关系。

 dict结构

 typedef struct dict {
 dicttype *type;
 void *privdata;
 dictht ht[2];
 long rehashidx; /* rehashing not in progress if rehashidx == -1 */
 int iterators; /* number of iterators currently running */
 } dict;

这个结构是hash的真正的底层数据结构,可以看到其中有5个属性。 

<1> dicttype *type

   可以看到它的类型是dicttype,从上面你也可以看到,它是有枚举结构定义的,如下:

 typedef struct dicttype {
 unsigned int (*hashfunction)(const void *key);
 void *(*keydup)(void *privdata, const void *key);
 void *(*valdup)(void *privdata, const void *obj);
 int (*keycompare)(void *privdata, const void *key1, const void *key2);
 void (*keydestructor)(void *privdata, void *key);
 void (*valdestructor)(void *privdata, void *obj);
 } dicttype;

从上面这个数据结构中你可以看到里面都是一些方法,但是有一个非常重要的方法,那就是第一个hashfunction,可以看到它就是计算hash值的,跟java中的求hash值差不多。 

<2> dictht ht[2]

       你可能会疑问,为什么这个属性是2个大小的数组呢,其实正真使用的是ht[0],而ht[1]是用于扩容hash表时的暂存数组,这一点也很奇葩,同时也很精妙,redis为什么会这么做呢???仔细想想你可能会明白,扩容有两种方法,要么一次性扩容,要么渐进性扩容,后面这种扩容是什么意思呢?就是我在扩容的同时不影响前端的curd,我慢慢的把数据从ht[0]转移到ht[1]中,同时rehashindex来记录转移的情况,当全部转移完成之后,将ht[1]改成ht[0]使用,就这么简单。 

 dicth结构

 typedef struct dictht {
dictentry **table;
 unsigned long size;
 unsigned long sizemask;
 unsigned long used;
 } dictht;

<1> dictentry **table;

       从上面这个结构体中,你可以看到一个非常重要的属性: dictentry **table, 其中table是一个数组,数组类型是dictentry,既然是一个数组,那后面的三个属性就好理解了,size是数组的大小,sizemask和数组求模有关,used记录数组中已使用的大小,现在我们把注意力放在dictentry这个数组实体类型上面。 

dictentry结构

 typedef struct dictentry {
 void *key;
 union {
  void *val;
  uint64_t u64;
 int64_t s64;
  double d;
 } v;
 struct dictentry *next;
 } dictentry;

从这个数据结构上面你可以看到有三个大属性。

第一个就是:   *key:它就是hash表中的key。

第二个就是:    union的*val 就是hash的value。

第三个就是:    *next就是为了防止hash冲突采用的挂链手段。 

如果总结上面描述的话,我可以画出如下的hash结构图。

redis哈希类型_动力节点Java学院整理