15天玩转redis —— 第四篇 哈希对象类型
redis中的hash也是我们使用中的高频数据结构,它的构造基本上和编程语言中的HashTable,Dictionary大同小异,如果大家往后有什么逻辑需要用
Dictionary存放的话,可以根据场景优先考虑下redis哦,起码可以装装逼嘛,现在我默认你已经有装逼的冲动了,打开redis手册,看看有哪些我们用得到
的装逼方法。
一:常用方法
只要是一个数据结构,最基础的永远是CURD,redis中的insert和update,永远只需要set来替代,比如下面的Hset,如下图:

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

接下来我在CentOS里面操作一下,
[administrator@localhost redis-3.0.]$ src/redis-cli
127.0.0.1:> clear 127.0.0.1:> hset person name jack
(integer)
127.0.0.1:> hset person age
(integer)
127.0.0.1:> hset person sex famale
(integer)
127.0.0.1:> hgetall person
) "name"
) "jack"
) "age"
) ""
) "sex"
) "famale"
127.0.0.1:> hkeys person
) "name"
) "age"
) "sex"
127.0.0.1:> hvals person
) "jack"
) ""
) "famale"
127.0.0.1:>
或许有人看了上面的console有一点疑惑,那就是前面有几个参数,比如person,name啦,然后才是value,如果你看了第一篇的话,你大概就明白了,
其实在redis的这个层面,它永远只有一个键,一个值,这个键永远都是字符串对象,也就是SDS对象,而值的种类就多了,有字符串对象,有队列对象,
还有这篇的hash对象,往后的有序集合对象等等,如果你还不明白的话,转化为C#语言就是。
var person=new Dictionary<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[];
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 {
dictType *type;
void *privdata;
dictht ht[];
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值的,
跟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 {
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
} dictht;
<1> dictEntry **table;
从上面这个结构体中,你可以看到一个非常重要的属性: dictEntry **table, 其中table是一个数组,数组类型是dictEntry,既然是一个数组,
那后面的三个属性就好理解了,size是数组的大小,sizemask和数组求模有关,used记录数组中已使用的大小,现在我们把注意力放在dictEntry这
个数组实体类型上面。
3. 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冲突采用的挂链手段。
这个原理和C#中的Dictionary还是一样一样的。

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

好了,就此打住,去公司了。
15天玩转redis —— 第四篇 哈希对象类型的更多相关文章
- 15天玩转redis —— 第三篇 无敌的列表类型
据说60%的人使用redis看重的是redis中的list类型,那这个list有什么用呢???不用我说大家都明白,做队列使用呗,为什么用它呢,很简单呗, 因为有了它我就不需要专门的MQ产品啦,比如说 ...
- 15天玩转redis —— 第六篇 有序集合类型
今天我们说一下Redis中最后一个数据类型 “有序集合类型”,回首之前学过的几个数据结构,不知道你会不会由衷感叹,开源的世界真好,写这 些代码的好心人真的要一生平安哈,不管我们想没想的到的东西,在这个 ...
- 15天玩转redis —— 第五篇 集合对象类型
这篇我们来看看Redis五大类型中的第四大类型:“集合类型”,集合类型还是蛮有意思的,第一个是因为它算是只使用key的Dictionary简易版, 这样说来的话,它就比Dictionary节省很多内存 ...
- 15天玩转redis —— 第十篇 对快照模式的深入分析
我们知道redis是带有持久化这个能力了,那到底持久化成到哪里,持久化成啥样呢???这篇我们一起来寻求答案. 一:快照模式 或许在用Redis之初的时候,就听说过redis有两种持久化模式,第一种是S ...
- 15天玩转redis —— 第十一篇 让你彻底了解RDB存储结构
接着上一篇说,这里我们来继续分析一下RDB文件存储结构,首先大家都知道RDB文件是在redis的“快照”的模式下才会产生,那么如果 我们理解了RDB文件的结构,是不是让我们对“快照”模式能做到一个心中 ...
- 15天玩转redis —— 第八篇 你不得不会的事务玩法
我们都知道redis追求的是简单,快速,高效,在这种情况下也就拒绝了支持window平台,学sqlserver的时候,我们知道事务还算是个比较复杂的东西, 所以这吊毛要是照搬到redis中去,理所当然 ...
- 15天玩转redis —— 第七篇 同事的一次缓存操作引起对慢查询的认识
上个星期同事做一个业务模块,需要将一个80M的数据存入到redis缓存中,想法总是好的,真操作的时候遇到了HSet超时,我们使用的是C#的 StackExchange.Redis驱动. <red ...
- 15天玩转redis —— 第二篇 基础的字符串类型
我们都知道redis是采用C语言开发,那么在C语言中表示string都是采用char[]数组的,然后你可能会想,那还不简单,当我执行如下命令,肯定是直 接塞给char[]数组的. 如果你真的这么想的话 ...
- 15天玩转redis —— 第九篇 发布/订阅模式
本系列已经过半了,这一篇我们来看看redis好玩的发布订阅模式,其实在很多的MQ产品中都存在这样的一个模式,我们常听到的一个例子 就是邮件订阅的场景,什么意思呢,也就是说100个人订阅了你的博客,如果 ...
随机推荐
- HTML5本地存储——IndexedDB(二:索引)
在HTML5本地存储——IndexedDB(一:基本使用)中介绍了关于IndexedDB的基本使用方法,很不过瘾,这篇我们来看看indexedDB的杀器——索引. 熟悉数据库的同学都知道索引的一个好处 ...
- 诡异的 未处理的IOErrorEvent 2035
今天游戏发布上线之后,总是随机的出现卡死. 换了个safari之后,看到抛了 IOErrorEvent. 问题是,我所有的Loader都加入了contentLoaderInfo监听.而抛出来的又没有堆 ...
- 自定义 Azure Table storage 查询过滤条件
本文是在Azure Table storage 基本用法一文的基础上,介绍如何自定义 Azure Table storage 的查询过滤条件.如果您还不太清楚 Azure Table storage ...
- 我心中的核心组件(可插拔的AOP)~第十五回 我的日志组件Logger.Core(策略,模版方法,工厂,单例等模式的使用)
回到目录 之前的讲过两篇关于日志组件的文章,分别是<第一回 日志记录组件之自主的Vlog>和<第三回 日志记录组件之log4net>,而今天主要说一下我自己开发的另一种日志 ...
- 带你走近AngularJS - 体验指令实例
带你走近AngularJS系列: 带你走近AngularJS - 基本功能介绍 带你走近AngularJS - 体验指令实例 带你走近AngularJS - 创建自定义指令 ------------- ...
- git忽略以点开头的文件夹
git忽略以点开头的文件夹 好像不是什么问题,可是我用的时候不好使,还是记录下 参考:http://www.oschina.net/question/1437985_2181276
- viewpage listview gridview加载本地大图多图OOM处理办法
很少上博客园写东西了. 最近在写公司项目,由于需要加载本地相册通过viewpager方式来加载, 最后发现直接进入界面就OOM了. 经过几天的整理最终搞定. 现在将加载本地和加载网络图片的缓存工具类贴 ...
- 快速入门系列--MVC--03控制器和IOC应用
Asp.net MVC也接触好久了,但由于自己一直主要负责后台,尤其是数据库方面的工作对于该框架并没有一个很好的了解,尤其是蒋金楠大师的ASP.NET MVC4框架剖析一书都买了2年多了,真正认真看过 ...
- Nokia Lumia通过电脑来升级Windows Phone 8.1
现在基本上所有lumia都推送了WP 8.1了,不过,有些朋友说在更新过程中常常断线,导致要重新下载.不知道是不是我的人品比较正能量,我从预览版升级,到正式版升级,都没有出现断网现象,每次都能顺利更新 ...
- Supplemental Logging
Supplemental Logging分为两种:Database-Level Supplemental Logging和Table-Level Supplemental Logging,即数据库级别 ...