Redisストレージ構造のソースコード分析

目次

1.ハッシュテーブル

2.辞書

3.redisデータベースの構造


1.ハッシュテーブル

//dict.h

typedef struct dictEntry {
    void *key;   //键
    union {
        void *val;  可以指向不同类型  redisobject
        uint64_t u64;  //用于redis集群 哨兵模式 选举算法
        int64_t s64;  //记录过期时间
        double d;
    } v;  //值
    struct dictEntry *next;  //指向下个哈希表节点,形成链表(解决哈希冲突使用,将多个哈希值相同的键链接在一起)
} dictEntry;

/* This is our hash table structure. Every dictionary has two of this as we
 * implement incremental rehashing, for the old to the new table. */
typedef struct dictht {
    dictEntry **table;    //table是一个数组,数组中每个元素都是指向dictEntry结构的指针,每个dictEntry结构体保存着一个键值对
    unsigned long size;   //size是哈希表的大小,也就是table数组的大小,默认值为4.
    unsigned long sizemask;  //sizemask总是等于size-1,这个属性和哈希值一起决定一个键应该被放在table数组的哪个索引上面;
    unsigned long used;   //hash表里已有的数量
} dictht;

まず、空のハッシュテーブルを見てみましょう。

次に、次のポインタを介して、同じインデックス値を持つ2つのキーk1とk0を接続する方法を示します。

上記の構造は、私たちがよく言う配列+リンクリストです(リンクリストはヘッド挿入方法を使用します

2.辞書

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);
    int (*expandAllowed)(size_t moreMem, double usedRatio);
} dictType;

typedef struct dict {
    dictType *type;  //该字典对应的特定操作函数
    void *privdata;  //该字典依赖的数据,上下文,具体是指操作上下文的对象
    dictht ht[2];    //哈希表,二维的,默认使用ht[0],当需要rehash的时候,会利用ht[1]进行
    long rehashidx; /* rehashing not in progress if rehashidx == -1 ,rehash的索引,记录了rehash目前的进度,当没有进行rehash时其值为-1*/
    int16_t pauserehash; /* If >0 rehashing is paused (<0 indicates coding error) ,hash表的迭代器,一般用于rehash和主从复制等等 */ 
} dict;

type属性とprivdata属性は、ポリモーフィックディクショナリを作成するためにさまざまなタイプのキーと値のペアに設定されます。

  • type属性は、dictType構造体へのポインターです。各dictType構造体は、特定のタイプのキーと値のペアを操作するための関数のクラスターを格納します。Redisは、さまざまな目的の辞書にさまざまなタイプ固有の関数を設定します。
  • Privdata属性は、これらのタイプ固有の関数に渡す必要のあるオプションのパラメーターを保持します。

ht属性は、2つの項目を含む配列です。配列内の各項目はdicthtハッシュテーブルです。通常、辞書はht [0]ハッシュテーブルのみを使用し、ht [1]ハッシュテーブルはht [を再ハッシュするときにのみ使用されます。 0]ハッシュテーブル。

通常の辞書を見せましょう:

/* Low level add or find:
 * This function adds the entry but instead of setting a value returns the
 * dictEntry structure to the user, that will make sure to fill the value
 * field as they wish.
 *
 * This function is also directly exposed to the user API to be called
 * mainly in order to store non-pointers inside the hash value, example:
 *
 * entry = dictAddRaw(dict,mykey,NULL);
 * if (entry != NULL) dictSetSignedIntegerVal(entry,1000);
 *
 * Return values:
 *
 * If key already exists NULL is returned, and "*existing" is populated
 * with the existing entry if existing is not NULL.
 *
 * If key was added, the hash entry is returned to be manipulated by the caller.
 */
dictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing)
{
    long index;
    dictEntry *entry;
    dictht *ht;

    if (dictIsRehashing(d)) _dictRehashStep(d);  //如果字典d的rehash还没有操作完,那就在add的时候进行一部分rehash

    /* Get the index of the new element, or -1 if
     * the element already exists. */
    if ((index = _dictKeyIndex(d, key, dictHashKey(d,key), existing)) == -1)
        return NULL;

    /* Allocate the memory and store the new entry.
     * Insert the element in top, with the assumption that in a database
     * system it is more likely that recently added entries are accessed
     * more frequently. */
    ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];  //如果字典d的rehash还没有操作完,那就将新数据插入到d->ht[1]中去。
    entry = zmalloc(sizeof(*entry));
    entry->next = ht->table[index];
    ht->table[index] = entry;
    ht->used++;

    /* Set the hash entry fields. */
    dictSetKey(d, entry, key);
    return entry;
}

当初、辞書のハッシュテーブルのサイズはわずか4(sizemaskは3)でしたが、ハッシュ関数によって計算されたハッシュ値は非常に大きくなる可能性があります。このとき、ハッシュ値はビット単位で操作されます(&sizemask)。 (n%sizeはn&(size-1)に等しい)、ハッシュテーブルに格納されている位置インデックスを取得します。

3.redisデータベースの構造

redisの内部には、redisServer構造のグローバル変数サーバーがあります。サーバーは、現在のプロセスのPID、サーバーのポート番号、データベースの数、統計情報など、redisサーバーのすべての情報を保存します。オン。もちろん、データベースの数やredisDb配列などのデータベース情報も含まれています。

struct redisServer {
    ...
    redisDb *db;
    int dbnum;                      /* Total number of configured DBs */
};

明らかに、dbnumはredis配列の長さです。各データベースはredisDbに対応します。redisクライアントでは、Nを選択することで使用するデータベースを選択でき、各データベースは互いに独立しています。例:「baichao」という名前のキーは、同時に異なるデータベースに存在できます。

/* 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. */
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 */
    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. */
} redisDb;

サーバーはグローバル変数であり、複数のredisDbが含まれ、各redisDbはキースペースであり、各キースペースは互いに独立しており、互いに干渉しません。

各redisデータベースは独立したキースペースです。これは、redisデータベースがハッシュテーブルであると考える可能性があるためです。ただし、redisDbの定義からすると、これはハッシュテーブルではなく、多くのハッシュテーブルを含む構造です。これは、redisがsetとgetに加えて、より豊富な機能を提供する必要があるためです(例:キータイムアウトメカニズム)

おすすめ

転載: blog.csdn.net/weixin_40179091/article/details/115259623