Redis源码阅读笔记(2)——字典(Map)实现原理
因为redis是用c写的,c中没有自带的map,所以redis自己实现了map,来看一下redis是怎么实现的。
1、redis字典基本数据类型
redis是用哈希表作为字典的底层实现,dictht是哈希表的定义:
typedef struct dictht {
// 哈希表节点指针数组(俗称桶,bucket)
dictEntry **table;
// 指针数组的大小
unsigned long size;
// 指针数组的长度掩码,用于计算索引值
unsigned long sizemask;
// 哈希表现有的节点数量
unsigned long used;
} dictht;
table是一个数组,数组中的元素都是一个指向dictEntry结构的指针,每个dictEntry结构保存着一个键值对。
dictEntry的结构如下:
typedef struct dictEntry {
// 键
void *key;
// 值
union {
void *val;
uint64_t u64;
int64_t s64;
} v;
// 链往后继节点
struct dictEntry *next;
} dictEntry;
可以看到有个next指针,是用链表法来解决hash冲突的;v保存值,可以是一个指针,uint64_t整数,或者int64_t整数。
Redis中字典的结构如下:
typedef struct dict {
// 特定于类型的处理函数
dictType *type;
// 类型处理函数的私有数据
void *privdata;
// 哈希表(2个)
dictht ht[];
// 记录 rehash 进度的标志,值为-1 表示 rehash 未进行
int rehashidx;
// 当前正在运作的安全迭代器数量
int iterators;
} dict;
这里需要解释一下dictType和privdata,前者是一组用于操作键值对的函数,redis会对不同用途的字典使用不同的函数,后者是这些函数需要用的可选参数。
ht[2]就是两个哈希表,一般情况下只会ht[0],ht[1]会在对ht[0]进行rehash时使用。rehashidx记录了rehash目前的进度,如果目前没有进行rehash那么rehashidx=-1。
2、哈希算法以及解决哈希冲突
redis使用MurmurHash2算法,哈希冲突使用链地址法,redis总是将新节点添加到链表头部。
3、rehash和渐进式rehash
redis的哈希表会随着对其操作而增大或减小,那么为了让负载因子保持合理,也保持字典的高效,需要在哈希表中数量太多或太少时进行扩展或收缩。
redis最小哈希表大小为DICT_HT_INITIAL_SIZE=4,扩展操作的话ht[1]的大小为第一个大于等于ht[0].used*2的2^n;收缩操作ht[1]的大小为第一个大于等于ht[0].used的2^n。然后将ht[0]中的所有键值对rehash到ht[1]上,当全部rehash之后,把ht[1]置为ht[0],并为ht[1]新创建一个空白哈希表,为下次rehash做准备。
渐进式rehash,让字典同时持有ht[0]和ht[1],将rehashidx设为0,表示rehash开始;在rehash期间,每次对字典进行增删查改操作时,redis除了执行指定的操作以外,还会顺带把ht[0]哈希表在rehashidx索引上的所有键值对rehash到ht[1],当rehash完成,rehashidx++;随着时间及操作的执行,最终ht[0]的所有键值对会rehash到ht[1]上,然后把rehashidx置为-1;表示rehash结束。
在渐进式rehash的过程中,字典的删除,查找,更新等操作会在两个哈希表上进行,新增的键值对会保存到ht[1]里面,这样保证了ht[0]里的键值对只增不减,最终变为空表。
Redis源码阅读笔记(2)——字典(Map)实现原理的更多相关文章
- [Redis源码阅读]dict字典的实现
dict的用途 dict是一种用于保存键值对的抽象数据结构,在redis中使用非常广泛,比如数据库.哈希结构的底层. 当执行下面这个命令: > set msg "hello" ...
- Redis源码阅读笔记(1)——简单动态字符串sds实现原理
首先,sds即simple dynamic string,redis实现这个的时候使用了一个技巧,并且C99将其收录为标准,即柔性数组成员(flexible array member),参考资料见这里 ...
- CI框架源码阅读笔记3 全局函数Common.php
从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap ...
- Three.js源码阅读笔记-5
Core::Ray 该类用来表示空间中的“射线”,主要用来进行碰撞检测. THREE.Ray = function ( origin, direction ) { this.origin = ( or ...
- jdk源码阅读笔记-LinkedHashMap
Map是Java collection framework 中重要的组成部分,特别是HashMap是在我们在日常的开发的过程中使用的最多的一个集合.但是遗憾的是,存放在HashMap中元素都是无序的, ...
- faster rcnn源码阅读笔记1
自己保存的源码阅读笔记哈 faster rcnn 的主要识别过程(粗略) (开始填坑了): 一张3通道,1600*1600图像输入中,经过特征提取网络,得到100*100*512的feature ma ...
- Apollo源码阅读笔记(二)
Apollo源码阅读笔记(二) 前面 分析了apollo配置设置到Spring的environment的过程,此文继续PropertySourcesProcessor.postProcessBeanF ...
- Apollo源码阅读笔记(一)
Apollo源码阅读笔记(一) 先来一张官方客户端设计图,方便我们了解客户端的整体思路. 我们在使用Apollo的时候,需要标记@EnableApolloConfig来告诉程序开启apollo配置,所 ...
- Redis源码阅读(五)集群-故障迁移(上)
Redis源码阅读(五)集群-故障迁移(上) 故障迁移是集群非常重要的功能:直白的说就是在集群中部分节点失效时,能将失效节点负责的键值对迁移到其他节点上,从而保证整个集群系统在部分节点失效后没有丢失数 ...
随机推荐
- 编程基础-msdn编程指南笔记
此博仅为笔记,摘自msdn编程指南文档,链接地址:http://msdn.microsoft.com/zh-cn/library/67ef8sbd.aspx 注释:// 单行注释 /* 多行注释*/ ...
- 比较好的自学IT的网站
其实这是我在知乎的一个回答,由于收藏人数众多,我想也许对有些初学者有用,故同步到Blog.此文章和知乎答案将不定期同步更新(知乎答案传送门). 入门与进阶: 学堂在线-最大的中文慕课(MOOC)平台学 ...
- CoreBluetooth
Core Bluetooth的基本常识 每个蓝牙4.0设备都是通过服务(Service)和特征(Characteristic)来展示自己的 一个设备必然包含一个或多个服务,每个服务下面又包含若干个特征 ...
- Java线程(学习整理)--3--简单的死锁例子
1.线程死锁的概念: 简单地理解下吧! 我们都知道,线程在执行的过程中是占着CPU的资源的,当多个线程都需要一个被锁住的条件才能结束的时候,死锁就产生了! 还有一个经典的死锁现象: 经典的“哲学家就餐 ...
- c++面试(一)
1.在c++中可以通过"::"来直接操作全局变量. 2.i++与++i效率的比较. (1)內建数据类型时,他们的效率差别不大. (2)自定义数据类型(类等)的情况,(++i)可以返 ...
- c++primerplus(第六版)编程题——第5章(循环和关系表达式)
声明:作者为了调试方便,每一章的程序写在一个工程文件中,每一道编程练习题新建一个独立文件,在主函数中调用,我建议同我一样的初学者可以采用这种方式,调试起来会比较方便. (具体方式参见第3章模板) 1. ...
- 移动平台3G手机网站前端开发布局技巧汇总
移动平台3G手机网站前端开发布局技巧汇总 作者:前端开发-武方博 发布:2011-05-10 09:11 分类:移动开发 阅读:120,618 views 7条评论 您或许正在 ...
- jQuery选择器种类整理
选择器概念 jQuery选择器是通过标签.属性或者内容对HTML内容进行选择,选择器运行对HTML元素组或者单个元素进行操作. jQuery选择器使用$符号,等同于jquery,例如: $(“li”) ...
- visual studio中验证控件的使用
1.RequiredFieldValidator:验证一个必填字段,如果这个字段没填,那么,将不能提交信息. RequiredFieldValidator控件中,主要设置三个属性: (1)ErrorM ...
- python模块之hashlib加密
40.加密模块:hashlib 1. >>> import hashlib >>> ret1 = hashlib.md5() ...