Redis设计与实现(3)字典

Redis的字典使用哈希表作为底层实现,一个哈希表里面可以有多个哈希表节点,而每一个哈希表节点就保存了字典中的一个键值对

redis字典所使用的哈希表由dict.h/dictht

复制代码
typedef struct dictht{
	//哈希表数组
	dictEntry **table;
	//哈希表大小
	unsigned long size;
	//哈希表大小掩码,用于计算索引值
	//总是等于size-1
	unsigned long sizemask;
	//该哈希表已有节点的数量
	unsigned long used;
}dictht;

table 属性是一个数组, 数组中的每个元素都是一个指向 dict.h/dictEntry 结构的指针, 每个 dictEntry 结构保存着一个键值对。

size 属性记录了哈希表的大小, 也即是 table 数组的大小, 而 used 属性则记录了哈希表目前已有节点(键值对)的数量。

sizemask 属性的值总是等于 size - 1 , 这个属性和哈希值一起决定一个键应该被放到 table 数组的哪个索引上面。

哈希表节点

复制代码
typedef struct dictEntry {
	
	// 键
	void *key;
	
	// 值
	union {
		void *val;
		uint64_t u64;
		int64_t s64;
	} v;
	
	// 指向下个哈希表节点,形成链表
	struct dictEntry *next;
	
} dictEntry;

key 属性保存着键值对中的键, 而 v 属性则保存着键值对中的值, 其中键值对的值可以是一个指针, 或者是一个 uint64_t 整数, 又或者是一个 int64_t 整数。

next 属性是指向另一个哈希表节点的指针, 这个指针可以将多个哈希值相同的键值对连接在一次, 以此来解决键冲突(collision)的问题。

举个例子, 图 4-2 就展示了如何通过 next 指针, 将两个索引值相同的键 k1k0 连接在一起。

字典

复制代码
typedef struct dict {

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

    // 私有数据
    void *privdata;

    // 哈希表
    dictht ht[2];

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

} dict;

type 属性和 privdata 属性是针对不同类型的键值对, 为创建多态字典而设置的:

  • type 属性是一个指向 dictType 结构的指针, 每个 dictType 结构保存了一簇用于操作特定类型键值对的函数, Redis 会为用途不同的字典设置不同的类型特定函数。
  • privdata 属性则保存了需要传给那些类型特定函数的可选参数。
复制代码
  typedef struct dictType {

      // 计算哈希值的函数
      unsigned int (*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;

ht 属性是一个包含两个项的数组, 数组中的每个项都是一个 dictht 哈希表, 一般情况下, 字典只使用 ht[0] 哈希表, ht[1] 哈希表只会在对 ht[0] 哈希表进行 rehash 时使用。

除了 ht[1] 之外, 另一个和 rehash 有关的属性就是 rehashidx : 它记录了 rehash 目前的进度, 如果目前没有在进行 rehash , 那么它的值为 -1

解决键冲突

采用的是链地址法

举个例子, 假设程序要将键值对 k2v2 添加到下图 所示的哈希表里面, 并且计算得出 k2 的索引值为 2 , 那么键 k1k2 将产生冲突, 而解决冲突的办法就是使用 next 指针将键 k2k1 所在的节点连接起来, 如图 下下图所示。

相关推荐
睡觉的时候不会困35 分钟前
Redis 主从复制详解:原理、配置与主从切换实战
数据库·redis·bootstrap
程序员的世界你不懂2 小时前
【Flask】测试平台开发,新增说明书编写和展示功能 第二十三篇
java·前端·数据库
自学也学好编程2 小时前
【数据库】Redis详解:内存数据库与缓存之王
数据库·redis
JAVA不会写3 小时前
在Mybatis plus中如何使用自定义Sql
数据库·sql
IT 小阿姨(数据库)3 小时前
PgSQL监控死元组和自动清理状态的SQL语句执行报错ERROR: division by zero原因分析和解决方法
linux·运维·数据库·sql·postgresql·centos
ChinaRainbowSea3 小时前
7. LangChain4j + 记忆缓存详细说明
java·数据库·redis·后端·缓存·langchain·ai编程
小马学嵌入式~5 小时前
嵌入式 SQLite 数据库开发笔记
linux·c语言·数据库·笔记·sql·学习·sqlite
Java小白程序员5 小时前
MyBatis基础到高级实践:全方位指南(中)
数据库·mybatis
Monly215 小时前
人大金仓:merge sql error, dbType null, druid-1.2.20
数据库·sql
鼠鼠我捏,要死了捏5 小时前
Redis缓存穿透、缓存击穿与雪崩防护及性能优化实战指南
redis·cache·performance