struct

sds結(jié)構
sds主要是用來存儲字符串

struct sdshdr {
    // buf 中已占用空間的長度
    int len;
    // buf 中剩余可用空間的長度
    int free;
    // 數(shù)據(jù)空間
    char buf[];
};

空間預分配

/*
 * 對 sds 中 buf 的長度進行擴展督函,確保在函數(shù)執(zhí)行之后借卧,
 * buf 至少會有 addlen + 1 長度的空余空間
 * (額外的 1 字節(jié)是為 \0 準備的)
 *
 * 返回值
 *  sds :擴展成功返回擴展后的 sds
 *        擴展失敗返回 NULL
 *
 * 復雜度
 *  T = O(N)
 */
sds sdsMakeRoomFor(sds s, size_t addlen) {
    struct sdshdr *sh, *newsh;
    // 獲取 s 目前的空余空間長度
    size_t free = sdsavail(s);
    size_t len, newlen;
    // s 目前的空余空間已經(jīng)足夠悼瓮,無須再進行擴展距淫,直接返回
    if (free >= addlen) return s;
    // 獲取 s 目前已占用空間的長度
    len = sdslen(s);
    sh = (void*)(s - (sizeof(struct sdshdr)));
    // s 最少需要的長度
    newlen = (len + addlen);
    // 根據(jù)新長度,為 s 分配新空間所需的大小
    if (newlen < SDS_MAX_PREALLOC)
        // 如果新長度小于 SDS_MAX_PREALLOC (1M)
        // 那么為它分配兩倍于所需長度的空間
        newlen *= 2;
    else
        // 否則佑颇,分配長度為目前長度加上 SDS_MAX_PREALLOC
        newlen += SDS_MAX_PREALLOC;
    // T = O(N)
    newsh = zrealloc(sh, sizeof(struct sdshdr) + newlen + 1);

    // 內(nèi)存不足雏吭,分配失敗,返回
    if (newsh == NULL) return NULL;

    // 更新 sds 的空余長度
    newsh->free = newlen - len;

    // 返回 sds
    return newsh->buf;
}

刪除空間

/*
 * 回收 sds 中的空閑空間淮悼,
 * 回收不會對 sds 中保存的字符串內(nèi)容做任何修改咐低。
 *
 * 返回值
 *  sds :內(nèi)存調(diào)整后的 sds
 *
 * 復雜度
 *  T = O(N)
 */
sds sdsRemoveFreeSpace(sds s) {
    struct sdshdr *sh;

    sh = (void*)(s - (sizeof(struct sdshdr)));

    // 進行內(nèi)存重分配,讓 buf 的長度僅僅足夠保存字符串內(nèi)容
    // T = O(N)
    sh = zrealloc(sh, sizeof(struct sdshdr) + sh->len + 1);

    // 空余空間為 0
    sh->free = 0;

    return sh->buf;
}



鏈表

typedef struct listNode {

    // 前置節(jié)點
    struct listNode *prev;

    // 后置節(jié)點
    struct listNode *next;

    // 節(jié)點的值
    void *value;

} listNode;

鏈表里面封裝了一個迭代器

//
// listIter 雙端鏈表迭代器
//
typedef struct listIter {

    // 當前迭代到的節(jié)點
    listNode *next;

    // 迭代的方向
    int direction;

} listIter;



字典

//哈希表結(jié)點
typedef struct dictEntry {
    // 鍵
    void *key;

    // 值
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
    } v;

    // 指向下個哈希表節(jié)點袜腥,形成鏈表, 哈希沖突解決的一種方法
    struct dictEntry *next;

} dictEntry;

//
// dictht 哈希表
//每個字典都使用兩個哈希表见擦,從而實現(xiàn)漸進式 rehash
// 
typedef struct dictht { // 這是字典的頭部

    // 哈希表數(shù)組, 每個元素都是一條鏈表
    dictEntry **table;

    // 哈希表大小
    unsigned long size;              //初始值為4钉汗, 跟STL不太一樣

    // 哈希表大小掩碼,用于計算索引值
    // 總是等于 size - 1
    unsigned long sizemask;

    // 該哈希表已有節(jié)點的數(shù)量
    unsigned long used;

} dictht;

//
// dict 字典
//
typedef struct dict {
    // 類型特定函數(shù)
    dictType *type; // type里面主要記錄了一系列的函數(shù),可以說是規(guī)定了一系列的接口

    // 私有數(shù)據(jù)
    void *privdata; // privdata保存了需要傳遞給那些類型特定函數(shù)的可選參數(shù)

    // 哈希表
    dictht ht[2]; // 有兩張hash表

    // rehash 索引
    // 并沒有rehash時鲤屡,值為 -1
    int rehashidx; /* rehashing not in progress if rehashidx == -1 */

    int iterators; // 目前正在運行的安全迭代器的數(shù)量

} dict;

哈希算法

/* Thomas Wang's 32 bit Mix Function */
unsigned int dictIntHashFunction(unsigned int key) {
    key += ~(key << 15);
    key ^= (key >> 10);
    key += (key << 3);
    key ^= (key >> 6);
    key += ~(key << 11);
    key ^= (key >> 16);
    return key;
}

/* MurmurHash2, by Austin Appleby
 * Note - This code makes a few assumptions about how your machine behaves -
 * 1. We can read a 4-byte value from any address without crashing
 * 2. sizeof(int) == 4
 *
 * And it has a few limitations -
 *
 * 1. It will not work incrementally.
 * 2. It will not produce the same results on little-endian and big-endian
 *    machines.
 */
unsigned int dictGenHashFunction(const void *key, int len) {
    /* 'm' and 'r' are mixing constants generated offline.
    They're not really 'magic', they just happen to work well.  */
    uint32_t seed = dict_hash_function_seed;
    const uint32_t m = 0x5bd1e995;
    const int r = 24;

    /* Initialize the hash to a 'random' value */
    uint32_t h = seed ^ len;

    /* Mix 4 bytes at a time into the hash */
    const unsigned char *data = (const unsigned char *)key;

    while (len >= 4) {
        uint32_t k = *(uint32_t*)data;

        k *= m;
        k ^= k >> r;
        k *= m;

        h *= m;
        h ^= k;

        data += 4;
        len -= 4;
    }

    /* Handle the last few bytes of the input array  */
    switch (len) {
    case 3: h ^= data[2] << 16;
    case 2: h ^= data[1] << 8;
    case 1: h ^= data[0]; h *= m;
    };

    /* Do a few final mixes of the hash to ensure the last few
    * bytes are well-incorporated. */
    h ^= h >> 13;
    h *= m;
    h ^= h >> 15;

    return (unsigned int)h;
}

/* And a case insensitive hash function (based on djb hash) */
unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len) {
    unsigned int hash = (unsigned int)dict_hash_function_seed;

    while (len--)
        hash = ((hash << 5) + hash) + (tolower(*buf++)); /* hash * 33 + c */
    return hash;
}


/* A fingerprintis a 64 bit number that represents the state of the dictionary 
 * at a given time, it's just a few dictproperties xored together. 
 * When an unsafe iterator is initialized, weget the dict fingerprint, and check 
 * the fingerprint again when the iterator isreleased. 
 * If the two fingerprints are different itmeans that the user of the iterator 
 * performed forbidden operations against thedictionary while iterating. */  
long longdictFingerprint(dict *d) {  
    long long integers[6], hash = 0;  
    int j;  
    //將dict結(jié)構體中的幾個狀態(tài)放入到數(shù)組中损痰,以便后面應用到64 bit MixFunctions中。  
    //dict結(jié)構體其實就是一個hash表的實現(xiàn)酒来,而這些狀態(tài)其實就是第一卢未、第二哈希表的表地址、表大小與  
    //已用條目的數(shù)量  
    integers[0] = (long) d->ht[0].table;  
    integers[1] = d->ht[0].size;  
    integers[2] = d->ht[0].used;  
    integers[3] = (long) d->ht[1].table;  
    integers[4] = d->ht[1].size;  
    integers[5] = d->ht[1].used;  
   
    /* We hash N integers by summing everysuccessive integer with the integer 
     * hashing of the previous sum. Basically: 
     * 
     * Result =hash(hash(hash(int1)+int2)+int3) ... 
     * 
     * This way the same set of integers in adifferent order will (likely) hash 
     * to a different number. */  
    //利用64 bit Mix Functions堰汉,將這些狀態(tài)信息混合到hash中辽社,組成最后的指紋,如果這些狀態(tài)中有一個  
    //出現(xiàn)變化翘鸭,可以通過一個算法逆推出該狀態(tài)變化之前的值滴铅。例如,d->ht[0].size發(fā)生變化就乓,則我們可  
    //以通過hash和其他的幾個狀態(tài)汉匙,逆推出d->ht[0].size的最初值。  
    for (j = 0; j < 6; j++) {  
        hash += integers[j];  
        /* For the hashing step we use TomasWang's 64 bit integer hash. */  
        hash = (~hash) + (hash << 21); //hash = (hash << 21) - hash - 1;  
        hash = hash ^ (hash >> 24);  
        hash = (hash + (hash << 3)) +(hash << 8); // hash * 265  
        hash = hash ^ (hash >> 14);  
        hash = (hash + (hash << 2)) +(hash << 4); // hash * 21  
        hash = hash ^ (hash >> 28);  
        hash = hash + (hash << 31);  
    }  
    return hash;  
} 

哈希沖突解決
redis使用的是鏈地址法生蚁, 每個哈希表結(jié)點有一個next指針盹兢;

哈希表調(diào)整
哈希表調(diào)整擴展觸發(fā)條件:

  1. 當服務器目前沒有在執(zhí)行BGSAVE命令或者BGREWRITEAOF命令, 并且哈希表的負載因子大于等于1守伸。
  2. 服務器目前正在執(zhí)行BGSAVE命令或者BFREWRITEAOF命令绎秒, 并且哈希表的負載因子大于等于5。
    哈希表調(diào)整規(guī)則由_dictNextPower函數(shù)確定, 規(guī)則如下:
  3. 如果執(zhí)行的是擴展操作尼摹, 那么ht[1]的大小為第一個大于等于ht[0].used*2的2^n见芹;
  4. 如果執(zhí)行的是收縮操作,那么ht[1]的大小為第一個大于等于ht[0].used的2^n;
    下面是遷移函數(shù)的實現(xiàn)
int dictRehash(dict *d, int n) {
    // 這里的n代表一共要遷移多少個dictEntry

    // 只可以在 rehash 進行中時執(zhí)行
    if (!dictIsRehashing(d)) return 0;

    // 進行 N 步遷移
    // T = O(N)
    while (n--) {  
        dictEntry *de, *nextde;

        // 如果 0 號哈希表為空蠢涝,那么表示 rehash 執(zhí)行完畢
        // T = O(1)
        if (d->ht[0].used == 0) {
            // 釋放 0 號哈希表
            zfree(d->ht[0].table);
            // 將原來的 1 號哈希表設置為新的 0 號哈希表
            d->ht[0] = d->ht[1];
            // 重置舊的 1 號哈希表
            _dictReset(&d->ht[1]);
            // 關閉 rehash 標識
            d->rehashidx = -1;
            // 返回 0 玄呛,向調(diào)用者表示 rehash 已經(jīng)完成
            return 0;
        }

        // Note that rehashidx can't overflow as we are sure there are more
        // elements because ht[0].used != 0
        // 確保 rehashidx 沒有越界
        assert(d->ht[0].size > (unsigned)d->rehashidx);

        // 略過數(shù)組中為空的索引,找到下一個非空索引
        while (d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++; //dictEntry偏移一位

        // 指向該索引的鏈表表頭節(jié)點
        de = d->ht[0].table[d->rehashidx];
        // 將鏈表中的所有節(jié)點遷移到新哈希表
        // T = O(1)
        while (de) {
            unsigned int h;

            // 保存下個節(jié)點的指針
            nextde = de->next;

            // 計算新哈希表的哈希值和二,以及節(jié)點插入的索引位置
            h = dictHashKey(d, de->key) & d->ht[1].sizemask;

            // 插入節(jié)點到新哈希表,而且是插入到表頭
            de->next = d->ht[1].table[h];
            d->ht[1].table[h] = de;

            // 更新計數(shù)器
            d->ht[0].used--;
            d->ht[1].used++;

            // 繼續(xù)處理下個節(jié)點
            de = nextde;
        }
        // 將剛遷移完的哈希表索引的指針設為空
        d->ht[0].table[d->rehashidx] = NULL;
        // 更新 rehash 索引
        d->rehashidx++;
    }

    return 1;
}

redis還有一個漸進式rehash方法徘铝, 主要解決鍵值對數(shù)量太多遷移時間太長的問題。


壓縮列表


整數(shù)集合


最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末惯吕,一起剝皮案震驚了整個濱河市惕它,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌废登,老刑警劉巖淹魄,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異堡距,居然都是意外死亡甲锡,警方通過查閱死者的電腦和手機兆蕉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缤沦,“玉大人虎韵,你說我怎么就攤上這事「追希” “怎么了劝术?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長呆奕。 經(jīng)常有香客問我,道長衬吆,這世上最難降的妖魔是什么梁钾? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮逊抡,結(jié)果婚禮上姆泻,老公的妹妹穿的比我還像新娘。我一直安慰自己冒嫡,他們只是感情好拇勃,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著孝凌,像睡著了一般方咆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蟀架,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天瓣赂,我揣著相機與錄音,去河邊找鬼片拍。 笑死煌集,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的捌省。 我是一名探鬼主播苫纤,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼纲缓!你這毒婦竟也來了卷拘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤祝高,失蹤者是張志新(化名)和其女友劉穎恭金,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體褂策,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡横腿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年颓屑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耿焊。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡揪惦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出罗侯,到底是詐尸還是另有隱情器腋,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布钩杰,位于F島的核電站纫塌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏讲弄。R本人自食惡果不足惜措左,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望避除。 院中可真熱鬧怎披,春花似錦、人聲如沸瓶摆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽群井。三九已至状飞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間书斜,已是汗流浹背昔瞧。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留菩佑,地道東北人自晰。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像稍坯,于是被迫代替她去往敵國和親酬荞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容