Redis内部数据结构Dict的实现方法

目录
  • 一、dict是什么
  • 二、dict数据结构
    • 1.结构梳理
    • 2. 扩容条件
    • 3. 缩容条件

我们平时用Redis的时候,只是了解到了它对外的一些结构,如:string、list、set、hash、zset,但是我们却很少能了解到Redis内部用的存储结构,小编将在这篇文章和大家秀一下Redis中的一个内部结构——dict。

一、dict是什么

不知道大家在用Redis的时候有没有注意到,我们在使用大多数Redis命令的时候,都会让你输入一个key,后面才会让你输入具体的值。

我们本篇文章所述的dict在Redis中最主要的作用就是用于维护Redis数据库中所有Key、value映射的数据结构,也就是我们在输入set、zadd等命令时输入的key与后面值的映射。321,上代码。代码来源(dict.h)。 如下代码所示,dict结构体里面有一个dictht 数组,dictht 里面的dictEntry 就是具体存放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 **table;
    unsigned long size; //  hashtable 容量
    unsigned long sizemask;  // size -1
    unsigned long used;  // hashtable 元素个数   used / size =1
} dictht;
typedef struct dict {
    dictType *type;
    void *privdata;
    dictht ht[2];// ht[0] , ht[1] =null
    long rehashidx; /* rehashing not in progress if rehashidx == -1 */
    unsigned long iterators; /* number of iterators currently running */
} dict;

小贴纸:dictEntry 中用到了union联合体这种结构。也就是多个变量的结构同时使用一块内存区域,区域的取值大小为该结构中长度最大的变量的值。这有利于减少内存碎片,提高内存利用率,在Java中的压缩指针技术就用到了联合体。

二、dict数据结构

1.结构梳理

我们仔细看上面代码中的结构,小编可以直接告诉你,其实它就是一个哈希表结构,在Java中相当于一个HashMap,因为Redis需要保证快速响应,所以选择哈希表作为存储结构是一个不错的决定。我们这里只一起了解结构中存具体数据的部分。

  • dictEntry:其实它就是一个链表结构,里面维护了key、value。
  • dictht :它维护了一个dictEntry 指针数组。 虽然我们肉眼能看到定义的是指针的指针,dictEntry **table。但其实指针的指针是指向了dictEntry 数组指针的首地址。Redis源码里大多会这么用 table[index]。指针这块有点绕,可以暂时直接认为它就是一个指针数组。
  • dict: dict里面维护了一个dictht ht[2] 数组,相当于两个dictht 结构。为什么要存两个dictht 结构呢。因为既然是哈希表那就要涉及到扩容,redis是单线程的,不可能一下子将所有的数据都转移到新的哈希表中,这样可能会造成服务长时间不可用,所以它退而求其次,选择了"渐进式扩容"。

小贴纸:Redis中的渐进式扩容,采用的是在内存中放置两个哈希表结构,无需扩容时,使用的是哈希表0,在扩容期间,将扩容标识设置为true,当有新数据进来的时候,发现正在扩容,就会在将新数据直接放入哈希表1。而表0中的数据会在每次有请求命令并且请求的数据在表0中时,将请求命令涉及到的数据直接挂到表1上。 在扩容期间如果执行查找命令会查找表0+表1的数据。

当然,Redis如果一直不执行命令的话。它也会有一个后台定时任务,对字典进行主动搬迁,它不会对未完成的事置之不理

// 服务器定时任务
void databaseCron() {
    ...
    if (server.activerehashing) {
        for (j = 0; j < dbs_per_call; j++) {
            int work_done = incrementallyRehash(rehash_db);
            if (work_done) {
                /* If the function did some work, stop here, we'll do
                 * more at the next cron loop. */
                break;
            } else {
                /* If this db didn't need rehash, we'll try the next one. */
                rehash_db++;
                rehash_db %= server.dbnum;
            }
        }
    }
}

2. 扩容条件

  • 当hash表中元素的个数等于数组长度时,就会开始扩容,扩容的新数组是原数组的2倍。
  • 当Redis发生其他情况没有在元素个数等于数组长度时扩容,那么Redis会有一个强制扩容的条件,就是元素个数达到数组长度5倍时进行强制扩容。
/* Expand the hash table if needed */
static int _dictExpandIfNeeded(dict *d)
{
    /* Incremental rehashing already in progress. Return. */
    if (dictIsRehashing(d)) return DICT_OK;

    /* If the hash table is empty expand it to the initial size. */
    if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);

    /* 元素个数大于等于数组长度&&(能扩容(bgsave时尽量不扩容)或元素大于5倍时强制扩容)
	 * static unsigned int dict_force_resize_ratio = 5;
	 */
    if (d->ht[0].used >= d->ht[0].size &&
        (dict_can_resize ||
         d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))
    {
        return dictExpand(d, d->ht[0].used*2);
    }
    return DICT_OK;
}

3. 缩容条件

既然有扩容,当前就有缩容,要不占那么大内存不是浪费吗?

  • 当元素小于数组长度的10%时进行缩容。
int htNeedsResize(dict *dict) {
    long long size, used;

    size = dictSlots(dict);
    used = dictSize(dict);
    return (size > DICT_HT_INITIAL_SIZE &&
            (used*100/size < HASHTABLE_MIN_FILL));
}

dict结构的实现相对来说比较简单,本文就介绍到这。

到此这篇关于Redis内部数据结构Dict的实现方法的文章就介绍到这了,更多相关redis dict数据结构内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Redis底层数据结构之dict、ziplist、quicklist详解

    目录 1 Redis dict 1.1 扩缩容的条件 1.2 渐进式rehash操作 2 Redis ziplist 2.1 ziplist结构 2.2 entry结构 3 Redis quicklist 此前我们学习了常见的Reids数据类型,这些数据类型都需要底层的数据结构的支持,现在我们来看看Redis常见的底层数据结构:dict.ziplist.quicklist. 1 Redis dict dict就是"字典"的意思,它是Redis中的一种使用的非常多的底层的数据结构,除了h

  • redis内部数据结构之SDS简单动态字符串详解

    前言 reids 没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组)而是构建了一种名为简单动态字符串的抽象类型,并为redis的默认字符串表示,因为C字符串不能满足redis对字符串的安全性.效率以及功能方面的需求 1.SDS 定义 在C语言中,字符串是以'\0'字符结尾(NULL结束符)的字符数组来存储的,通常表达为字符指针的形式(char *).它不允许字节0出现在字符串中间,因此,它不能用来存储任意的二进制数据. sds的类型定义 typedef char *sds; 每个sds

  • Redis内部数据结构Dict的实现方法

    目录 一.dict是什么 二.dict数据结构 1.结构梳理 2. 扩容条件 3. 缩容条件 我们平时用Redis的时候,只是了解到了它对外的一些结构,如:string.list.set.hash.zset,但是我们却很少能了解到Redis内部用的存储结构,小编将在这篇文章和大家秀一下Redis中的一个内部结构——dict. 一.dict是什么 不知道大家在用Redis的时候有没有注意到,我们在使用大多数Redis命令的时候,都会让你输入一个key,后面才会让你输入具体的值. 我们本篇文章所述的

  • Redis底层数据结构详解

    Redis作为Key-Value存储系统,数据结构如下: Redis没有表的概念,Redis实例所对应的db以编号区分,db本身就是key的命名空间. 比如:user:1000作为key值,表示在user这个命名空间下id为1000的元素,类似于user表的id=1000的行. RedisDB结构 Redis中存在"数据库"的概念,该结构由redis.h中的redisDb定义. 当redis 服务器初始化时,会预先分配 16 个数据库 所有数据库保存到结构 redisServer 的一

  • redis执行lua脚本的实现方法

    目录 1. 语法格式 2.类型转换 3.lua脚本 3.1 script命令 3.2 脚本原子性 3.3 脚本缓存和EVALSHA 3.4 全局变量保护 3.5 日志记录 从redis 2.6.0版本开始,redis内置了Lua解释器,并提供了eval命令来解析Lua脚本求值. 1. 语法格式 语法: eval script numkeys keys args 参数: eval - redis提供解析lua脚本的命令          script - lua脚本           numke

  • redis实现加锁的几种方法示例详解

    前言 本文主要给大家介绍了关于redis实现加锁的几种方法,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 1. redis加锁分类 redis能用的的加锁命令分表是INCR.SETNX.SET 2. 第一种锁命令INCR 这种加锁的思路是, key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作进行加一. 然后其它用户在执行 INCR 操作进行加一时,如果返回的数大于 1 ,说明这个锁正在被使用当中. 1. 客户端A请求服务器获取key的值为1表示

  • 关于redis Key淘汰策略的实现方法

    1 配置文件中的最大内存删除策略 在redis的配置文件中,可以设置redis内存使用的最大值,当redis使用内存达到最大值时(如何知道已达到最大值?),redis会根据配置文件中的策略选取要删除的key,并删除这些key-value的值.若根据配置的策略,没有符合策略的key,也就是说内存已经容不下新的key-value了,但此时有不能删除key,那么这时候写的话,将会出现写错误. 1.1 最大内存参数设置 若maxmemory参数设置为0,则分两种情况: *在64位系统上,表示没有限制.

  • 通过Docker部署Redis 6.x集群的方法

    系统环境: Redis 版本:6.0.8 Docker 版本:19.03.12 系统版本:CoreOS 7.8 内核版本:5.8.5-1.el7.elrepo.x86_64 一.什么是 Redis 集群模式 在 Redis 3.0 版本后正式推出 Redis 集群模式,该模式是 Redis 的分布式的解决方案,是一个提供在多个 Redis 节点间共享数据的程序集,且 Redis 集群是去中心化的,它的每个 Master 节点都可以进行读写数据,每个节点都拥有平等的关系,每个节点都保持各自的数据和

  • 浅谈Redis存储数据类型及存取值方法

    Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合) String存取值: 是 redis 最基本的类型 一个 key 对应一个 value.value其实不仅是String,也可以是数字.string 类型是二进制安全的.意思是 redis 的 string 可以包含任何数据.比如jpg图片或者序列化的对象.string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512M

  • 浅谈redis五大数据结构和使用场景

    老规矩,先抛结论后验证 string:有点像java的hashMap,存的时候什么key,取的时候也什么key,常用于做缓存,保存用户信息.查询列表等: hash:这个有点像hashMap的value又套了个hashMap,下文有举例,一看就明白了: list:有序列表,类似Java的linkedList,可以在左边右边插入数据: set:去重集合,类似Java的hashset,可用于求交集,比如共同好友: zset:带权重的set集合,可用于做排行榜: 为了方便理解,我们基于这个dog类来做测

随机推荐