【Redis】数据结构 - 字典

概述

字典又称映射(map), 是一种用于保存键值对的数据结构。

字典的键(key)唯一一个键对应一个值(value),查找,删除,更新都需要通过键来操作。

redis的数据库就是使用字典作为底层

新增键值对"msg"->"hello world", 执行redis命令:
redis> SET msg "hello world"

字典也是哈希键的底层实现之一

实现

字典

字典结构 dict.h/dict

typedef struct dict{
	// 类型特定函数
	dictType *type;
	// 私有数据
	void *privdata;
	// 哈希表
	dictht ht[2];
	// rehash索引 当rehash不在进行时值为-1
	int trehashidx;
}dict;
  • type 指向dictType结构体的指针, 该结构体保存了对特性类型键值对操作的函数
  • privadata 保存type中函数的特定参数.
  • ht 包含两个dictht哈希表, 一般只是使用ht[0], ht[1]在rehash时才会使用
  • rehashidx 记录了rehash目前的进度.

哈希表

哈希表dictht结构

typedef struct dictht{
	// 哈希表数组
	dictEntry **table;
    // 哈希表大小
    unsigned long size;
    //值为 size - 1 掩码 计算索引
    unsigned long sizemask;
    //
    unsigned long used;
}dictht;
  • table 中的元素都是dict.h/dictEntry结构, 是一个保存着键值对的哈希表节点.
  • size 为哈希表的大小, 即table数组大小.
  • sizemask 值等于size - 1, 用于计算索引.
  • used 记录哈希表已有节点

哈希表节点dictEntry结构

dictht中table数组保存的键值对元素.

typedef struct dictEntry{
	// 键
	void *key;
	// 值
	union{
		void *val;
		uint64_t u64;
		int64_t s64;
	}v;
	
	// 指向下个哈希表的节点, 形成链表.
	struct dictEntry *next;
}dictEntry;
  • key 保存着键值对中的键
  • v 是一个共用体, 表示键值对中的值, 它可以是void * 指针, uint64_t无符号整数和 int64_t整数其中的一个.
  • next 指向下一个节点的指针, 链地址法解决哈希冲突.

普通状态下的字典结构

在这里插入图片描述

哈希算法

  1. 使用字段设置的特定函数中的哈希函数计算哈希值
hash = dict->type->hashFunction(key);
  1. 通过sizemask属性和哈希值, 计算出索引值
index = hash & dict->ht[x].sizemask;

解决键冲突

  • Redis 的哈希表采用链地址法解决哈希冲突
  • 多个相同hash值的哈希表节点使用next指针构成一个单向链表.

rehash

  • 让哈希表的负载因子维持在一个合理的范围, 应对哈希表底层的数组进行相应的扩增或缩小

rehash步骤:

  • 为字典的ht[1]哈希表分配空间, 扩展则其大小为于 第一个大于等于ht[0].used * 2 的 2^n。 收缩则其大小为第一个小于等于 ht[0].used 的2^n。
  • 将ht[0]中的所有键值对rehash到ht[1] 中(经过重新计算)。
  • 释放ht[0], 将ht[1] 设为 ht[0]。

rehash条件:

  • 服务器目前没有执行BGSAVE命令和BGREWRITEAOF命令, 且哈希表负载因子大于1
  • 服务器正在执行BGSAVE命令和BGREWRITEAOF命令, 且哈希表负载因子大于5
  • 哈希表负载因子小于0.1, 则会进行哈希表的收缩
  • 负载因子 = 哈希表已保存节点数量 / 哈希表大小

BGSAVE和BGREWRITEAOF命令会创建服务进程的子进程,扩展操作会影响写时复制,造成内存浪费。

写时复制:对于父进程和子进程,其共用一份内存空间,只有进程空间的各段的内容要发生变化时,才会将父进程的内存复制一份给子进程。

渐进式rehash

rehash存在的问题:

  • 存有大量数据时扩展较慢,影响正常地增删改查操作

解决:

  • 使用渐进式rehash,将rehash的工作均摊每一次的增删改查上。

渐进式rehash步骤:

  1. 为ht[1] 分配空间, 让字典同时持有ht[0] 和 ht[1]两个哈希表
  2. 维持一个索引计数器变量rehashidx = 0
  3. 每次对字典的增删改查操作, 都会顺带将 ht[0] 中 rehashdx 位置的所有键值对rehash到 ht[1] 上. rehashidx向后递增。
  4. 随着对字典操作的进行,所有的ht[0] 中的键值对都会被rehash到 ht[1],最后设置rehashidx = -1, 渐进式 rehash 完成。

渐进式rehash时对字典进行操作:

  • 所有的增删改查都会根据情况同时再ht[0] 和 ht[1] 上同时操作。
  • 查找操作,会先在ht[0]中查找, 不存在则再在ht[1]中查找
  • 新增操作, 都会在ht[1]上进行。

API

函数作用时间复杂度
dictCreate创建一个新字典O(1)
dictAdd添加键值对O(1)
dictReplace替换键值对, 不存在则添加O(1)
dictFetchValue返回给定键的值O(1)
dictGetRandomKey随机返回一个键值对O(1)
dictDelete删除键值对O(1)
dictRelease释放字典, 以及所有键值对O(N)

参考文献

《Redis设计与实现》黄健宏

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值