玩转redis —— 第4篇 哈希对象类型

redis中的hash也是我们使用中的高频数据结构, 它的构造基本上和编程语言中的HashTableDictionary大同小异, 如果大家往后有什么逻
辑需要用
Dictionary存放的话, 可以根据场景优先考虑下redis哦, 起码可以装装逼嘛, 现在我默认你已经有装逼的冲动了, 打开redis手册, 看看有哪些我们
用得到
的装逼方法。

一: 常用方法

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


前面几篇文章我都没有挑选一个方法仔细讲解, 其实也没什么好讲解的, 就好似C#中的一个类的一个方法而已, 知道传递一些啥参数就OK了, 就比
如要
说的
HSet, 它的格式如下:

HSET key field value

将哈希表 key 中的域field 的值设为 value

如果 key 不存在,一个新的哈希表被创建并进行HSET 操作。

如果域 field 已经存在于哈希表中,旧值将被覆盖。

可用版本:
>= 2.0.0
时间复杂度:
O(1)
返回值:
如果 field 是哈希表中的一个新建域,并且值设置成功,返回1
如果哈希表中域 field 已经存在且旧值已被新值覆盖,返回 0
redis> HSET website google "www.g.cn"       # 设置一个新域
(integer) 1

redis> HSET website google "www.google.com" # 覆盖一个旧域
(integer) 0
接下来我在CentOS里面操作一下
前提是连接上redis

在文件夹中的执行:src/redis-cli


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


调用方法就是这么的简单, 关键在于时不时的需要你看一看手册, 其实最重要的是了解下它在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的源代码数据结构, 接下来我来撸一撸其中的逻辑关系。

1. dict结构

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

这个结构是hash的真正的底层数据结构, 可以看到其中有5个属性。
<1> dictType *type
可以看到它的类型是dictType, 从上面你也可以看到, 它是有枚举结构定义的, 如下:
typedef struct dictType {
2 unsigned int (*hashFunction)(const void *key);
3 void *(*keyDup)(void *privdata, const void *key);
4 void *(*valDup)(void *privdata, const void *obj);
5 int (*keyCompare)(void *privdata, const void *key1, const void *key2);
6 void (*keyDestructor)(void *privdata, void *key);
7 void (*valDestructor)(void *privdata, void *obj);
8 } dictType;

从上面这个数据结构中你可以看到里面都是一些方法, 但是有一个非常重要的方法, 那就是第一个hashFunction, 可以看到它就是计算hash
的,
C#中的dictionary中求hash值一样一样的。

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

typedef struct dictht {
2 dictEntry **table;
3 unsigned long size;
4 unsigned long sizemask;
5 unsigned long used;
6 } dictht;

<1> dictEntry **table;
从上面这个结构体中, 你可以看到一个非常重要的属性: dictEntry **table, 其中table是一个数组, 数组类型是dictEntry, 既然是一个
数组,
那后面的三个属性就好理解了,
size是数组的大小,sizemask和数组求模有关,used记录数组中已使用的大小, 现在我们把注意力放在dictEntry
这 个
数组实体类型上面。
3. dictEntry结构
typedef struct dictEntry {2 void *key;
3 union {
4 void *val;
5 uint64_t u64;
6 int64_t s64;
7 double d;
8 } v;
9 struct dictEntry *next;
10 } dictEntry;

从这个数据结构上面你可以看到有三个大属性。
第一个就是:
*key: 它就是hash表中的key
第二个就是:
union*val就是hashvalue
第三个就是:
*next就是为了防止hash冲突采用的挂链手段。
这个原理和
C#中的Dictionary还是一样一样的。

不知道你看懂了没有, 如果总结上面描述的话, 我可以画出如下的hash结构图。
















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值