一. 概述

  字典又称符号表(symbol table),关联数组(associative array), 映射(map),是一种用于保存键值对(key-value pair)的抽象数据结构。在字典中,一个key和一个value进行关联称为键值对。在字典中每个键都是唯一的,程序可以在字典中根据键查找关联的值,或通过键更新删除值等操作。在C语言中并没有内置这种数据结构,因此Redis构建了自己的字典实现。在Redis中应用广泛, 对数据库的增,删,查,改 都是构建在对字典的操作之上的。

-- 例1
127.0.0.1:> set msg "hello world"
OK
127.0.0.1:> get msg
"hello world"

  在例1中数据库创建一个键为"msg",值为"hello world"的键值对,这个键值对就是保存在数据库的字典里面。字典还是哈希键的底层实现之一,当哈希键包含的键值对比较多,或者键值对中的元素都是比较长的字符串时,Redis就会使用字典作为哈希键的底层实现。

-- 例2: website是一个包含3个键值对的哈希键(也叫哈希表),哈希键(key)为 website,哈希键的节点键是:数据库名字,哈希键的节点值是:网址
127.0.0.1:> hmset website redis "Redis.io" mariadb "mariadb.org" mongodb "mongodb.org"
OK
127.0.0.1:> hlen website
(integer)
127.0.0.1:> hgetall website
) "redis"
) "Redis.io"
) "mariadb"
) "mariadb.org"
) "mongodb"
) "mongodb.org"

  在例2中,website哈希键的底层实现就是一个字典。字典中包含了3个键值对。字典除了用来实现数据库和哈希键之处,Redis在后续学习中会看到各种不同应用。

二. 字典的实现

   一个哈希(键)表里面可以有多个哈希节点(key-vlaue), 每个哈希节点保存了字典的一个键值对。下面三个小节将分别介绍Redis的哈希表,哈希表节点,以及字典的实现。

  2.1 哈希表定义

typedef struct dictht
{
//哈希表数组,C语言中,*号是为了表明该变量为指针,有几个* 号就相当于是几级指针,这里是二级指针,理解为指向指针的指针
dictEntry **table; //哈希表大小
unsigned long size; //哈希表大小掩码,用于计算索引值
unsigned long sizemask; //该哈希已有节点的数量
unsigned long used; }dictht;

    上面table属性是一个数组,数组中的每个元素都是一个指向dict.h/dictEntry结构的指针,每个dictEntry结构保存着一个键值对,size属性记录了哈希表的大小,也是table数组的大小,而used属性则记录哈希表目前已有节点(键值对)的数量。sizemask属性的值总是等于 size-1(从0开始),这个属性和哈希值一起决定一个键应该被放到table数组的哪个索引上面。

    例如:上面例2中,哈希表叫website,  对应一个dictht 结构,键值对table数组值是[3], 哈希表size值是3,索引值sizemask值是2,已有节点数量used值是3。

  2.2 哈希表节点定义 (键值对)

//哈希表节点定义dictEntry结构表示,每个dictEntry结构都保存着一个键值对。
typedef struct dictEntry
{
//键
void *key;
//值
union{
void *val;
uint64_tu64;
int64_ts64;
}v; // 指向下个哈希表节点,形成链表
struct dictEntry *next;
}dictEntry;

    上面dictEntry 结构中,key属性保存着键值中的键,而v属性则保存着键值对中的值,其中键值(v属性)可以是一个指针,或uint64_t整数,或int64_t整数。 next属性是指向另一个哈希表节点的指针,这个指针可以将多个哈希值相同的键值对连接在一起,解决键冲突问题。

    下图通过next指针,将两个索引值相同(索引是2)的键k1和k0连接在一起。

  2.3 字典定义

// Redis中的字典由dict.h/dict结构表示
typedef struct dict
{
//类型特定函数
void *type; //私有数据
void *privdata; //哈希表
dictht ht[]; // rehash 索引
int trehashidx;
}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 *(*keyDup) (void *privdata,const void *obj); //复制值的函数
void *(*keyCompare) (void *privdata,const void *key1, const void *key2); //销毁键的函数
void (*keyDestructor) (void *privdata, void *key); //销毁值的函数
void (*keyDestructor) (void *privdata, void *obj);
}dictType;

    ht属性是一个包含两个项的数组,数组中的每个项都是一个dictht哈希表, 一般情况下,字典只使用ht[0] 哈希表, ht[1]哈希表只会对ht[0] 哈希表进行rehash时使用。另一个和rehash有关的属性是rehashidx,它记录了rehash目前的进度,如果目前没有进行rehash,值为-1。下面图是一个没有进行rehash的字典。

  rehash是指渐进式的哈希,一张表是旧表,一张表是新表,当hashtable的大小需要动态改变的时候,旧表中的元素就往新开辟的新表中迁移,当下一次变动大小,当前的新表又变成了旧表,以此达到资源的复用和效率的提升。

redis 系列5 数据结构之字典(上)的更多相关文章

  1. redis 系列6 数据结构之字典(下)

    一.概述 接着上篇继续,这篇把数据结构之字典学习完, 这篇知识点包括:哈希算法,解决键冲突, rehash , 渐进式rehash,字典API. 1.1 哈希算法 当一个新的键值对 需要添加到字典里面 ...

  2. Redis系列二 - 数据结构

    前言 redis作为我们开发的一大神器,我们接触肯定不会少,但是很多同学也许只会存储String类型的值,这是非常不合理的.在这里,将带大家认识Redis的5中数据结构. 1.问:Redis有那些数据 ...

  3. redis 系列7 数据结构之跳跃表

    一.概述 跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的.在大部分情况下,跳跃表的效率可以和平衡树(关系型数据库的索引就是平衡树 ...

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

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

  5. redis 系列8 数据结构之整数集合

    一.概述 整数集合(intset)是集合键的底层实现之一, 当一个集合只包含整数值元素,并且这个集合元素数量不多时, Redis就会使用整数集合作为集合键的底层实现.下面创建一个只包含5个元素的集合键 ...

  6. redis 系列4 数据结构之链表

    一. 概述 链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可能通过增删节点来灵活地调整链表的长度.作为一种数据结构,在C语言中并没有内置的这种数据结构.所以Redis构建了自己的链表实现 ...

  7. redis 系列3 数据结构之简单动态字符串 SDS

    一.  SDS概述 Redis 没有直接使用C语言传统的字符串表示,而是自己构建了一种名为简单动态字符串(simple dynamic string, SDS)的抽象类型,并将SDS用作Redis的默 ...

  8. 【目录】redis 系列篇

    随笔分类 - redis 系列篇 redis 系列27 Cluster高可用 (2) 摘要: 一. ASK错误 集群上篇最后讲到,对于重新分片由redis-trib负责执行,关于该工具以后再介绍.在进 ...

  9. redis 系列14 有序集合对象

    一. 有序集合概述 Redis 有序集合对象和集合对象一样也是string类型元素的集合,且不允许重复的成员.不同的是每个元素都会关联一个double类型的分数.redis正是通过分数来为集合中的成员 ...

随机推荐

  1. linux 访问到对应的接口

    8080端口已经打开. 8080端口已被监听 telnet 192.168.163.128 8080   成功 也可以将防火墙关闭,这样所有的接口已经开放,不会再被拦截.这种情况下只要8080端口被某 ...

  2. 南京邮电大学//bugkuCTF部分writeup

    WEB 1.签到题 nctf{flag_admiaanaaaaaaaaaaa} 右键查看源代码或按f12即可. 2.这题不是web nctf{photo_can_also_hid3_msg} 下载图片 ...

  3. 展开被 SpringBoot 玩的日子 《 五 》 spring data jpa 的使用

    在上篇文章< 展开被 SpringBoot 玩的日子 < 二 >WEB >中简单介绍了一下spring data jpa的基础性使用,这篇文章将更加全面的介绍spring da ...

  4. C++输出

    setiosflags 意思就是设置输入输出的标志iso::fixed 是操作符setiosflags 的参数之一,该参数指定的动作是以带小数点的形式表示浮点数,并且在允许的精度范围内尽可能的把数字移 ...

  5. iOS 获取app进程被杀死事件

    程序被用户双击上滑杀死后,就对app做一些特殊的处理 下面的方法可以获取到用户双击上滑杀死的事件 - (void)applicationDidEnterBackground:(UIApplicatio ...

  6. Pi 3B+编译安装python3.6.8

    树莓派镜像版本2018-11-13,更新到2019-01-09 sudo apt-get update sudo apt-get upgrade -dev libgdbm-dev libsqlite3 ...

  7. HBase shell scan 过滤器用法总结

    比较器: 前面例子中的regexstring:2014-11-08.*.binary:\x00\x00\x00\x05,这都是比较器.HBase的filter有四种比较器: (1)二进制比较器:如’b ...

  8. EXCLE 导入 或 导出

    首先要引用 NPOI.dll   (可在网上下载!)//导入public void OnSubmit()        {            string path = Server.MapPat ...

  9. mysql_Navicat数据库破解

    Navicat Premium 12.1.16.0安装与激活 Navicat Premium 12是一套数据库开发管理工具,支持连接 MySQL.Oracle等多种数据库,可以快速轻松地创建.管理和维 ...

  10. 【记录】Windows 操作系统常用快捷命令

    https://www.lifewire.com/command-line-commands-for-control-panel-applets-2626060 打印机      control pr ...