文章参考 《Redis 设计与实现》黄建宏

字典

在字典中,每个键都是独一无二的,程序可以在字典中根据键查找与之相关联的值,或者通过键来更新和删除值。

字典在 Redis 中的应用相当广泛,比如 Redis 的数据库就是使用字典来作为底层实现的,例如:

redis> SET msg "hello world"
OK

在数据库中创建一个键为 “msg” 值为 “hello world” 的键值对, 这个键值对就保存在数据库的字典里面。

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

哈希表

Redis 字典所使用的是哈希表

typedef struct dictht {
// 哈希表数组
dictEntry **table;
// 哈希表大小
unsigned long size;
// 哈希表大小掩码,用于计算索引值
// 总是等于 size-1
unsigned long sizemask;
// 该哈希表已有节点的数量
unsigned long used;
} dictht;
  • table 属性是一个数组, 数组中的每个元素都是指向 dictEntry 结构的指针, 每个 dictEntry 结构保存着一个键值对
  • size 属性记录了 table 数组的大小
  • used 属性记录了哈希表目前已有节点的数量
  • sizemask 属性的值总是等于 size - 1, 这个属性和哈希值一起决定一个键该被放到 table 数组的哪个索引上面

哈希表节点 dictEntry

typedef struct dictEntry {
// 键
void *key; // 值
union {
void *val;
uint64_tu64;
int64_ts64;
} v; // 指向下个 dictEntry, 形成链表
struct dictEntry *next;
} dictEntry;
  • key 属性保存键值对的键, v 属性保存键值对的值,其中键值对的值可以是一个指针,或者是一个 uint64_t 整数 或 int64_t 整数
  • next 指向另一个哈希表节点的指针,这个指针将多个哈希值相同的键值对连接在一起, 以此来解决哈希冲突(拉链法)

字典

typedef struct dict {
// 类型特定函数
dictType *type;
// 私有数据
void *privdata;
// 哈希表
dictht ht[2];
// rehash 索引
// 当 rehash 不在进行时,值为-1
in trehashidx;
} dict;

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

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

rehashidx 属性记录了 rehash 目前的进度,如果目前没有在进行的 rehash, 那么它的值为 -1。

rehash

随着操作的不断执行,哈希表保存的键值对会逐渐增多或减少,为了让哈希表的负载因子 load factor 维持在一个合理的范围内,需要对哈希表进行合理的扩展或者收缩,即通过 rehash (重新散列) 操作来完成

  1. 为字典 ht[1] 哈希表分配空间,这个哈希表的空间大小取决于要执行的操作,以及 ht[0] 当前包含的键值对数量( ht[0].used 的值 )

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

什么情况下会触发 rehash

  1. 服务器目前没有在执行 BGSAVE 或者 BGREWRITEAOF 命令,并且哈希表的负载因子 >= 1

  2. 服务器目前正在执行 BGSAVE 或 BGREWRITEAOF 命令,并且哈希表负载因子 >= 5

    负载因子 = used / size

  3. 当负载因子 < 0.1 时,程序自动开始对哈希表执行收缩操作

渐进式 rehash:

rehash动作并不是一次性完成的,防止庞大的计算可能导致服务器在一定时间内停止服务。所以在渐进式 rehash 的过程中,字典会同时使用 ht[0] 和 ht[1] 两个哈比表,其中查找会先查找 ht[0], 再查找 ht[1], 添加操作则只对 ht[1] 添加,这一措施保证了 ht[0] 包含的键值对数量会只减不增,并随着 rehash 结束变成空表。

Redis 底层数据结构之字典的更多相关文章

  1. Redis 底层数据结构介绍

    Redis 底层数据结构 版本:2.9 支持的数据类型: 字符串 散列 列表 集合 有序集合 字符串 Redis 利用原生的 c 字符串进行了一次封装.封装的字符串叫做简单动态字符串:SDS(simp ...

  2. Redis底层数据结构详解

    上一篇说了Redis有五种数据类型,今天就来聊一下Redis底层的数据结构是什么样的.是这一周看了<redis设计与实现>一书,现来总结一下.(看书总是非常烦躁的!) Redis是由C语言 ...

  3. 【redis】redis底层数据结构原理--简单动态字符串 链表 字典 跳跃表 整数集合 压缩列表等

    redis有五种数据类型string.list.hash.set.zset(字符串.哈希.列表.集合.有序集合)并且自实现了简单动态字符串.双端链表.字典.压缩列表.整数集合.跳跃表等数据结构.red ...

  4. Redis底层数据结构实现

    REDIS  较宽泛的支持5种数据结构  分别为 字符串 列表 集合 散列 有序集合 关于这几种数据结构的使用 相信网上有很多资料,查看官网API 也很详细了  读者可以自己随意翻阅 很方便 . 接下 ...

  5. Redis学习笔记(二)redis 底层数据结构

    在上一节提到的图中,我们知道,可以通过 redisObject 对象的 type 和 encoding 属性.可以决定Redis 主要的底层数据结构:SDS.QuickList.ZipList.Has ...

  6. redis底层数据结构--简单动态字符串 链表 字典 跳跃表 整数集合 压缩列表

    1.动态字符串 redis中使用c语言的字符床存储字面量,默认字符串存储采用自己构建的简单动态字符串SDS(symple dynamic string) redis包含字符串的键值对都是用SDS实现的 ...

  7. Redis 的底层数据结构(字典)

    字典相对于数组,链表来说,是一种较高层次的数据结构,像我们的汉语字典一样,可以通过拼音或偏旁唯一确定一个汉字,在程序里我们管每一个映射关系叫做一个键值对,很多个键值对放在一起就构成了我们的字典结构. ...

  8. redis 底层数据结构 压缩列表 ziplist

    压缩列表是列表键和哈希键的底层实现之一.当一个列表键只包含少量列表项,并且每个列表项要么就是小整数,要么就是长度比较短的字符串,redis就会使用压缩列表来做列表键的底层实现 当一个哈希键只包含少量键 ...

  9. redis 底层数据结构 整数集合intset

    整数集合是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时Redis就会使用整数集合作为集合键的底层实现 整数集合是Redis用于保存整数值的集合抽象数据结构,它可以保存 ...

随机推荐

  1. date命令月日时分年

    # date +%Y/%m/%d2019/09/29[root@a-3e5 lpts-20190910-keyan-v0.2]# date +%H:%M20:00

  2. linux服务器市场特性高可用高性能 (集群上体现)安全

    https://www.linuxprobe.com/chapter-00.html 1 linux安全 更安全 对比windows  代码漏洞 及时修补 全世界看到源代码 2 linux 可以卸载图 ...

  3. spec2006与spec2000的对比简要说明

    ec2006使用说明  2014-10-10 五 性能测试  benchmark 一.工具介绍 SPEC CPU 2006 benchmark是SPEC新一代的行业标准化的CPU测试基准套件.重点测试 ...

  4. shell基础之99乘法表

    方法一: 1 #!/bin/bash 2 for a in {1..9};do 3 for b in {1..9};do 4 c=`echo "$a*$b" |bc` 5 if [ ...

  5. rm删除破折号开头的文件或目录

    转载地址:http://blog.chinaunix.net/uid-25266990-id-3458755.html rm删除(清除)一个或多个文件 -f 选项将强制删除文件,即使这个文件是只读的. ...

  6. Centos双网卡配置默认路由

    Centos6.5 双网卡,我们只需要一个默认路由,如果两个都有或都没有会有一系列的问题 [root@centos]# vi /etc/sysconfig/network修改以下内容NETWORKIN ...

  7. 继承(extends), 多态 , 抽象(abstract)接口() 易混难点解析

    特性 java是单继承的,一个类直接继承的父类只能有唯一的一个 java中父类可以有多个子类 Object是所有类的父类,一个类没有父类则默认继承Object; 继承中的重写 子类重写方法访问权限不能 ...

  8. 解决Caused by: org.apache.ibatis.exceptions.PersistenceException:

    在mybatis-config核心配置文件中注册了xml以后出现了新的异常错误 Caused by: java.io.IOException: Could not find resource cn.d ...

  9. 转置卷积Transposed Convolution

    转置卷积Transposed Convolution 我们为卷积神经网络引入的层,包括卷积层和池层,通常会减小输入的宽度和高度,或者保持不变.然而,语义分割和生成对抗网络等应用程序需要预测每个像素的值 ...

  10. 77GHz 和24GHz Radar性能解析

    77GHz 和24GHz Radar性能解析 一.77GHz MRR 77GHz MRR Automotive Collision Warning Radar Application MRR – Fo ...