Redis(129)Redis的字典(Dictionary)是如何实现的?

Redis的字典(Dictionary)实现是基于哈希表的数据结构,并采用了渐进式rehash(重新哈希)机制来保证性能。Redis字典主要用于实现数据库键空间、哈希类型等。下面详细介绍Redis字典的实现,包括关键结构体定义、主要操作函数及其实现细节。

主要结构体

  1. dictEntry:字典中的每个条目(键值对)。
  2. dictht:哈希表,包含哈希表数组和大小信息。
  3. dict:字典结构,包含两个哈希表和rehash状态。

结构体定义

c 复制代码
#include <stdlib.h>
#include <string.h>

// 字典条目
typedef struct dictEntry {
    void *key;
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next;
} dictEntry;

// 哈希表
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; // rehash 索引,-1 表示没有进行 rehash
    unsigned long iterators; // 当前激活的迭代器数量
} dict;

字典的主要操作

1. 初始化

字典的初始化包括创建哈希表并设置默认值。

c 复制代码
int dictInit(dict *d, dictType *type, void *privDataPtr) {
    _dictReset(&d->ht[0]);
    _dictReset(&d->ht[1]);
    d->type = type;
    d->privdata = privDataPtr;
    d->rehashidx = -1;
    d->iterators = 0;
    return DICT_OK;
}

static void _dictReset(dictht *ht) {
    ht->table = NULL;
    ht->size = 0;
    ht->sizemask = 0;
    ht->used = 0;
}

2. 插入

插入操作首先会检查是否需要进行rehash,然后根据键计算哈希值和索引,将键值对插入到哈希表中。

c 复制代码
int dictAdd(dict *d, void *key, void *val) {
    dictEntry *entry = dictAddRaw(d, key);
    if (!entry) return DICT_ERR;
    dictSetVal(d, entry, val);
    return DICT_OK;
}

dictEntry *dictAddRaw(dict *d, void *key) {
    int index;
    dictht *ht;

    if (dictIsRehashing(d)) _dictRehashStep(d);

    // 计算索引
    if ((index = _dictKeyIndex(d, key)) == -1) return NULL;

    // 选择哈希表(ht[0] 为当前使用表,rehashing 时 ht[1] 为新表)
    ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];

    // 创建新条目
    dictEntry *entry = zmalloc(sizeof(*entry));
    entry->next = ht->table[index];
    ht->table[index] = entry;
    ht->used++;

    // 设置键
    dictSetKey(d, entry, key);
    return entry;
}

int _dictKeyIndex(dict *d, const void *key) {
    unsigned int h, idx, table;
    dictEntry *he;

    // 计算哈希值
    h = dictHashKey(d, key);

    // 检查是否需要 rehash
    if (dictIsRehashing(d)) _dictRehashStep(d);

    for (table = 0; table <= 1; table++) {
        idx = h & d->ht[table].sizemask;
        he = d->ht[table].table[idx];
        while (he) {
            if (dictCompareKeys(d, key, he->key)) return -1;
            he = he->next;
        }
        if (!dictIsRehashing(d)) break;
    }
    return idx;
}

3. 查找

查找操作根据键计算哈希值和索引,从哈希表中找到对应的条目。

c 复制代码
dictEntry *dictFind(dict *d, const void *key) {
    if (d->ht[0].used == 0 && d->ht[1].used == 0) return NULL;

    if (dictIsRehashing(d)) _dictRehashStep(d);

    unsigned int h = dictHashKey(d, key);
    for (unsigned int table = 0; table <= 1; table++) {
        unsigned int idx = h & d->ht[table].sizemask;
        dictEntry *he = d->ht[table].table[idx];
        while (he) {
            if (dictCompareKeys(d, key, he->key)) return he;
            he = he->next;
        }
        if (!dictIsRehashing(d)) return NULL;
    }
    return NULL;
}

4. 删除

删除操作将对应的条目从哈希表中移除。

c 复制代码
int dictDelete(dict *d, const void *key) {
    if (d->ht[0].used == 0 && d->ht[1].used == 0) return DICT_ERR;

    if (dictIsRehashing(d)) _dictRehashStep(d);

    unsigned int h = dictHashKey(d, key);
    for (unsigned int table = 0; table <= 1; table++) {
        unsigned int idx = h & d->ht[table].sizemask;
        dictEntry *he = d->ht[table].table[idx];
        dictEntry *prevHe = NULL;
        while (he) {
            if (dictCompareKeys(d, key, he->key)) {
                if (prevHe) {
                    prevHe->next = he->next;
                } else {
                    d->ht[table].table[idx] = he->next;
                }
                dictFreeKey(d, he);
                dictFreeVal(d, he);
                zfree(he);
                d->ht[table].used--;
                return DICT_OK;
            }
            prevHe = he;
            he = he->next;
        }
        if (!dictIsRehashing(d)) return DICT_ERR;
    }
    return DICT_ERR;
}

5. Rehash

rehash 过程通过将条目从旧哈希表迁移到新哈希表,来逐渐完成哈希表的扩展或收缩。

c 复制代码
void _dictRehashStep(dict *d) {
    if (d->iterators != 0) return;

    dictEntry *entry, *nextEntry;
    if (d->rehashidx == -1) return;

    while (d->ht[0].table[d->rehashidx] == NULL) {
        d->rehashidx++;
        if (d->rehashidx >= (signed)d->ht[0].size) {
            _dictRehash(d, 1);
        }
    }

    entry = d->ht[0].table[d->rehashidx];
    while (entry) {
        unsigned int h = dictHashKey(d, entry->key) & d->ht[1].sizemask;
        nextEntry = entry->next;
        entry->next = d->ht[1].table[h];
        d->ht[1].table[h] = entry;
        d->ht[0].used--;
        d->ht[1].used++;
        entry = nextEntry;
    }
    d->ht[0].table[d->rehashidx] = NULL;
    d->rehashidx++;
    if (d->ht[0].used == 0) {
        zfree(d->ht[0].table);
        d->ht[0] = d->ht[1];
        _dictReset(&d->ht[1]);
        d->rehashidx = -1;
    }
}

示例使用

下面是一个简单的Java示例,展示如何使用类似Redis的字典数据结构实现基本的插入和查找功能。为了简化实现,不包含rehash逻辑。

java 复制代码
import java.util.Arrays;

class DictEntry {
    Object key;
    Object value;
    DictEntry next;

    DictEntry(Object key, Object value) {
        this.key = key;
        this.value = value;
    }
}

class Dict {
    private static final int INIT_SIZE = 4;
    private DictEntry[] table;
    private int size;
    private int sizemask;
    private int used;

    public Dict() {
        this.table = new DictEntry[INIT_SIZE];
        this.size = INIT_SIZE;
        this.sizemask = INIT_SIZE - 1;
        this.used = 0;
    }

    private int hashFunction(Object key) {
        return key.hashCode() & sizemask;
    }

  
相关推荐
24白菜头1 天前
若依框架Ruoyi-Vue-SpringBoot3部署
前端·javascript·笔记·后端·学习
MX_93591 天前
基于注解方式配置声明式事务
java·开发语言·后端·spring
野犬寒鸦1 天前
JVM垃圾回收机制深度解析(G1篇)(垃圾回收过程及专业名词详解)
java·服务器·jvm·后端·面试
我爱吃土豆11 天前
HTTP首部讲解
后端·http·web
woniu_buhui_fei1 天前
Spring Boot知识总结
spring boot·后端
彭于晏Yan1 天前
Spring AI(一):玩转AI大模型
spring boot·后端·ai
计算机学姐1 天前
基于SpringBoot的蛋糕烘焙销售服务系统
java·spring boot·后端·spring·tomcat·intellij-idea·mybatis
代码探秘者1 天前
【算法篇】2.滑动窗口
java·数据结构·后端·python·算法·spring
百锦再1 天前
Spring Boot + JWT + RBAC 权限系统实战,从登录鉴权到接口级权限控制完整落地
java·数据库·spring boot·后端·sql·mysql·oracle
小江的记录本1 天前
【Filter / Interceptor】过滤器(Filter)与拦截器(Interceptor)全方位对比解析(附底层原理 + 核心对比表)
java·前端·后端·spring·java-ee·前端框架·web