Redis设计与实现(五)—— 哈希表

一、哈希表概述

在Redis中,哈希表用的很多,首先是我们的数据库,数据库表采用的就是两张哈希表,用于扩容转化,然后我们的数据类型,像Hash和Set两种类型都有Hash的编码类型,然后接下来说说Hash表

二、哈希表结构

哈希表

typedef struct dictht {
    // 哈希表数组
    dictEntry **table;

    // 哈希表大小
    unsigned long size;

    // 哈希表大小掩码,用于计算索引值
    // 总是等于 size - 1
    unsigned long sizemask;

    // 该哈希表已有节点的数量
    unsigned long used;

} dictht;

在这里插入图片描述

上面是我们的哈希表的总结构,我们可以看到他记录了一个size大小,指向哈希表的指针,已使用的数量。
然后来看看他的节点的类型

dictEntry

typedef struct dictEntry {

    // 键
    void *key;

    // 值
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
    } v;

    // 指向下个节点,形成链表
    struct dictEntry *next;

} dictEntry;

在这里插入图片描述

可以看到我们的节点中会包含下一个结点的指针,K的指针,以及Value的指针

来看看我们Redis中的字典

typedef struct dict {

    // 类型特定函数
    dictType *type;

    // 私有数据
    void *privdata;

    // 哈希表两张
    dictht ht[2];

    // rehash 索引
    // 当 rehash 不在进行时,值为 -1
    int rehashidx; /* rehashing not in progress if rehashidx == -1 */

} dict;

在这里插入图片描述

三、渐进式rehash

因为我们这边有size的字段来记录我们的哈希表的大小,当超过一定的阈值,就会进行扩容,但是如果阻塞所有操作,然后进行扩容,因为Redis是单线程的,此时不能对外响应,肯定会有很多问题,而且,我们的数据库也使用的哈希结构,如果进行扩容,当有很多数据的时候,肯定一时半会搞不定,所以Redis提出了渐进式Rehash来进行扩容,使用了两张表,然后不是一次性全部弄完的
下面介绍rehash的步骤
1、为h[1]分配空间,让我们的字典同时拥有ht[0]和ht[1]两张表
2、在字典中维护一个索引计数器变量rehashidx,初始值为0,表示rehash还没哟开始
3、在rehash运行期间,每次对字典执行添加,删除,查找,更新等操作的时候,程序除了响应这些操作外,还会顺带将ht[0]哈希表上rehashidx处索引的键值对放到ht[1],然后将这个rehashidx++
4、最终ht[0]所有键值对都会被放到ht[1],然后设置rehashidx为-1,表示操作已经完成

这边利用了一个懒的思想,不会一次性先把所有的数据转移,而是利用用户线程去完成一部分的操作,ConcurrentHashMap中也会依赖用户线程去进行我们的帮助扩容

### Redis设计实现概述 Redis 是一种高性能的键值数据库,支持多种数据结构并提供了丰富的功能。以下是关于 Redis设计实现细节以及其主要数据结构的相关说明。 #### 1. Redis 数据结构详解 Redis 中所有的 key 都是以字符串形式存在,而 value 则可以采用种不同的数据结构来表示[^1]。这些数据结构分别是: - **String(字符串)**: 基本的数据类型,能够存储字符串或者整数/浮点数值。 - **List(列表)**: 实现为双向链表,适合用于队列和栈的操作。 - **Set(集合)**: 存储无序且唯一的字符串元素集合。 - **Hash(哈希表)**: 类似于字典的形式,适用于对象字段映射。 - **ZSet(有序集合)**: 和 Set 类似,但是每个成员关联一个分数(score),按照分数排序。 每种数据结构都有特定的应用场景和支持的操作集,这使得 Redis 能够灵活应对各种需求[^2]。 #### 2. 底层数据结构分析 Redis 的底层采用了多种高效的数据结构来支撑上述高级抽象。例如: - 字符串(Strings) 使用简单的动态字符串 SDS (Simple Dynamic String)[^3]; - 列表(Lists) 可能基于压缩列表(ziplist) 或者双端链表(linkedlist) 构建; - 集合(Sets) 和散列(Hashes) 在小规模时可能利用 ziplists, 当增长到一定大小则切换至 hashtable; - ZSets 结构内部由两个部分组成——一个是普通的 set 来保存 member;另一个是 skip list 维护 score-ordering 关系以便快速查找范围内的 members。 这种根据不同情况自动调整使用的策略极大地优化了内存利用率及访问效率。 #### 3. 持久化机制探讨 为了保障数据安全性和可靠性,Redis 提供两种主要的持久化方式:RDB(Redis Database File)快照文件和 AOF(Append Only File)日志记录。通过合理配置这两种方法参数组合可满足不同程度上的恢复要求。 另外值得注意的是,在实际工程实践中还需要考虑诸如过期策略、淘汰算法等因素对于整体性能的影响。 ```python import redis # 创建连接池实例 pool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True) r = redis.Redis(connection_pool=pool) r.set('foo', 'bar') # 设置键 foo 对应值 bar print(r.get('foo')) # 获取键 foo 所对应的值 ``` 以上代码片段展示了如何简单地操作 Redis 客户端库完成基本增删改查动作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值