深入了解Redis基础数据结构

77 阅读5分钟
  • 基本数据类型
名称数据类型(Type)说明
简单动态字符串类stringredis中最基本的数据类型,一个key对应一个value,string类型是二进制安全的,意思是 redis 的 string 可以包含任何数据。如数字,字符串,jpg图片或者序列化的对象
链表listlist 说白了就是链表(redis 使用双端链表实现的 list),是有序的,value可以重复,可以通过下标取出对应的value值,左右两边都能进行插入和删除数据
字典hash是一个Map,指值本身又是一种键值对结构,如 value={{field1,value1},......fieldN,valueN}}
集合set集合类型也是用来保存多个字符串的元素,但和列表不同的是集合中 1. 不允许有重复的元素,2.集合中的元素是无序的,不能通过索引下标获取元素,3.支持集合间的操作,可以取多个集合取交集、并集、差集
有序集合zset有序集合和集合有着必然的联系,保留了集合不能有重复成员的特性,区别是,有序集合中的元素是可以排序的,它给每个元素设置一个分数,作为排序的依据
  • 数据类型底层编码结构图

redis.png

  • 数据库结构体源码 (redisDb、dict、dictht、dictEntry、redisObject 源码示意)

    • redisDb

    Redis是内存数据库,除了需要redisObject对基础数据结构封装外,还需要一个结构体对数据库进行封装,用来管理数据库相关数据 和实现相关操作,这个结构体就是redisDb。

    redisDb有两个重要属性——dict和expires,分别是键空间散列 表、过期时间散列表。dict保存了所有的键值对,expires保存了键的过期时间,像scan、move、sort等命令是对redisDb键空间散列表的操作,expire、persist等命令是对redisDb键的过期时间散列表的操作,具 体我们将在后面讲解,redisDb结构体定义如下

    typedef struct redisDb {
        dict *dict; /* 键空间字典 */
        dict *expires; /* key的超时时间字典 */
        dict *blocking_keys; /* 阻塞的key */
        dict *ready_keys; /* 准备好的key */
        dict *watched_keys; /* 执行事务的key */
        int id; /* 数据库ID */
        long long avg_ttl; /* 平均生存时间,用于统计 */
        list *defrag_later; /* 逐渐尝试逐个碎片整理的key列表*/
    } redisDb;
    ​
    
    属性说明
    *dict键空间散列表,存放所有键值对
    *expires过期时间散列表,存放键的过期时间,注意dict和expires中的键都指向同一个键的sds
    *blocking_keys处于阻塞状态的键和对应的client
    *ready_keys解除阻塞状态的键和对应的client,与blocking_keys,属性相对,为了实现需要阻塞的命令设计
    *watched_keyswatch的键和对应的client,主要用于事务
    id数据库ID
    avg_ttl数据库内所有键的平均生存时间
    defrag_later逐渐尝试逐个碎片整理的key列表
    • dict
    typedef struct dict {
        dictType *type; /*该字典对应的特定操作函数*/
        void *privdata; /*该字典依赖的数据*/
        dictht ht[2]; /*Hash表,键值对存储再此; 一个字典有两个哈希表*/
        long rehashidx; /*rehash索引 rehashing not in progress if rehashidx == -1 */
        unsigned long iterators; /*当前正在使用的迭代器数量/ */
    } dict;
    ​
    
    • dictType
    typedef struct dictType {
        uint64_t (*hashFunction)(const void *key);/*该字典对应的Hash函数*/
        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;/*值的销毁函数*/
    • dictht
    typedef struct dictht {
        dictEntry **table; // 指针数组,用于存储键值对
        unsigned long size; // table数组的大小
        unsigned long sizemask; //掩码 = size - 1
        unsigned long used; //table数组已存元素个数,包含next单链表的数据
    } dictht;
    
    • dictEntry
    typedef struct dictEntry {
        void *key; /*存储键*/
        union {
            void *val; /*db.dict中的val*/
            uint64_t u64;
            int64_t s64;/*db.expires中存储过期时间*/
            double d;
        } v; /*封装成一个对象*/
        struct dictEntry *next; /*当Hash冲突时,指向冲突的元素,形成单链表*
    } dictEntry;
    
    • redisObject

    Redis主要的数据结构,为了便于操作和维护,Redis在此基础上封装了redisObject对象,其定义在server.h文件,redisObject根据type的不同可以分为字符串对象、列表对象、集合对 象、有序集合对象、散列表对象、模块对象和流对象

    此外对象保存了数据底层存储所使用的编码,在存储数据时,Redis会自动选择合适的编码。对象还实现了引用计数,当程序不再使 用对象时,对象会自动释放

    typedef struct redisObject {
        unsigned type:4; //type表示Redis对象的类型,占用4位
        unsigned encoding:4; //encoding表示对象内部存储的编码,在一定条件下, 对象的编码可以在多个编码之间转化,长度占用4位
        unsigned lru:LRU_BITS; //lru占用24位,当用于LRU时表示最后一次访问时间,当用 于LFU时,高16位记录分钟级别的访问时间,低8位记录访问频率0到 255,默认配置8位可表示最大100万访问频次
        int refcount; // refcount表示对象被引用的LRU_BITS,当refcount为0时候,表示该对象不被任何对象引用,可进行垃圾回收
        void *ptr;// ptr是指向具体数据的指针,比如一个字符串对象,该指针 指向存放数据sds的地址
    } robj;
    
    属性说明
    type#define OBJ_STRING 0 /* 字符串对象 / #define OBJ_LIST 1 / 列表对象 / #define OBJ_SET 2 / 集合对象 / #define OBJ_ZSET 3 / 有序集合对象 / #define OBJ_HASH 4 / 散列表对象 / #define OBJ_MODULE 5 / 模块对象 / #define OBJ_STREAM 6 / 流对象 */
    encoding#define OBJ_ENCODING_RAW 0 /* Raw representation / #define OBJ_ENCODING_INT 1 / 编码为整数 / #define OBJ_ENCODING_HT 2 / 编码为散列表 / #define OBJ_ENCODING_ZIPMAP 3 / 编码为zipmap / #define OBJ_ENCODING_LINKEDLIST 4 / 不再使用:旧列表编码 / #define OBJ_ENCODING_ZIPLIST 5 / 编码为压缩列表 / #define OBJ_ENCODING_INTSET 6 / 编码为整数集合 / #define OBJ_ENCODING_SKIPLIST 7 / 编码为跳表 / #define OBJ_ENCODING_EMBSTR 8 / 编码为简短字符串 / #define OBJ_ENCODING_Quicklist 9 / 编码为快速链表 / #define OBJ_ENCODING_STREAM 10 / 编码为listpacks的基数树*/
  • 数据库结构体图

redis源码架构图.png

总结

由Redis基础的数据结构,引出对基本数据类型的介绍

\