Redis中的hashtable底层数据结构

本文深入解析了 Redis 的核心数据结构,包括 redisDb 结构及其组成部分如 dict、expires 等,以及字典结构 dict 的实现细节,如 hashFunction、keyDup 等。此外,介绍了压缩列表 ziplist 和快速列表 quicklist 的特性,它们作为 Redis 内存优化的数据结构,用于节省存储空间。文章还展示了这些数据结构的示意图,帮助理解其工作原理。

制图工具:GitHub网站      (如果觉得我的图还行,直接用我的也可以)

Redis整体结构(Redis server v=4.0.12 ,Redis服务器版本为4.0.12)

 源码: server.h

typedef struct redisDb {
    dict *dict;                 /* The keyspace for this DB */
    dict *expires;              /* Timeout of keys with a timeout set */
    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP)*/
    dict *ready_keys;           /* Blocked keys that received a PUSH */
    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */
    int id;                     /* Database ID */
    long long avg_ttl;          /* Average TTL, just for stats */
} redisDb;

  *dict:字典结构 

 *expire: 过期时间设置

 *blocking_keys:阻塞命令

  *ready_keys:

  *watched_keys

 id: 数据库编号

  avg_ttl:平均ttl

 

  字典结构:dict.h 

typedef struct dict {
    dictType *type;
    void *privdata;
    dictht ht[2];
    long rehashidx; /* rehashing not in progress if rehashidx == -1 */
    unsigned long iterators; /* number of iterators currently running */
} dict;

  *type : 

  * privdata:私有数据

  dictht  ht[2]  : hashtable,  为什么数组长度为2: 另一个使用来进行hashtable扩容的

  rehashidx:重哈希

   iterators: 

typedef struct dictType {
    uint64_t (*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;
typedef struct dictht {
    dictEntry **table;
    unsigned long size;
    unsigned long sizemask;
    unsigned long used;
} dictht;
typedef struct dictEntry {
    void *key;
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next;
} dictEntry;

key : redisObject 

typedef struct redisObject {
    unsigned type:4;
    unsigned encoding:4;
    unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
                            * LFU data (least significant 8 bits frequency
                            * and most significant 16 bits access time). */
    int refcount;
    void *ptr;
} robj;

type : 区分类型,string,list,hash,set, sorted  set

encoding:数据编码,包括embstr, int, raw

Lru: 内存淘汰

refcount: 内存管理的

*ptr :真实数据存储

总体示意图:

 

 

Ziplist(压缩列表)

 

示意图:

  • 属性介绍:

  zlbytes:  32bit,表示ziplist占用字节的总数
  zltail:   32bit,表示ziplist表中最后一项(entry)在ziplist中的偏移字节数,通过zltail可以很方便地找到最后一项,从而可以在   ziplist尾端快速地执行push或pop操作
  zlen:   16bit,表示ziplist中数据项(entry)的个数
 prerawlen:前一个entry的数据长度
 len:   entry中的数据长度
 data:  真实数据的存储

特点:

  • ziplist是为节省内存空间而生的【紧凑型】。
  • ziplist是一个为Redis专门提供的底层数据结构之一,本身可以有序也可以无序。当作为listhash的底层实现时,节点之间没有顺序;当作为zset的底层实现时,节点之间会按照大小顺序排列。

 quicklist(快速列表)

源码:  quicklist.h

typedef struct quicklist {
    quicklistNode *head;
    quicklistNode *tail;
    unsigned long count;        /* total count of all entries in all ziplists */
    unsigned long len;          /* number of quicklistNodes */
    int fill : 16;              /* fill factor for individual nodes */
    unsigned int compress : 16; /* depth of end nodes not to compress;0=off */
} quicklist;
typedef struct quicklistNode {
    struct quicklistNode *prev;
    struct quicklistNode *next;
    unsigned char *zl;
    unsigned int sz;             /* ziplist size in bytes */
    unsigned int count : 16;     /* count of items in ziplist */
    unsigned int encoding : 2;   /* RAW==1 or LZF==2 */
    unsigned int container : 2;  /* NONE==1 or ZIPLIST==2 */
    unsigned int recompress : 1; /* was this node previous compressed? */
    unsigned int attempted_compress : 1; /* node can't compress; too small */
    unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;

示意图:

 属性介绍:

   head: 头结点

  tail:尾结点

 count:ziplist中的entry节点的个数

 len: quicknode的个数

 prev:前一个节点的指针

 next:下一个节点的指针

 

Redis 中的 Set 数据类型是一种无序且不包含重复元素的集合,底层采用了两种数据结构来实现:**整数集合(intset)** 和 **哈希表(hashtable)**。这两种结构的选择是根据 Set 的实际使用场景动态决定的,以兼顾内存效率和操作性能。 当 Set 中的元素数量较少,并且所有元素都是整数时,Redis 会优先使用 **intset** 作为底层实现。intset 是一种紧凑的、基于数组的结构,专门用于存储整数集合。它的内存占用小,访问速度快,适用于存储少量整数的情况。intset 支持插入、删除和查找操作,其中删除操作通过查找目标元素的位置,然后进行内存移动和空间重新分配来实现[^4]。这种实现方式在数据量小的情况下具有较高的性能。 当 Set 中的元素数量增加到一定程度,或者元素类型不再是全部为整数时,Redis 会自动将底层实现从 intset 切换为 **哈希表(hashtable)**。在哈希表实现中,Set 的每个元素作为哈希表的 key,而 value 被设置为 NULL。这种方式支持任意类型的元素存储,并且由于哈希表的查找、插入和删除操作的平均时间复杂度为 O(1),因此在大规模数据场景下依然保持高效的性能[^3]。 Redis 的这种动态切换机制确保了在不同使用场景下都能获得最佳的性能和内存利用率。例如,在存储少量整数时使用 intset 可以显著减少内存开销;而在处理大量数据或非整数元素时,切换为哈希表可以保证操作的高效性[^2]。 ### Set 的操作特性 - **插入操作**:如果当前 Set 使用的是 intset,插入操作会检查是否需要升级编码格式(如从 16 位升级到 32 位),以容纳更大的整数;如果使用的是哈希表,则直接将元素作为 key 插入。 - **删除操作**:intset 中的删除会涉及查找元素位置并进行内存移动,而哈希表中则是标准的哈希删除操作。 - **查找操作**:intset 使用二分查找来判断元素是否存在,而哈希表则通过哈希函数定位 key。 ### 示例代码:Set 的基本操作 以下是一个简单的 Redis 客户端示例,展示如何使用 Set 的基本命令: ```python import redis # 连接 Redis 服务器 r = redis.Redis(host='localhost', port=6379, db=0) # 添加元素到 Set r.sadd('myset', 'element1', 'element2', 'element3') # 获取 Set 中的所有元素 elements = r.smembers('myset') print("Set elements:", elements) # 删除一个元素 r.srem('myset', 'element2') # 再次获取所有元素 elements = r.smembers('myset') print("Set elements after removal:", elements) ``` 这段代码展示了如何使用 Redis 的 `sadd` 命令向 Set 中添加元素,使用 `smembers` 获取所有元素,以及使用 `srem` 删除特定元素。 ### 总结 Redis 的 Set 数据类型通过 **intset** 和 **哈希表** 的组合实现了高效且灵活的存储机制。intset 在存储少量整数时表现出色,而哈希表则在处理大规模数据或非整数类型时提供了稳定的性能。这种设计不仅优化了内存使用,还确保了各种操作的高效性,使得 Redis 能够在不同的应用场景下保持出色的性能表现。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值