redis 字典

字典:(符号表)

字典就是一个存储kv的存储结构,类似与c++的map,redis数据库的底层就是使用字典实现的

除了数据库,字典也是哈希键的底层实现

字典使用哈希表实现,哈希表中存储的都是kv结构

typedef struct dictht {

    // 哈希表数组
    dictEntry **table;

    // 哈希表大小
    unsigned long size;

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

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

} dictht;

sizemask和哈希值一起决定了这儿节点应该放在哪里,我们每一个哈希表节点都有一个next属性,这个可以解决链表冲突的问题,使得多个键值一样的可以连在一起

下面我们看一下哈希表节点的定义:

typedef struct dictEntry {

    // 键
    void *key;

    // 值
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
    } v;

    // 指向下个哈希表节点,形成链表
    struct dictEntry *next;

} dictEntry;

下面是字典的定义:

type主要是针对不同的类型,private是针对函数的参数

其中计算哈希值的函数就在type里面指向的

有个哈希表数组,ht[1]只有rehash的时候使用,rehashindex也是rehash的时候使用

typedef struct dict {

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

    // 私有数据
    void *privdata;

    // 哈希表
    dictht ht[2];

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

} dict;
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;

当加入一个键值的时候,我们先根据type里面的函数计算出哈希值,&mask计算出索引值,加入哈希表的指定索引中,

为了解决哈希表的冲突,我们使用拉链发,但是为了考虑效率,我们通常将新加入的节点放在最前面,不yongO(N)掺入

rehash:

哈希表的键值会不听的增多减少,为了让负载因子,维持在一个合理的范围,我们需要适当的进行扩展和收缩

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

哈希表的扩展和收缩的条件:

1:如果没有执行BSAVE或者BGREWRITEAOF,并且负载因子大于等于1

2:如果执行BSAVE或者BGREWRITEAOF,并且负载因子大于等于5

这样设计是因为如果执行的话,会fork出新的进程,因为遵循写实复制,为了尽量避免写入内存进行复制,所以将负载因子提高一些

如果负载因子小0.1执行收缩

渐进事rehash:

因为哈希表的数据可能特别的多,所有rehash不是一次完成的,是多次分批完成的,这里就用到了reashindex,最开始rehashindex=0,表示对索引值0指向的复制,结束了,开始索引值1的,rehashindx+1,这个过程中如果查找的话,会先查找ht[0]->ht[1],添加的话都会添加大1里面,这样可能保证服务器正常的运作

时间: 2024-10-02 06:31:07

redis 字典的相关文章

Redis 字典的实现

[Redis 字典的实现] 注意 dict 类型使用了两个指针,分别指向两个哈希表. 其中, 0 号哈希表(ht[0])是字典主要使用的哈希表, 而 1 号哈希表(ht[1])则只有在程序对 0 号哈希表进行 rehash 时才使用. table 属性是个数组, 数组的每个元素都是个指向 dictEntry 结构的指针. 每个 dictEntry 都保存着一个键值对, 以及一个指向另一个 dictEntry 结构的指针: 整体结构如下: 参考:http://redisbook.readthedo

跋山涉水 —— 深入 Redis 字典遍历

前言 Redis 字典的遍历过程逻辑比较复杂,互联网上对这一块的分析讲解非常少.我也花了不少时间对源码的细节进行了整理,将我个人对字典遍历逻辑的理解呈现给各位读者.也许读者们对字典的遍历过程有比我更好的理解,还请不吝指教. 一边遍历一边修改 我们知道 Redis 对象树的主干是一个字典,如果对象很多,这个主干字典也会很大.当我们使用 keys 命令搜寻指定模式的 key 时,它会遍历整个主干字典.值得注意的是,在遍历的过程中,如果满足模式匹配条件的 key 被找到了,还需要判断 key 指向的对

REDIS 字典数据结构

对于REDIS来讲  其实就是一个字典结构,key ---->value  就是一个典型的字典结构 [当然  对于vaule来讲的话,有不同的内存组织结构 这是后话] 试想一个这样的存储场景: key:"city" value:"beijing" 如果有若干个这样的键值对,你该怎么去存储它们呢 要保证写入和查询速度非常理想~! 抛开redis不说,如果你想要存储 快速查找的话, Hash算法是最快的,理想的哈希函数可以带来O(1)的查找速度,你都这样想,那么r

Redis字典数据结构

字典节点.key/value结构. typedef struct dictEntry {     void *key;     union {         void *val;         uint64_t u64;         int64_t s64;         double d;     } v;     struct dictEntry *next; } dictEntry; 字典表 typedef struct dictht {     dictEntry **tabl

Redis源码阅读笔记(2)——字典(Map)实现原理

因为redis是用c写的,c中没有自带的map,所以redis自己实现了map,来看一下redis是怎么实现的. 1.redis字典基本数据类型 redis是用哈希表作为字典的底层实现,dictht是哈希表的定义: typedef struct dictht { // 哈希表节点指针数组(俗称桶,bucket) dictEntry **table; // 指针数组的大小 unsigned long size; // 指针数组的长度掩码,用于计算索引值 unsigned long sizemask

Redis数据结构(二)字典

Redis字典其实就是Hash表,其实现和JAVA语言中的hashmap结构大同小异,按Key-Value方式存储键值对,但是又存在一定的差异. java中的hashmap结构即包含hash表,又实现了rehash自我扩充: 而redis字典则通过dictht结构实现hash表,通过字典(dict)实现rehash(字典中包含一个dictht数组dictht ht[2]). Redis字典的实现 Redis字典所使用的哈希表由dict.h/dictht结构定义: typedef struct d

redisbook笔记——redis内部数据结构

在Redis的内部,数据结构类型值由高效的数据结构和算法进行支持,并且在Redis自身的构建当中,也大量用到了这些数据结构. 这一部分将对Redis内存所使用的数据结构和算法进行介绍. 动态字符串 Sds(Simple Dynamic String,简单动态字符串) Sds在Redis中的主要作用有以下两个: 1. 实现字符串对象(StringObject): 2. 在Redis程序内部用作char* 类型的替代品: 对比C 字符串,sds有以下特性: –可以高效地执行长度计算(strlen):

Redis 一、数据结构与对象--五大数据类型的底层结构实现

redis上手比较简单,但是它的底层实现原理一直很让人着迷.具体来说的话,它是怎么做到如此高的效率的?阅读Redis设计与实现这本书,可以很好的理解Redis的五种基本类型:String,List,Hash,Set,ZSet是如何在底层实现的.还可以了解Redis的其他机制的原理.我们现在来看看Redis中的基本的数据结构吧. 简单动态字符串 Redis的简单动态字符串,通常被用来存储字符串值,几乎所有的字符串类型都是SDS的.另外,SDS还常被用作缓冲区(buffer):AOF模块中的AOF缓

Redis架构设计

一.前言 因为近期项目中开始使用Redis,为了更好的理解Redis并应用在适合的业务场景,需要对Redis设计与实现深入的理解. 我分析流程是按照从main进入,逐步深入分析Redis的启动流程.同时根据Redis初始化的流程,理解Redis各个模块的功能及原理. 二.redis启动流程 1.初始化server变量,设置redis相关的默认值 2.读入配置文件,同时接收命令行中传入的参数,替换服务器设置的默认值 3.初始化服务器功能模块.在这一步初始化了包括进程信号处理.客户端链表.共享对象.