最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

redis 存儲結構原理 2

2023-08-19 20:33 作者:阿兵云原生  | 我要投稿

我正在參與掘金創(chuàng)作者訓練營第4期,點擊了解活動詳情,一起學習吧!

咱們接著上一部分來進行分享,我們可以在如下地址下載 redis 的源碼

https://redis.io/download

此處我下載的是 redis-6.2.5 版本的,xdm 可以直接下載上圖中的 **redis-6.2.6 **版本,

redis 中 hash 表的數(shù)據(jù)結構

redis hash 表的數(shù)據(jù)結構定義在:

redis-6.2.5\src\dict.h

哈希表的結構,每一個字典都有兩個實現(xiàn)從舊表到新表的增量重哈希

typedef?struct?dictht?{
????dictEntry?**table;
????unsigned?long?size;
????unsigned?long?sizemask;
????unsigned?long?used;
}?dictht;

table:

table 是一個二級指針,對應這一個數(shù)組,數(shù)組中的每個元素都是指向了一個 dictEntry 結構體指針的,dictEntry 具體的數(shù)據(jù)結構是保存一個鍵值對

具體的 ?dictEntry ?數(shù)據(jù)結構是這樣的:

size:

size 屬性是記錄了整個 hash 表的大小,也可以理解為上述 table 數(shù)組的大小

sizemask:

sizemask 屬性,和具體的 hash 值來一起決定鍵要放在 table 數(shù)組的哪個位置

sizemask ?的值,總是會比 size 小 1 ,我們可以來演示一下

使用取余的方式,實際上是很低效的,咱們的計算機是不會做乘除法的,同樣都是用加減法來進行處理的,為了高效處理,我們可以使用二進制的方式

使用二進制的方式,就會用到該字段 sizemask ,主要是用來 和 具體的 hash 值做按位與操作

如圖就很明確了, size = 4,sizemask = 3,hash 值為 7, 最后 hash 值 & sizemask = 0011, 也就是 3,因此就會插入到上圖的具體位置

used:

used 屬性表示 hash 表里面已經(jīng)有鍵值對的數(shù)量

對于上述的案例,可以用一個簡圖來表示一下 hash 表結構 dictht

dictEntry 結構每個屬性的含義

typedef?struct?dictEntry?{
????void?*key;
????union?{
????????void?*val;
????????uint64_t?u64;
????????int64_t?s64;
????????double?d;
????}?v;
????struct?dictEntry?*next;
}?dictEntry;

上面我們看到數(shù)組中的節(jié)點信息,是 ?dictEntry 結構,屬性分別是這些意思:

  • key

    具體的 redis 鍵

  • union v

    • val

      指向不同類型的數(shù)據(jù),此處是 void * ,使用該類型,是為了節(jié)省內存

    • u64

      用于 redis 集群中的哨兵模式和選舉模式

    • s64

      記錄過期時間的

  • next

    指向下一個節(jié)點的指針

dict 結構

src\dict.h 文件中,咱們接著往下看,能夠看到一個非常關鍵的結構,就是 dict ,redis 中都是使用這個結構來進行組織的

typedef?struct?dict?{
????dictType?*type;
????void?*privdata;
????dictht?ht[2];
????long?rehashidx;?/*?rehashing?not?in?progress?if?rehashidx?==?-1?*/
????int16_t?pauserehash;?/*?If?>0?rehashing?is?paused?(<0?indicates?coding?error)?*/
}?dict;

  • type

字段對應的操作函數(shù),具體有哪些操作函數(shù),我們可以看到typedef struct dictType 給出的信息

  • privdata

字典依賴的數(shù)據(jù),例如 redis 具體的操作等等

  • ht[2]

hash 表的鍵值對,放在此處,一個舊的,一個新的

ht[0] :是擴容前的數(shù)組

ht[1]:是擴容后的數(shù)組

這個是當數(shù)據(jù)量大的時候,用于漸進式 rehash 的

  • rehashidx

來指定具體 rehash 的位置,對應到 ht[0] 的索引上,rehashidx == -1 ,就是沒有進行再 hash , rehashidx != -1 ?時,說明正在進行再 hash

還記得我們之前說到 redis 有 16 個 db 嗎?

我們在 redis 源碼中 src\server.h 也能夠看到 redisdb 的數(shù)據(jù)結構

我們可以看到 dict 這個字典,是 redis 中使用是相當頻繁和關鍵的

上面有說到 ?ht[2] 會用在漸進式 rehash 上,那么為什么要用漸進式 rehash 以及他是如何做的?

擴容的時候,會觸發(fā) rehash

當數(shù)據(jù)量很大的時候,會涉及到擴容,若一次性從 ht[0] 拷貝到 ht[1] 是比較慢的,會阻塞其他操作,那么就沒有辦法處理其他請求了,因為 redis 是單線程處理任務的

ht[0] 數(shù)據(jù)拷貝到 ht[1] ?的方式一

是這樣進行 rehash 的

擴容的時候,rehash 是這樣做的:

  • 先會對上述說到的 ht[1] 開辟內存空間,會將 ht[0].size * 2 給到 ht[1]

  • 然后再將 ht[0] 的數(shù)據(jù),從 ht[0][0] ... ht[0][size-1] 將數(shù)據(jù)拷貝到 ht[1] 里面

如何做到漸進式呢?

使用分而治之的思想,無論 redis 目前是否在做持久化的時候,當我們每次操作 redis 增刪改查,就會進行邊枚舉邊篩查的方式,逐步的將 ht[0][0] ... ht[0][size-1] rehash 到 ht[1] 中

可以追一下代碼流程 , 我們從 src\server.c 注冊 setCommand 命令開始追起,代碼設計關鍵流程如下

當追到 dictAddRaw 函數(shù)的時候,我們可以清晰的看出來,當 redis 加入數(shù)據(jù)的時候,使用的是頭插法

  • 先對新的節(jié)點開辟相應的內存

  • 將新建節(jié)點的 next 對象指向鏈表的頭

  • 然后將鏈表的頭指向新建的節(jié)點地址,即完成了一次 頭插

此處我們可以看到,實際上是做了一次 rehash

追到 dictRehash 函數(shù)的時候,可以看到此處的再 hash 函數(shù) dictRehash,我們可以看到 rehash 的做法是:

  • 在 ht[0] 數(shù)組中,取得 rehashidx 對應的桶,或者腳數(shù)組對應的索引位置

  • 通過上述找到的索引位置,取 ht[0].table[d->rehashidx] 對應的鏈表

  • 然后將鏈表中的數(shù)據(jù)依次進行 rehash

此處 dictRehash ?的 ?n 的參數(shù),表示再 hash 的次數(shù),再 hash ?1 次,表示對于數(shù)組的這個桶對應的鏈表上的所有數(shù)據(jù),進行一輪 hash

可以看到代碼中

?/*?Get?the?index?in?the?new?hash?table?*/
??h?=?dictHashKey(d,?de->key)?&?d->ht[1].sizemask;

此處正是 dictHashKey 計算出一個整數(shù),然后和我們 dictht 中的 sizemask 進行一次按位與操作 , 旨在得到一個新的 hash 表索引位置

redis 調用 ?_dictRehashStep 的位置

通過查看代碼中調用 _dictRehashStep 函數(shù)的位置并不多,我們一次查看調用關系,我們會知道確實是當我們每次操作 redis 增刪改查的時候,會發(fā)生漸進式的 rehash , 這些是在我們進行擴容之后,如何將 ht[0] 的數(shù)據(jù)拷貝到 ht[1] 的實現(xiàn)方式

實際 redis 中涉及到如上幾個函數(shù) 都會調用 _dictRehashStep:

  • dictAddRaw

  • dictGenericDelete

  • dictFind

  • dictGetRandomKey

  • dictGetSomeKeys

ht[0] 數(shù)據(jù)拷貝到 ht[1] ?的方式二

定時器調用 dictRehash的邏輯

當 redis 中沒有持久化操作的時候,redis 中的定時操作就會就會走定時的邏輯,邏輯是這樣的

我們可以在 redis 源碼中搜索使用 dictRehash 函數(shù)的位置

使用的位置也并不多,我們很容易就能找到按照毫秒級別來定時操作的位置

dictRehashMilliseconds

此處的邏輯是,while 循環(huán)是以 100 次 rehash 為一輪,時間限制是 1ms,只要時間不超過 1ms,能做的 rehash 次數(shù)至少是 100 次(每一輪 100 次),若超過 1 ms,則會立刻結束本次定時操作

此處我們可以看到,dictRehash(d,100) 傳遞的參數(shù)是 100,表示 rehash 100 次,還記得之前的漸進式 rehash 是 傳入的 1 次 嗎,可以往上看看文章內容

今天就到這里,學習所得,若有偏差,還請斧正

歡迎點贊,關注,收藏

朋友們,你的支持和鼓勵,是我堅持分享,提高質量的動力

好了,本次就到這里

技術是開放的,我們的心態(tài),更應是開放的。擁抱變化,向陽而生,努力向前行。

我是阿兵云原生,歡迎點贊關注收藏,下次見~


redis 存儲結構原理 2的評論 (共 條)

分享到微博請遵守國家法律
英超| 惠水县| 临高县| 邢台市| 子长县| 雅江县| 炎陵县| 兰溪市| 汕头市| 永顺县| 子洲县| 留坝县| 松溪县| 定陶县| 科技| 顺义区| 云南省| 新干县| 定南县| 聊城市| 土默特左旗| 洪湖市| 田林县| 通榆县| 如东县| 曲松县| 翁源县| 芒康县| 乡宁县| 安康市| 祁阳县| 花莲市| 嘉义县| 应城市| 桐乡市| 宾阳县| 旬邑县| 台中市| 基隆市| 潞城市| 许昌市|