redis中的hash也是我们使用中的高频数据结构,它的构造基本上和编程语言中的HashTable,Dictionary大同小异,如果大家往后有什么逻辑需要用

Dictionary存放的话,可以根据场景优先考虑下redis哦,起码可以装装逼嘛,现在我默认你已经有装逼的冲动了,打开redis手册,看看有哪些我们用得到

的装逼方法。

一:常用方法

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

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

说的HSet,它的格式如下:

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

  1. [administrator@localhost redis-3.0.]$ src/redis-cli
  2. 127.0.0.1:> clear
  3.  
  4. 127.0.0.1:> hset person name jack
  5. (integer)
  6. 127.0.0.1:> hset person age
  7. (integer)
  8. 127.0.0.1:> hset person sex famale
  9. (integer)
  10. 127.0.0.1:> hgetall person
  11. ) "name"
  12. ) "jack"
  13. ) "age"
  14. ) ""
  15. ) "sex"
  16. ) "famale"
  17. 127.0.0.1:> hkeys person
  18. ) "name"
  19. ) "age"
  20. ) "sex"
  21. 127.0.0.1:> hvals person
  22. ) "jack"
  23. ) ""
  24. ) "famale"
  25. 127.0.0.1:>

或许有人看了上面的console有一点疑惑,那就是前面有几个参数,比如person,name啦,然后才是value,如果你看了第一篇的话,你大概就明白了,

其实在redis的这个层面,它永远只有一个键,一个值,这个键永远都是字符串对象,也就是SDS对象,而值的种类就多了,有字符串对象,有队列对象,

还有这篇的hash对象,往后的有序集合对象等等,如果你还不明白的话,转化为C#语言就是。

  1. var person=new Dictionary<string,string>();
  2. person.Add("name","jack");
  3. ....

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

二:探索原理

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

  1. 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;
  11.  
  12. typedef struct dictType {
  13. unsigned int (*hashFunction)(const void *key);
  14. void *(*keyDup)(void *privdata, const void *key);
  15. void *(*valDup)(void *privdata, const void *obj);
  16. int (*keyCompare)(void *privdata, const void *key1, const void *key2);
  17. void (*keyDestructor)(void *privdata, void *key);
  18. void (*valDestructor)(void *privdata, void *obj);
  19. } dictType;
  20.  
  21. /* This is our hash table structure. Every dictionary has two of this as we
  22. * implement incremental rehashing, for the old to the new 0. */
  23. typedef struct dictht {
  24. dictEntry **table;
  25. unsigned long size;
  26. unsigned long sizemask;
  27. unsigned long used;
  28. } dictht;
  29.  
  30. typedef struct dict {
  31. dictType *type;
  32. void *privdata;
  33. dictht ht[];
  34. long rehashidx; /* rehashing not in progress if rehashidx == -1 */
  35. int iterators; /* number of iterators currently running */
  36. } dict;
  37.  
  38. /* If safe is set to 1 this is a safe iterator, that means, you can call
  39. * dictAdd, dictFind, and other functions against the dictionary even while
  40. * iterating. Otherwise it is a non safe iterator, and only dictNext()
  41. * should be called while iterating. */
  42. typedef struct dictIterator {
  43. dict *d;
  44. long index;
  45. int table, safe;
  46. dictEntry *entry, *nextEntry;
  47. /* unsafe iterator fingerprint for misuse detection. */
  48. long long fingerprint;
  49. } dictIterator;

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

1. dict结构

  1. typedef struct dict {
  2. dictType *type;
  3. void *privdata;
  4. dictht ht[];
  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,从上面你也可以看到,它是有枚举结构定义的,如下:

  1. 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结构

  1. 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结构

  1. 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 就是hash的value。

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

这个原理和C#中的Dictionary还是一样一样的。

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

好了,就此打住,去公司了。

15天玩转redis —— 第四篇 哈希对象类型的更多相关文章

  1. 15天玩转redis —— 第三篇 无敌的列表类型

     据说60%的人使用redis看重的是redis中的list类型,那这个list有什么用呢???不用我说大家都明白,做队列使用呗,为什么用它呢,很简单呗, 因为有了它我就不需要专门的MQ产品啦,比如说 ...

  2. 15天玩转redis —— 第六篇 有序集合类型

    今天我们说一下Redis中最后一个数据类型 “有序集合类型”,回首之前学过的几个数据结构,不知道你会不会由衷感叹,开源的世界真好,写这 些代码的好心人真的要一生平安哈,不管我们想没想的到的东西,在这个 ...

  3. 15天玩转redis —— 第五篇 集合对象类型

    这篇我们来看看Redis五大类型中的第四大类型:“集合类型”,集合类型还是蛮有意思的,第一个是因为它算是只使用key的Dictionary简易版, 这样说来的话,它就比Dictionary节省很多内存 ...

  4. 15天玩转redis —— 第十篇 对快照模式的深入分析

    我们知道redis是带有持久化这个能力了,那到底持久化成到哪里,持久化成啥样呢???这篇我们一起来寻求答案. 一:快照模式 或许在用Redis之初的时候,就听说过redis有两种持久化模式,第一种是S ...

  5. 15天玩转redis —— 第十一篇 让你彻底了解RDB存储结构

    接着上一篇说,这里我们来继续分析一下RDB文件存储结构,首先大家都知道RDB文件是在redis的“快照”的模式下才会产生,那么如果 我们理解了RDB文件的结构,是不是让我们对“快照”模式能做到一个心中 ...

  6. 15天玩转redis —— 第八篇 你不得不会的事务玩法

    我们都知道redis追求的是简单,快速,高效,在这种情况下也就拒绝了支持window平台,学sqlserver的时候,我们知道事务还算是个比较复杂的东西, 所以这吊毛要是照搬到redis中去,理所当然 ...

  7. 15天玩转redis —— 第七篇 同事的一次缓存操作引起对慢查询的认识

    上个星期同事做一个业务模块,需要将一个80M的数据存入到redis缓存中,想法总是好的,真操作的时候遇到了HSet超时,我们使用的是C#的 StackExchange.Redis驱动. <red ...

  8. 15天玩转redis —— 第二篇 基础的字符串类型

    我们都知道redis是采用C语言开发,那么在C语言中表示string都是采用char[]数组的,然后你可能会想,那还不简单,当我执行如下命令,肯定是直 接塞给char[]数组的. 如果你真的这么想的话 ...

  9. 15天玩转redis —— 第九篇 发布/订阅模式

    本系列已经过半了,这一篇我们来看看redis好玩的发布订阅模式,其实在很多的MQ产品中都存在这样的一个模式,我们常听到的一个例子 就是邮件订阅的场景,什么意思呢,也就是说100个人订阅了你的博客,如果 ...

随机推荐

  1. Winform文件下载之断点续传

    在本系列的前两篇文章中,分别向大家介绍了用于完成下载任务的 WebClinet 和 WinINet 的基本用法和一些实用技巧. 今天来为大家讲述下载过程中最常遇到的断点续传问题. 首先明确一点,本文所 ...

  2. Axis 1 https(SSL) client 证书验证错误ValidatorException workaround

    Axis 1.x 编写的client在测试https的webservice的时候, 由于client 代码建立SSL连接的时候没有对truststore进行设置,在与https部署的webservic ...

  3. 我心中的核心组件(可插拔的AOP)~第十三回 实现AOP的拦截组件Unity.Interception

    回到目录 说在前 本节主要说一下Unity家族里的拦截组件,对于方法拦截有很多组件提供,基本上每个Ioc组件都有对它的实现,如autofac,它主要用在orchard项目里,而castle也有以拦截的 ...

  4. 爱上MVC~为非法进行Action的用户提供HttpStatusCodeResult

    回到目录 对一MVC来说,它有Controller和Action,其中Action用来为页面提供数据和相关逻辑,并最后将页面渲染出来,而有些action是需要一些参数的,如文章的最终页,可能需要一个I ...

  5. Android开发学习之路-SimpleAdapter源码分析学习

    今天在课堂上,老师用到了SimpleAdapter,然后女神在边上问我为什么这个SimpleAdapter不能做到我app那种带有进度条的效果,言语说不清,然后就开始看源代码,发现这个Adapter的 ...

  6. flow.ci + Github + Slack 一步步搭建 Python 自动化持续集成

    理想的程序员必须懒惰,永远追随自动化法则.Automating shapes smarter future. 在一个 Python 项目的开发过程中可能会做的事情:编译.手动或自动化测试.部署环境配置 ...

  7. WPF入门教程系列五——Window 介绍

    一.窗体类基本概念 对于WPF应用程序,在Visual Studio和Expression Blend中,自定义的窗体均继承System.Windows.Window类.用户通过窗口与 Windows ...

  8. 跨域API

    跨域API 简单跨域请求 只需要简单的设置允许跨域就可以了 def set_default_headers(self): self.set_header('Access-Control-Allow-O ...

  9. 通过bootstrap来学习less

    很早之前就听说过less了,但是一直拖着没去学习.最近抽空看了less,其实语法很简单,看一遍基本就知道怎么用了.平时自己写页面用less的话,感觉是方便了些,但是难道less的好处就只是这样? 刚好 ...

  10. Sqlserver分页的问题

    好久没有用SqlServer了,今天写了一个分页,遇到了小问题,本着温故而知新的道理,再来随便写些什么吧. 语句是这样的 string sql=“select * from ( select*,(ro ...