神奇,Redis存储原理竟然是这样!

2023-07-21 02:16:42 浏览数 (2)

本文的部分内容参考自《小林Coding》,部分地方根据源代码进行剖析。

Redis源码地址:https://github.com/redis/redis.git

今天继续

Redis存储是如何实现的?

键值对的实现

Redis是一个Key-Value模式的非关系型数据库,那么Key和Value的保存模式我们在这里说一说。

其实kv给大家的第一影响是啥?数组?哈希?

没错就是哈希,redis其实是用哈希表保存的键值对,键为str,值为数据类型,哈希表中的每一对元素是哈希桶

哈希桶存放的是啥?如果学过C 的STL,这里你可以理解为pair<string,T>

下面来西索一下。

imgimg

下面来简单聊聊图上面的东西,具体的聊我会在后文说,真实的存储其实和这个图还是有些区别

Redis的数据库管理

  • redisDb 结构,表示 Redis 数据库的结构,结构体里存放了指向了 dict 结构的指针;
  • dict 结构,结构体里存放了 2 个哈希表,正常情况下都是用「哈希表1」,「哈希表2」只有在 rehash 的时候才用;
  • dictht 结构,表示哈希表的结构,结构里存放了哈希表数组,数组中的每个元素都是指向一个哈希表节点结构(dictEntry)的指针;

在最新版的dict中已经将dictht改为了数组,这样可能稍微难以理解,一会儿细说

  • dictEntry 结构,表示哈希表节点的结构,结构里存放了 void key 和 void value 指针, *key 指向的是 String 对象,而 *value 则可以指向 String 对象,也可以指向集合类型的对象,比如 List 对象、Hash 对象、Set 对象和 Zset 对象

下面我们来说说源代码,我会在保留原注释的条件下进行解释

代码语言:c复制
/* Redis database representation. There are multiple databases identified
 * by integers from 0 (the default database) up to the max configured
 * database. The database number is the 'id' field in the structure. */
//Redis数据库结构,通过ID来识别多个数据库
typedef struct redisDb {
    // 当前数据口的所有键值对空间
    dict *dict;                 /* The keyspace for this DB */
    // 存放已设置exptime的key的集合
    dict *expires;              /* Timeout of keys with a timeout set */
    
    // 客户端等待的阻塞中的key
    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP)*/
    // 客户端等待的阻塞中的key,如果key被删除,那么应该取消阻塞,是blocking_keys的子集
    dict *blocking_keys_unblock_on_nokey;    /* Keys with clients waiting for data, 
    									      *and should be unblocked if key is deleted (XREADEDGROUP).
    									      *This is a subset of blocking_keys*/
	// 可以解除阻塞的键(客户端已经收到的key)
    dict *ready_keys;           /* Blocked keys that received a PUSH */
    // 还在被watch命令监视中的键
    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */
    // 数据库id
    int id;                     /* Database ID */
    // 数据库的平均TTL
    long long avg_ttl;          /* Average TTL, just for stats */
    unsigned long expires_cursor; /* Cursor of the active expire cycle. */
    list *defrag_later;         /* List of key names to attempt to defrag one by one, gradually. */
    clusterSlotToKeyMapping *slots_to_keys; /* Array of slots to keys. Only used in cluster mode (db 0). */
} redisDb;

//dictEntry的实现很简单,就是个链表
struct dictEntry {
    void *key;
    union {
        void *val;			//其他
        uint64_t u64;		//整数
        int64_t s64;		//整数
        double d;			//双精度数值
    } v;
    struct dictEntry *next;     /* Next entry in the same hash bucket.  指向下一个hash值相同的entry*/
    void *metadata[];           /* An arbitrary number of bytes (starting at a
                                 * pointer-aligned address) of size as returned
                                 * by dictType's dictEntryMetadataBytes(). */
};


struct dict {
    dictType *type;

    dictEntry **ht_table[2];	//第二个表用来进行rehash	
    /**
    *	dictEntry **ht_table[2]; <==>
    * 	typedef struct dictht{
    *		dictEntry **table;
    *	}dictht;
	*    ditcht ht[2];
    **/
    
    unsigned long ht_used[2];	
    
	//用于保证rehash安全,如果值为1,那么不能执行rehash
    long rehashidx; /* rehashing not in progress if rehashidx == -1 */

    //redis6之后新加入,如果值>0则暂停rehash,保证rehash安全
    /* Keep small vars at end for optimal (minimal) struct padding */
    int16_t pauserehash; /* If >0 rehashing is paused (<0 indicates coding error) */
    
    //哈希的内存指数
    signed char ht_size_exp[2]; /* exponent of size. (size = 1<<exp) */

    //由dictType的dictEntryBytes定义的大小的任意字节数(从指针对齐的地址开始)。
    void *metadata[];           /* An arbitrary number of bytes (starting at a
                                 * pointer-aligned address) of size as defined
                                 * by dictType's dictEntryBytes. */
};

/* If safe is set to 1 this is a safe iterator, that means, you can call
 * dictAdd, dictFind, and other functions against the dictionary even while
 * iterating. Otherwise it is a non safe iterator, and only dictNext()
 * should be called while iterating. */
typedef struct dictIterator {
    dict *d;
    long index;
    int table, safe;
    dictEntry *entry, *nextEntry;
    /* unsafe iterator fingerprint for misuse detection. */
    unsigned long long fingerprint;
} dictIterator;

然后redisServer的内容有点多,我就截图了

image-20230720025349861image-20230720025349861

从这个图我们看出来,不仅数据可以用dict来存储,命令也是。

0 人点赞