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

一:常用方法

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

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

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


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

或许有人看了上面的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[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 {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个属性。

  • 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值一样一样的。

  • 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;
  • 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结构图。

理解 redis 中的 哈希对象类型相关推荐

  1. redis 中 Hash哈希介绍 及常用命令 (附有示例)

    目录 一.Redis中Hash介绍 二.常用命令 三.示例 hset hget hmset   .. hexists hkeys hvals hincrbu hsetnx 四.redis中Hash底层 ...

  2. 「Redis数据结构」哈希对象(Hash)

    「Redis数据结构」哈希对象(Hash) 文章目录 「Redis数据结构」哈希对象(Hash) 一.概述 二.编码 ZipList HashTable 三.编码转换 一.概述 Redis中hash对 ...

  3. 理解 redis 中的 集合对象类型

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

  4. 一文带你深入理解Redis中的底层数据结构,再也不怕不懂数据类型的底层了

    数据结构前言 都说Redis快,因为什么呢?只是因为它是内存数据库,所有操作都是基于内存进行的吗?其实不然,这与它的数据结构也是密不可分的.下面我们就来了解一下Redis的数据结构. Redis 数据 ...

  5. 深入理解Java中的不可变对象

    二哥,你能给我说说为什么 String 是 immutable 类(不可变对象)吗?我想研究它,想知道为什么它就不可变了,这种强烈的愿望就像想研究浩瀚的星空一样.但无奈自身功力有限,始终觉得雾里看花终 ...

  6. python一切皆对象的理解_python中为什么万物皆对象

    在python环境中我们输出一个没有声明的变量a 为什么会报name 'a' is not defined.为什么是name 而不是variable. 因为在python中把所有的函数名,变量名.类型 ...

  7. 深入理解redis中的lua脚本

    本文来说下redis中的lua脚本 文章目录 概述 Lua简介 使用Lua脚本的好处 Redis+Lua实现限流 本文小结 概述 今天讲一些redis和lua脚本的相关的东西,lua这个脚本是一个好东 ...

  8. 深入理解Javascript中构造函数和原型对象的区别

    在 Javascript中prototype属性的详解 这篇文章中,详细介绍了构造函数的缺点以及原型(prototype),原型链(prototype chain),构造函数(constructor) ...

  9. 怎么确定迭代器后面还有至少两个值_如何理解Python中的可迭代对象、迭代器和生成器

    ▍前言 在讨论可迭代对象.迭代器和生成器之前,先说明一下迭代器模式(iterator pattern),维基百科这么解释: 迭代器是一种最简单也最常见的设计模式.它可以让用户透过特定的接口巡访容器中的 ...

最新文章

  1. java drools5_Java Drools5.1 规则流基础【示例】
  2. 两大主流IT媒体全程解秘我的“心路历程”
  3. android dtb文件位置_android MSM8974 上DeviceTree简介
  4. 关于优酷开放SDK中setOnRealVideoStartListener
  5. Python实现局域网(Socket)通信
  6. 『原创』.Net CF下ListView的数据绑定
  7. 异质图神经网络及其在电商领域中的应用
  8. arduino灯光装置_【pinpong库控制硬件】之Arduino uno-调光台灯
  9. bzoj千题计划315:bzoj3172: [Tjoi2013]单词(AC自动机)
  10. 智能优化算法之海豚回声定位(Dolphin echolocation,DE)
  11. 第11章:项目风险管理——章节真题
  12. QQ出现大规模盗号,qq被盗发布不良信息怎么办
  13. Python3 粗略计算PI的值
  14. 线性加权平均 c语言程序,【求助】编的一个加权平均分计算程序,总是出错。求高手指点~...
  15. 性价比很高的三款ePUB阅读器(转载)
  16. 游戏开发中为什么要控制模型的面数
  17. 基于Android Q的OTA包制作/签名(包含差分包)
  18. 腾达ac1200远端服务器无响应,连3个磊科MG1200ac必死机
  19. 微信统一下单prepay_id为空php,微信支付-普通下单开发者文档
  20. 行驶证识别/行驶证OCR识别全方位解析

热门文章

  1. 嵌套函数,匿名函数,高阶函数
  2. eclipse新发现功能之dos和terminal(ssh连接)
  3. UVA - 11181 数学
  4. java.util.ListIterator
  5. 将数据库设置为运行在限制模式下
  6. DNS resolving 占用大量日志
  7. api游戏编程鼠标选择拖动_如何选择合适的游戏鼠标
  8. acm教程 java版_[转]ACM之java速成
  9. Kotlin 基础 - 数据类型
  10. sort命令详解及Nginx统计运用