redis中的字典结构是怎样的?
点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人。
文章不定期同步公众号,还有各种一线大厂面试原题、我的学习系列笔记。
基础概念
redis支持的5种数据类型中,有hash类型,hash类型的底层采用字典结构(多对key-value)实现,而字典结构的代码实现=hashTable=用到了hash表
字典结构的实现
字典结构由三种结构组合而成:字典结构=dict+dictht+dictEntry,关系如下:
代码实现:
typedef struct dict {
dictType *type; //dictType也是一种数据结构,dictType结构中包含了一些函数,这些函数用来计算key的哈希值,进而用这个哈希值计算key在dictEntry型table数组中的下标
void *privdata; //私有数据,保存着dictType结构中函数的参数
dictht ht[2]; //两张哈希表:一张用来正常存储节点,一张用来在rehash时临时存储节点
long rehashidx; //rehash的标记:默认-1,当table数组中已有元素个数增加/减少到一定量时,整个字典结构将进行rehash给每个table元素重新分配位置,rehashidx代表rehash过程的进度,rehashidx==-1代表字典没有在进行rehash,rehashidx>-1代表该字典结构正在对进行rehash
} dict;
typedef struct dictht { //哈希表
dictEntry **table; //存放一个数组的地址,每个数组元素存放的是哈希表节点dictEntry的地址
unsigned long size; //哈希表的table数组长度小,初始化大小为4
unsigned long sizemask; //哈希表掩码sizemask的值总是等于(size-1),用于计算每个key在table中的下标位置=hash(key)&sizemask
unsigned long used; //记录哈希表的table中已有的节点数量(节点=dictEntry=键值对)。
} dictht;
typedef struct dictEntry {
void *key;//键
union{ //值
void *val;//值可以是指针
uint64_tu64;//值可以是无符号整数
int64_ts64;//值可以是带符号整数
} v;
struct dicEntry *next;//指向下个dictEntry节点:redis的字典结构采用链表法解决hash冲突,当table数组某个位置处已有元素时,该位置采用头插法形成链表解决hash冲突
} dictEntry;
用key计算(key-value)在table中的位置下标:
//1、先计算key的hash值:使用字典中计算key哈希值的函数
hash = dict结构->dictType结构的函数hashFunction(key)
//2、根据hash值与哈希表掩码sizemask进行“与运算”得到下标,x=(0或1)=两张哈希表中用来正常存储节点的那张哈希表的下标
index = hash & dict->ht[x].sizemask;
字典中的负载因子及rehash
先来看看几个重要概念:
redis字典中哈希表的rehash=扩展或收缩哈希表中的table数组长度;
负载因子=dict结构的ht[0].used/dict结构的ht[0].size=哈希表的table中已有的节点数量/哈希表的table数组长度;
bgsave操作:将redis内存中的数据以rdb的形式持久化到磁盘;
bgrewriteaof操作:将redis内存中的数据以aof的形式持久化到磁盘中,持久化成功后旧的aof文件会被替换(redis2.4之后aof由redis自动触发,而bgrewriteaof需要手动地触发)
redis中的哈希表什么时候进行扩展操作?
两种情况:
当没有bgsave操作 && 没有bgrewriteaof操作 && 负载因子>=1时;
当(正在bgsave操作 || 正在bgrewriteaof操作) && 负载因子>=5时;
负载因子为什么会>=1?因为当hash冲突时,新节点会以头插法的形式插入哈希表的table数组某个位置中形成链表,就会使table中节点的总数量>table数组长度,进而负载因子>=1
redis中的哈希表什么时候进行扩展操作?
当负载因子<=0.1时(如table数组长度经过多次扩展变为了16,某时刻table只有1个元素,1/16=0.0625<0.1,则哈希表进行rehash收缩table长度)
redis中的哈希表进行扩展或缩收缩的过程?
扩展:dict数据结构中有两张哈希表ht[2],ht[0]拿来正常地装redis数据(key-value),ht[1]用来进行rehash扩展时存放ht[0]的元素,直到ht[0]中所有元素被重新计算下标存放到ht[1]中的table数组为止,ht[1]的大小=第一个大于等于ht[0].used的2^n
收缩:同"扩展"操作一样,ht[1]拿来装在收缩过程中ht[0]的元素,ht[1]的大小=第一个大于等于ht[0].used的2^n(公式与扩展一样)
特点:
- 扩展或收缩完成后,释放哈希表ht[0],并把哈希表ht[1]置为ht[0],然后再重新分配一个新的空白ht[1]作为下次rehash使用
- 在rehash(扩展或收缩)过程中有两张哈希表,并且字典会同时使用两张哈希表:查找、删除、更新会同时操作两张哈希表(先在ht[0]中找,找不到再去ht[1]中找),'插入'则只操作ht[1]
- rehash过程是渐进式的,它采取分而治之的方法,以扩展为例,一开始先将rehashidx值由-1置为0代表rehash工作开始:
此时rehashidx为0,第一次对redis字典进行【添加、删除、查找或者更新】操作,则对ht[0]中table[rehashidx]=table[0]的数据重新计算下标放到ht[1]的table数组中,然后rehashidx自增为1;
此时rehashidx为1,第二次对redis字典进行【添加、删除、查找或者更新】操作,则对ht[0]中table[rehashidx]=table[1]的数据重新计算下标放到ht[1]的table数组中,然后rehashidx自增为1
......以此类推,直到ht[0]中的所有table元素都被重新计算下标rehash到ht[1]中为止,最后把rehashidx置为-1,此时rehash完成;
渐进式rehash可以把这个过程中的计算压力分摊到每次对字典进行【添加、删除、查找或者更新】操作的时候,避免在某一时刻对整个hash类型数据(redis的5中数据类型之一)进行庞大的rehash计算,进而避免了redis阻塞,唯一不好的地方就是在rehash时同时使用两个哈希表,导致redis内部使用量暴增
OK,如果文章哪里有错误或不足,欢迎各位留言。
创作不易,各位的「三连」是二少创作的最大动力!我们下期见!
redis中的字典结构是怎样的?的更多相关文章
- 快速整明白Redis中的字典到底是个啥
字典简介 字典是一种用于保存键值对的数据结构,可以通过键值对中的键快速地查找到对应的值.在Redis所使用的C语言中,并没有内置字典,所以Redis自己实现了字典. 整个Redis数据库的所有的键和值 ...
- Redis数据结构详解(2)-redis中的字典dict
前提知识 字典,又被称为符号表(symbol table)或映射(map),其实简单地可以理解为键值对key-value. 比如Java的常见集合类HashMap,就是用来存储键值对的. 字典中的键( ...
- Redis核心原理与实践--散列类型与字典结构实现原理
Redis散列类型可以存储一组无序的键值对,它特别适用于存储一个对象数据. > HSET fruit name apple price 7.6 origin china 3 > HGET ...
- Redis实现之字典
字典 字典,又称为符号表(symbol table).关联数组(associative array)或映射(map),是一种用于保存键值对(key-value pair)的抽象数据结构.在字典中,一个 ...
- 《闲扯Redis十》Redis 跳跃表的结构实现
一.前言 Redis 提供了5种数据类型:String(字符串).Hash(哈希).List(列表).Set(集合).Zset(有序集合),理解每种数据类型的特点对于redis的开发和运维非常重要. ...
- Redis 中的 set 和 sorted set 如何使用,源码实现分析
set 和 sorted set 前言 set 常见命令 set 的使用场景 看下源码实现 insert dict sorted set 常见的命令 使用场景 分析下源码实现 ZADD ZRANGE ...
- 《闲扯Redis七》Redis字典结构的底层实现
一.前言 上节<闲扯Redis六>Redis五种数据类型之Hash型 中说到 Hash(哈希对象)的底层实现有: 1.ziplist 编码的哈希对象使用压缩列表作为底层实现 2.hasht ...
- Redis中5种数据结构的使用场景介绍
转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/108.html?1455861435 一.redis 数据结构使用场景 原 ...
- 深入理解Redis中的主键失效及其实现机制
参考:http://blog.sina.com.cn/s/articlelist_1221155353_0_1.html 作为一种定期清理无效数据的重要机制,主键失效存在于大多数缓存系统中,Reids ...
随机推荐
- 简述synchronized和java.util.concurrent.locks.Lock的异同 ?
主要相同点:Lock能完成synchronized所实现的所有功能 主要不同点:Lock有比synchronized更精确的线程语义和更好的性能.synchronized会自动释放锁,而Lock一定要 ...
- 【Java面试宝典】什么情况下会发生栈内存溢出?
如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常. 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常.
- osi七层模型&tcp/udp
1.TCP/UDP协议 1.1 TCP协议 可靠,速度慢,全双工通信 建立连接三次握手,断开连接四次挥手 建立起链接之后,发送每条消息都有回执,为了保证数据的完整性,还有重传机制 数据传输:有收必有发 ...
- 简易shell脚本启动jar包
可参考博客: Shell脚本中$0.$?.$!.$$.$*.$#.$@等的意义 https://blog.csdn.net/csgd2000/article/details/80396996 s ...
- 51单片机头文件reg51.h详解
转自:http://www.51hei.com/mcu/2670.html 我们在用c语言编程时往往第一行就是头文件,51单片机为reg51.h或reg52.h,51单片机相对来说比较简单,头文件里面 ...
- 网络协议之:socket协议详解之Datagram Socket
目录 简介 什么是Datagram Socket 使用socat来创建UDP服务 使用ss命令来监控Datagram Sockets 使用nc建立和UDP Socket的连接 总结 简介 上一篇文章我 ...
- 使用css完成引导用户按照流程完成任务的进度导航条
首先先看设计稿 图中的12345便是主角进度条. 分析需求如下:线的长度不固定,适应移动端和pc端点平均地分布在一条线上点的个数不固定,可能会改变激活的点之间线的颜色是绿色的 两种种方式 百分比宽度切 ...
- 访问控制protected是不同包中对子类可见,什么意思?
2.2 以下例子说明:protected是不同包中对子类可见,对非子类不可见. 例1.2.2.a:---本例为正常用法. package p1;public class A { protecte ...
- Android CheckBox的监听事件
1.在xml文件中定义CheckBox,一定要定义id <CheckBox android:id="@+id/beijing" android:layout_width=&q ...
- CSS简单样式练习(六)
运行效果: 源代码: 1 <!DOCTYPE html> 2 <html lang="zh"> 3 <head> 4 <meta char ...