理解 redis 中的 哈希对象类型
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 中的 哈希对象类型相关推荐
- redis 中 Hash哈希介绍 及常用命令 (附有示例)
目录 一.Redis中Hash介绍 二.常用命令 三.示例 hset hget hmset .. hexists hkeys hvals hincrbu hsetnx 四.redis中Hash底层 ...
- 「Redis数据结构」哈希对象(Hash)
「Redis数据结构」哈希对象(Hash) 文章目录 「Redis数据结构」哈希对象(Hash) 一.概述 二.编码 ZipList HashTable 三.编码转换 一.概述 Redis中hash对 ...
- 理解 redis 中的 集合对象类型
这篇我们来看看Redis五大类型中的第四大类型:集合类型,集合类型还是蛮有意思的,第一个是因为它算是只使用key的Dictionary简易版,这样说来的话,它就比Dictionary节省很多内存消耗, ...
- 一文带你深入理解Redis中的底层数据结构,再也不怕不懂数据类型的底层了
数据结构前言 都说Redis快,因为什么呢?只是因为它是内存数据库,所有操作都是基于内存进行的吗?其实不然,这与它的数据结构也是密不可分的.下面我们就来了解一下Redis的数据结构. Redis 数据 ...
- 深入理解Java中的不可变对象
二哥,你能给我说说为什么 String 是 immutable 类(不可变对象)吗?我想研究它,想知道为什么它就不可变了,这种强烈的愿望就像想研究浩瀚的星空一样.但无奈自身功力有限,始终觉得雾里看花终 ...
- python一切皆对象的理解_python中为什么万物皆对象
在python环境中我们输出一个没有声明的变量a 为什么会报name 'a' is not defined.为什么是name 而不是variable. 因为在python中把所有的函数名,变量名.类型 ...
- 深入理解redis中的lua脚本
本文来说下redis中的lua脚本 文章目录 概述 Lua简介 使用Lua脚本的好处 Redis+Lua实现限流 本文小结 概述 今天讲一些redis和lua脚本的相关的东西,lua这个脚本是一个好东 ...
- 深入理解Javascript中构造函数和原型对象的区别
在 Javascript中prototype属性的详解 这篇文章中,详细介绍了构造函数的缺点以及原型(prototype),原型链(prototype chain),构造函数(constructor) ...
- 怎么确定迭代器后面还有至少两个值_如何理解Python中的可迭代对象、迭代器和生成器
▍前言 在讨论可迭代对象.迭代器和生成器之前,先说明一下迭代器模式(iterator pattern),维基百科这么解释: 迭代器是一种最简单也最常见的设计模式.它可以让用户透过特定的接口巡访容器中的 ...
最新文章
- java drools5_Java Drools5.1 规则流基础【示例】
- 两大主流IT媒体全程解秘我的“心路历程”
- android dtb文件位置_android MSM8974 上DeviceTree简介
- 关于优酷开放SDK中setOnRealVideoStartListener
- Python实现局域网(Socket)通信
- 『原创』.Net CF下ListView的数据绑定
- 异质图神经网络及其在电商领域中的应用
- arduino灯光装置_【pinpong库控制硬件】之Arduino uno-调光台灯
- bzoj千题计划315:bzoj3172: [Tjoi2013]单词(AC自动机)
- 智能优化算法之海豚回声定位(Dolphin echolocation,DE)
- 第11章:项目风险管理——章节真题
- QQ出现大规模盗号,qq被盗发布不良信息怎么办
- Python3 粗略计算PI的值
- 线性加权平均 c语言程序,【求助】编的一个加权平均分计算程序,总是出错。求高手指点~...
- 性价比很高的三款ePUB阅读器(转载)
- 游戏开发中为什么要控制模型的面数
- 基于Android Q的OTA包制作/签名(包含差分包)
- 腾达ac1200远端服务器无响应,连3个磊科MG1200ac必死机
- 微信统一下单prepay_id为空php,微信支付-普通下单开发者文档
- 行驶证识别/行驶证OCR识别全方位解析