Redis学习笔记之字典

Redis 学习笔记之字典

一、字典的实现

1.1 字典内部定义

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

    // 哈希表大小
    unsigned long size;

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

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

} dictht;
  • 哈希表节点定义
typedef struct dictEntry {
    // 键
    void *key;
    // 值
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
    } v;
    // 指向下个哈希表节点,形成链表
    struct dictEntry *next;
} dictEntry;
  • 图示

Redis学习笔记之字典
  • 字典结构定义
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 属性则保存了需要传给那些类型特定函数的可选参数。

  • dictType

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;
  • 图示
    Redis学习笔记之字典

1.2 哈希算法

Redis 使用 MurmurHash2 算法来计算键的哈希值
MurmurHash 算法请查看 https://github.com/aappleby/smhasher

1.3 解决键冲突问题

这里与 Java HashMap 类似,也是采用链地址法解决键冲突问题

1.4 rehash (重新散列)

  • 重新散列步骤:

  • 为字典的 ht[1] 哈希表分配空间

  • 如果执行的是扩展操作, 那么 ht[1] 的大小为第一个大于等于 ht[0].used * 2 的 2^n (2 的 n 次方幂);
  • 如果执行的是收缩操作, 那么 ht[1] 的大小为第一个大于等于 ht[0].used 的 2^n 。
  • 将保存在 ht[0] 中的所有键值对 rehash 到 ht[1] 上面: rehash 指的是重新计算键的哈希值和索引值, 然后将键值对放置到 ht[1] 哈希表的指定位置上。
  • 当 ht[0] 包含的所有键值对都迁移到了 ht[1] 之后 (ht[0] 变为空表), 释放 ht[0] , 将 ht[1] 设置为 ht[0] ,并在 ht[1] 新创建一个空白哈希表, 为下一次 rehash 做准备。

  • 图示

Redis学习笔记之字典

Redis学习笔记之字典

Redis学习笔记之字典

Redis学习笔记之字典
  • 哈希表的扩展与收缩
  • 扩展
    • 服务器目前没有在执行 BGSAVE 命令或者 BGREWRITEAOF 命令, 并且哈希表的负载因子大于等于 1
    • 服务器目前正在执行 BGSAVE 命令或者 BGREWRITEAOF 命令, 并且哈希表的负载因子大于等于 5
  • 收缩
    • 当哈希表的负载因子小于 0.1 时, 程序自动开始对哈希表执行收缩操作。
负载因子 = 哈希表已保存节点数量 / 哈希表大小
load_factor = ht[0].used / ht[0].size

1.5 渐进式 rehash 了解

  • 什么是渐进式 rehash?

Redis 字典 rehash 的过程需要把哈希表 ht[0]的元素重新哈希放到 ht[1]中,当数据量庞大的时候,为了性能上的考虑,不能一次性 rehash,只能分批 rehash。 所以 Redis 在对字典的每个添加、删除、查找和更新操作上,都会进行分批 rehash。

  • 渐进式 rehash 的步骤
  • 为 ht[1] 分配空间, 让字典同时持有 ht[0] 和 ht[1] 两个哈希表。
  • 在字典中维持一个索引计数器变量 rehashidx , 并将它的值设置为 0 , 表示 rehash 工作正式开始。
  • 在 rehash 进行期间, 每次对字典执行添加、删除、查找或者更新操作时, 程序除了执行指定的操作以外, 还会顺带将 ht[0] 哈希表在 rehashidx 索引上的所有键值对 rehash 到 ht[1] , 当 rehash 工作完成之后, 程序将 rehashidx 属性的值增一。
  • 随着字典操作的不断执行, 最终在某个时间点上, ht[0] 的所有键值对都会被 rehash 至 ht[1] , 这时程序将 rehashidx 属性的值设为 -1 , 表示 rehash 操作已完成。

  • 渐进式 rehash 的相关问题?

  • rehash 进行时又有增删改查如何处理?
    • 增加时,直接往新的 1 号哈希表增加。
    • 删除、修改、查询时,由于无法确定 entry 在哪块哈希表上,所以只能先查 0 号哈希表,找不到再查 1 号哈希表。
  • 什么时候不允许 rehash?
    • 如果在 rehash 进行中,上层获取并长久持有了 dict 的迭代器,那么 rehash 需要暂停,以避免迭代器迭代时访问到重复 entry 或丢失 entry。
    • 另外 redis 如果正在将数据持久化,也会关闭 rehash 的开关,避免 copy-on-write 受影响。

参考

Original: https://www.cnblogs.com/JianJianHuang/p/13776762.html
Author: JiaJianHuang
Title: Redis学习笔记之字典

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/573092/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 针对FILES和PATH的操作

    在修改漏洞的时候发现,根据建议都使用NIO包的FILES和PATH来进行文件操作,来保证安全性. 使用Files前,需要先使用Path来获取文件路径,而且可以用Path转化为其他多…

    Java 2023年6月15日
    074
  • CTF中常见密码学

    前言 参考,我们任课老师的WORD和PPT,结合自己的理解,在结合网上文章的理解。 一.BASE64编码 BASE64编码中,特征和所拥有的字符 字&…

    Java 2023年6月13日
    068
  • Ubuntu 制作 系统的启动盘

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月8日
    081
  • Java程序员的macOS手册

    针对Java程序员而言,开发服务器端程序,程序的运行环境是Linux,在Mac OS X这个Unix上面开发,其实很方便。 本技术专栏帮忙Java程序员,搭建工作环境,如JDK、M…

    Java 2023年5月29日
    066
  • 干货分享:小技巧大用处之Bean管理类工厂多种实现方式

    前言:最近几个月很忙,都没有时间写文章了,今天周末刚好忙完下班相对早点(20:00下班)就在家把之前想总结的知识点写出来,于是就有了这篇文章。虽无很高深的技术,但小技巧有大用处。 …

    Java 2023年6月9日
    0175
  • Paxos 协议简单介绍

    一、简介 Paxos 协议是少数在工程实践中证实的强一致性、高可用的去中心化分布式协议。Google 的很多大型分布式系统都采用了 Paxos 算法来解决分布式一致性问题,如 Ch…

    Java 2023年6月7日
    095
  • Unity应用架构设计(10)——绕不开的协程和多线程(Part 1)

    在进入本章主题之前,我们必须要了解客户端应用程序都是 单线程模型,即只有一个主线程(Main Thread),或者叫做UI线程,即所有的UI控件的创建和操作都是在主线程上完成的。而…

    Java 2023年5月30日
    083
  • springboot引入三方jar包打包报错问题解决

    (1)jar包放项目中(如下图) (2)修改依赖配置 加入: <dependency> <groupId>cn.newhopegroupId> &lt…

    Java 2023年5月30日
    0116
  • 多线程与高并发(四)—— 根据 HotSpot 源码讲透 Java 中断机制

    前言 我们首先介绍中断的三个 APPI 及其底层代码,在对方法的实现有了清晰的认知后,再结合场景谈谈什么是中断,以及中断该如何正确使用? 一、中断方法 1. isInterrupt…

    Java 2023年6月9日
    052
  • Linux 终端运行命令时出现多行带有加号的信息(详见文章内容)

    ++_vte_ prompt_ command +++ HISTTIMEFORMAT= +++ history 1 +++ sed ‘s/^ *[0-9] \+ *//’ ++ l…

    Java 2023年6月8日
    078
  • 《隐入尘烟》-我眼中的年度最佳

    《隐入尘烟》无疑是一部不可多得的佳作。然而,正是这样的佳作,票房却比不上那些烂俗的电影,那些烂俗甚至空洞的电影票房动辄上亿甚至几十亿,却是最有市场的最叫座的。 故事发生在西北的一个…

    Java 2023年6月13日
    085
  • Spring Security默认的用户登录表单 页面源代码

    Spring Security默认的用户登录表单 页面源代码 <html><head><title>Login Pagetitle>hea…

    Java 2023年5月30日
    066
  • 搭建maven私服

    1.1 简介: Nexus 是Maven仓库管理器,如果你使用Maven,你可以从Maven中央仓库 下载所需要的构件(artifact),但这通常不是一个好的做法,你应该在本地架…

    Java 2023年6月5日
    089
  • 高并发场景案例分享(二)count实时查询之坑

    上一篇主要从设计层面,分享了一些小经验。 因软件系统有其复杂性和多样性,不同的场景、架构下,系统的瓶颈各不相同。 文章里的一些想法和设计并不通用,主要针对的是 高并发场景下海量数据…

    Java 2023年6月5日
    0100
  • SpringBoot扩展接口- BeanFactoryPostProcessor后置处理器

    BeanFactoryPostProcessor接口,对BeanFactory进行后置方法调用 BeanFactoryPostProcessor分为两组:BeanDefinitio…

    Java 2023年5月30日
    079
  • 【leetcode】151. 颠倒字符串中的单词

    给你一个字符串 s ,颠倒字符串中 单词 的顺序。 单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。 返回 单词 顺序颠倒且 单词 之间用单个空…

    Java 2023年6月6日
    084
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球