因为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)实现原理的更多相关文章

  1. [Redis源码阅读]dict字典的实现

    dict的用途 dict是一种用于保存键值对的抽象数据结构,在redis中使用非常广泛,比如数据库.哈希结构的底层. 当执行下面这个命令: > set msg "hello" ...

  2. Redis源码阅读笔记(1)——简单动态字符串sds实现原理

    首先,sds即simple dynamic string,redis实现这个的时候使用了一个技巧,并且C99将其收录为标准,即柔性数组成员(flexible array member),参考资料见这里 ...

  3. CI框架源码阅读笔记3 全局函数Common.php

    从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap ...

  4. Three.js源码阅读笔记-5

    Core::Ray 该类用来表示空间中的“射线”,主要用来进行碰撞检测. THREE.Ray = function ( origin, direction ) { this.origin = ( or ...

  5. jdk源码阅读笔记-LinkedHashMap

    Map是Java collection framework 中重要的组成部分,特别是HashMap是在我们在日常的开发的过程中使用的最多的一个集合.但是遗憾的是,存放在HashMap中元素都是无序的, ...

  6. faster rcnn源码阅读笔记1

    自己保存的源码阅读笔记哈 faster rcnn 的主要识别过程(粗略) (开始填坑了): 一张3通道,1600*1600图像输入中,经过特征提取网络,得到100*100*512的feature ma ...

  7. Apollo源码阅读笔记(二)

    Apollo源码阅读笔记(二) 前面 分析了apollo配置设置到Spring的environment的过程,此文继续PropertySourcesProcessor.postProcessBeanF ...

  8. Apollo源码阅读笔记(一)

    Apollo源码阅读笔记(一) 先来一张官方客户端设计图,方便我们了解客户端的整体思路. 我们在使用Apollo的时候,需要标记@EnableApolloConfig来告诉程序开启apollo配置,所 ...

  9. Redis源码阅读(五)集群-故障迁移(上)

    Redis源码阅读(五)集群-故障迁移(上) 故障迁移是集群非常重要的功能:直白的说就是在集群中部分节点失效时,能将失效节点负责的键值对迁移到其他节点上,从而保证整个集群系统在部分节点失效后没有丢失数 ...

随机推荐

  1. .NET 4.6

    http://referencesource.microsoft.com/ DownLoad 下载原代码

  2. How to Make LastPass Even More Secure with Google Authenticator

    Google Authenticator LastPass supports Google Authenticator, which is officially available as an app ...

  3. oc常用正则表达式

    常用的第三方正则库: http://regexkit.sourceforge.net/RegexKitLite/index.html 匹配中文字符的正则表达式: [\u4e00-\u9fa5]评注:匹 ...

  4. 【POJ2185】【KMP + HASH】Milking Grid

    Description Every morning when they are milked, the Farmer John's cows form a rectangular grid that ...

  5. MVC埰坑日记 文件权限

    public static void DownLoadFile(string FileFullPath) { if (!string.IsNullOrEmpty(FileFullPath) & ...

  6. C# winform 递归选中TreeView子节点

    /// <summary> /// 递归选中所有的自节点 /// </summary> /// <param name="nodeThis">T ...

  7. bom type:Phantom

    bom的类型 'type': fields.selection([('normal','Normal BoM'),('phantom','Sets / Phantom')], 'BoM Type', ...

  8. vs 2015 菜单重复的问题解决方法

    打开 “运行” 输入 D:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe /resetuserdata ...

  9. NSValue NSNumber NSData类

    NSValue NSNumber NSData类 步骤1 NSValue 我们先看看NSValue能做什么: 一个NSValue对象是用来存储一个C或者Objective-C数据的简单容器.它可以保存 ...

  10. linux直接启动到字符界面或从字符界面启动到图形化界面

    修改/etc/inittab文件 将内容为:"id:5:initdefault"的行的数字5改为3,保存重启即可直接进入字符界面 PS:3和5分别表示运行级别 从字符界面启动到图形 ...