钻研Redis源码,掌握Dict结构
Redis作为一种高性能的key-value存储系统,被广泛应用于数据缓存、消息队列等场景。其核心数据结构包括String、List、Set、ZSet和Hash等类型,在Redis的代码实现中,很多数据结构采用C语言实现。其中,Dict结构作为Redis中常用的哈希表,被广泛使用。本文将介绍Dict的结构、原理和实现,并且详细探讨Dict的部分源码,希望对读者了解Redis的数据结构有所帮助。
1. Dict结构
在源码中,Dict结构被定义在dict.h文件中。Dict是一个哈希表的结构体,包含以下几个成员:
typeDEF struct dictEntry {
uint64_t u64;
int64_t s64;
struct dictEntry *next;
} dictEntry;
typedef struct dictType {
uint64_t (*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;
typedef struct dictht {
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
long rehashidx;
unsigned long iterators;
其中,dictEntry结构体表示哈希表中的一个键值对,包含键、值、指向下一个dictEntry的指针等成员;dictType结构体用于定义字典操作的相关函数,比如哈希函数、键值复制函数和键值比较函数等;dictht结构体则是哈希表的关键结构体,包含哈希桶指针数组、桶大小、桶数量等成员;而dict则包含dictType、dictht等成员,用于表示一个完整的字典结构。2. 原理Dict通过哈希表实现快速查找键值对。哈希表是一种用于实现关联数组的数据结构,其基本思路是将键通过哈希函数转换成索引,存放在哈希桶中,从而实现快速的查找和插入。Dict中的哈希桶使用指针数组来存储,每个哈希桶元素对应一个dictEntry结构体。当插入或查找键值对时,先通过哈希函数计算出对应的哈希值,并将其映射到哈希桶上,找到对应的桶元素指针,然后在桶中查找或插入dictEntry。为了解决哈希冲突的问题,Dict中使用了拉链法作为哈希桶的冲突解决方法。当多个键值对的哈希值相同时,将这些键值对通过一个链表连接在一起,从而解决了哈希冲突问题。3. 实现Dict的源码实现分为几个部分,包括初始化、添加、删除、查找、哈希冲突处理等。下面我们通过具体的代码实例来详细介绍Dict的源码实现。初始化Dict的初始化主要是对哈希桶进行初始化,同时设置字典类型和哈希扩展因子等信息。下面是Dict初始化代码的实现:```c// 初始化一个新的字典dict *dictCreate(dictType *type, void *privDataptr){dict *d = zmalloc(sizeof(*d));_dictInit(d,type,privDataPtr);return d;}// 配置字典信息void _dictInit(dict *d, dictType *type, void *privdata){// 对两张哈希表分别进行初始化操作_dictReset(&d->ht[0]);_dictReset(&d->ht[1]);// 设置字典类型信息d->type = type;// 设置字典类型私有数据d->privdata = privdata;d->rehashidx = -1;d->iterators = 0;}
添加
Dict的添加操作主要是对键值对进行哈希计算,并将该键值对插入到对应的哈希桶中。如果哈希冲突,则将该键值对插入到链表的尾部,如果链表中已经存在相同的键,则更新该键所对应的值。

int dictAdd(dict *d, void *key, void *val)
dictEntry *entry = dictAddRaw(d,key,NULL);
if (!entry) return DICT_ERR;
// 更新值
dictSetVal(d, entry, val);
return DICT_OK;
dictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing)
long index;
dictEntry *entry;
dictht *ht;
if (dictIsRehashing(d))
_dictRehashStep(d);
// 找到kv对应的table index
if ((index = _dictKeyIndex(d, key, dictHashKey(d, key), existing)) == -1)
return NULL;
// 计算kv对应bucket
ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];
entry = zmalloc(sizeof(*entry));
entry->next = ht->table[index];
ht->table[index] = entry;
ht->used++;
dictSetKey(d, entry, key);
return entry;
删除Dict中的删除操作主要涉及到对哈希桶中相应键值对的删除。对于通过哈希计算得到的键值对,我们可以直接在哈希桶中删除,如果是在哈希冲突链表中的键值对,则需要先查找链表中的该键值对,在将其删除。具体代码实现如下:```CInt dictDelete(dict *d, const void *key){return dictGenericDelete(d,key,NULL);}int dictGenericDelete(dict *d, const void *key, dictEntry **existing){unsigned long h, idx;dictEntry *he, *prevHe;int table;// 确定删除的位置以及tableif (d->ht[0].used == 0 && d->ht[1].used == 0) return DICT_ERR;if (dictIsRehashing(d)) _dictRehashStep(d);h = dictHashKey(d, key);for (table = 0; table idx = h & d->ht[table].sizemask;he = d->ht[table].table[idx];prevHe = NULL;while(he) {if (key==he->key || dictCompareKeys(d, key, he->key)) {if (existing)*existing = he;if (prevHe)prevHe->next = he->next;elsed->ht[table].table[idx] = he->next;if (d->type->keyDestructor)d->type->keyDestructor(d->privdata, he->key);if (d->type->valDestructor)d->type->valDestructor(d->privdata, he->v.val);zfree(he);d->ht[table].used--;return DICT_OK;}prevHe = he;he = he->next;}if (!dictIsRehashing(d)) break;}return DICT_ERR;}
查找
Dict中的查找操作是对键值对的查询,通过哈希函数得到该键对应的哈希桶,再在哈希桶中查找该键值对。具体代码实现如下:
dictEntry *dictFind(dict *d, const void *key)
dictEntry *he;
香港服务器首选树叶云,2H2G首月10元开通。树叶云(shuyeidc.com)提供简单好用,价格厚道的香港/美国云 服务器 和独立服务器。IDC+ISP+ICP资质。ARIN和APNIC会员。成熟技术团队15年行业经验。
学习易语言最重要的部分是哪里?
学习任何一种语言都不是一个简简单单的过程,易语言虽然是一种中高级语言,但是它确极易入门(用官方话说:会汉语就会编程),但是要想深入掌握它,没有刻苦的钻研和反复上机练习,是根本不行的!想日后为祖国的“母语编程”计划进献自己的一份力量,就要具备努力学习、不怕吃苦的精神!
大家好!我是学JAVA编程的初学者!想问一下高手前辈,怎么样才能学好JAVA编程,是靠记源代码么?
考练习,考钻研,光看是不行的,要从小项目开始,慢慢道最近端的技术,像我现在就在搞J2ME,编写手机游戏,你想学可以更我
PHP开发工程师岗位工作经历怎么写
自我评价(案例一)· 拥有良好的代码习惯,结构清晰,命名规范,逻辑性强,代码冗余率低,注重用户体验开发;· 有很强的事业心和进取精神,热爱开发工作,能承受较大的工作压力;· 具备很好的学习钻研能力,思路清晰,优秀的分析问题和解决问题的能力;· 严谨细致,有责任心,诚实守信,有良好的团队合作能力,工作责任心强。 自我评价(案例二)1.熟练掌握oop的编程思想和mvc的开发模式;2.熟练HTML/CSS/JavaScript,熟练使用ajax,jquery等技术;3.熟练ThinkPHP,Ci,Yii,Laravel等开源框架;4.熟练各种业务项目开发流程及模式;5.熟悉ECShop,Iwebshop,discuz的二次开发;6.熟悉svn,git等版本控制工具的安装配置以及使用;7.熟练使用 memcache ,redis,mongoDB等缓存技术;8.熟悉对象存储(oss)的上传下载;9.熟练单点登录和第三方登录技术;10.熟练各种接口的开发使用,如支付宝支付、短信接口、网络地图等;11.熟练app接口的开发,有独立编写接口的能力;12.熟悉微信公众号的开发;13.熟悉网站静态化:页面静态化和伪静态;14.熟悉常用的数据库优化技巧:索引,缓存,分区分表,sql优化等;15.熟悉服务器架构设计:主从复制,读写分离,动静分离,负载均衡等;16. 具有较强的团队意识,高度的责任感,工作积极严谨,勇于承担压力自我评价(案例三)从小生活在农村家庭,比较能吃苦耐劳,对编程感兴趣,有新的知识或技术出现的时候,会及时学习。 之前工作主要是与客户,物流,业务员以及厂内生产工作的沟通,沟通和协调能力很强。 平时喜欢打打篮球,喜欢团队合作的娱乐项目。 自我评价(案例四)在工作中,自学能力强,能够很容易的解决技术上遇到的问题,当技术上遇到一些新的技术,通过上网或是利用手头资料,技术上的问题都能迎刃而解,对新的技术有很强的求知欲和自主学习能力。 生活上,有责任心,团队的任务一定按时完成,心胸豁达,可以和周围的人融洽的相处。
发表评论