6、Redis系統-數據結構-04-Hash

四、哈希表(Hashtable)

哈希表是一種高效的鍵值對數據結構,通過散列函數將鍵映射到表中的位置,實現快速的插入、刪除和查找操作。Redis 廣泛使用哈希表來實現 Hash 對象和數據庫的鍵值存儲。以下將從結構設計、哈希沖突與鏈式哈希、rehash、漸進式哈希和哈希觸發條件等角度詳細介紹 Redis 中的哈希表。

1. 結構設計

在 Redis 中,哈希表(dict)由兩個哈希表數組(dictht)組成,用于實現漸進式 rehash,以應對數據量增大時的性能問題。每個哈希表數組包含一個哈希表節點(dictEntry)的指針數組。哈希表節點用于存儲實際的鍵值對,并通過鏈地址法處理哈希沖突。

哈希表結構
typedef?struct?dictht?{dictEntry **table;????// 哈希表數組unsigned?long?size;???// 哈希表大小unsigned?long?sizemask;??// 哈希表大小掩碼,用于計算索引值unsigned?long?used;???// 哈希表中已使用的節點數量
} dictht;typedef?struct?dict?{dictType *type;???????// 類型特定函數void?*privdata;???????// 私有數據dictht ht[2];?????????// 哈希表,支持漸進式 rehashlong?rehashidx;???????// rehash 索引,-1 表示沒有進行 rehashunsigned?long?iterators;??// 當前正在運行的安全迭代器數量
} dict;typedef?struct?dictEntry?{void?*key;????????????// 鍵union?{void?*val;????????// 值uint64_t?u64;?????// 無符號整數值int64_t?s64;??????// 有符號整數值double?d;?????????// 雙精度浮點值} v;struct?dictEntry?*next;??// 指向下一個哈希表節點,解決沖突
} dictEntry;
主要字段介紹
  1. table:指向哈希表節點指針數組的指針。
  2. size:哈希表的大小,即哈希表數組的長度。
  3. sizemask:哈希表大小掩碼,用于計算鍵的索引值。
  4. used:哈希表中已使用的節點數量。
  5. rehashidx:rehash 的索引,表示當前進行到哪個位置,-1 表示沒有進行 rehash。
  6. type:指向哈希表類型特定函數的指針,用于實現不同類型的哈希表。
  7. privdata:指向私有數據的指針,可以用于存儲與哈希表相關的額外信息。

2. 哈希沖突及鏈式哈希
哈希沖突

哈希沖突是指不同的鍵通過哈希函數計算得到相同的索引值,從而映射到哈希表數組的同一位置。哈希沖突是哈希表的一種常見問題,需要有效的處理機制來解決。

鏈式哈希

鏈式哈希是一種解決哈希沖突的常用方法。它通過鏈表的形式,將所有哈希值相同的鍵值對連接在一起。每個哈希表數組的元素(dictEntry)都指向一個鏈表的頭節點,當發生哈希沖突時,新節點被插入到鏈表的頭部。

typedef?struct?dictEntry?{void?*key;????????????// 鍵union?{void?*val;????????// 值uint64_t?u64;?????// 無符號整數值int64_t?s64;??????// 有符號整數值double?d;?????????// 雙精度浮點值} v;struct?dictEntry?*next;??// 指向下一個哈希表節點,解決沖突
} dictEntry;
插入操作

在哈希表中插入鍵值對時,Redis 首先計算鍵的哈希值,并將其映射到哈希表數組中的一個位置。如果該位置已經有其他節點,則通過鏈地址法將新節點插入到鏈表的頭部。

int?dictAdd(dict *d,?void?*key,?void?*val)?{dictEntry *entry = dictAddRaw(d, key);if?(!entry)?return?DICT_ERR;dictSetVal(d, entry, val);return?DICT_OK;
}
查找操作

在哈希表中查找鍵對應的值時,Redis 通過計算鍵的哈希值來確定其在哈希表數組中的位置,然后遍歷鏈表查找對應的鍵值對。

dictEntry *dictFind(dict *d,?const?void?*key)?{if?(d->ht[0].size ==?0)?return?NULL;dictEntry *he = dictFindRaw(d, key);if?(he ==?NULL)?return?NULL;return?he;
}
刪除操作

在哈希表中刪除鍵值對時,Redis 同樣通過計算鍵的哈希值來確定其位置,然后遍歷鏈表找到對應的節點并將其刪除。

int?dictDelete(dict *d,?const?void?*key)?{if?(dictSize(d) ==?0)?return?DICT_ERR;if?(dictDeleteRaw(d, key) == DICT_OK) {if?(d->ht[0].used ==?0) _dictReset(&d->ht[0]);return?DICT_OK;}return?DICT_ERR;
}
3. rehash
rehash 的必要性

隨著哈希表中元素數量的增多,哈希表的負載因子(load factor)會不斷增大,從而影響性能。為了維持哈希表的性能,Redis 需要進行 rehash 操作,即重新分配哈希表的大小,并將原有的鍵值對重新分布到新的哈希表中。

rehash 過程
  1. 初始化新哈希表:當需要進行 rehash 時,首先為哈希表分配新的哈希表數組,并調整新哈希表的大小。
  2. 遷移節點:將舊哈希表的所有節點逐個遷移到新哈希表中。遷移過程中,計算每個節點的新哈希值,并將其插入到新哈希表的對應位置。
  3. 完成 rehash:當所有節點都遷移完成后,釋放舊哈希表的內存,將新哈希表替換為當前哈希表。
void?_dictRehashStep(dict *d) {if?(d->iterators ==?0) dictRehash(d,?1);
}
4. 漸進式 rehash
漸進式 rehash 的基本思想

漸進式 rehash 的基本思想是:將哈希表的擴容操作分攤到多次操作中完成,而不是一次性完成,從而避免一次性 rehash 帶來的性能問題。通過漸進式 rehash,Redis 可以在保證高效操作的同時,平滑地完成哈希表的擴容。

漸進式 rehash 的具體實現
  1. 分階段遷移:在每次對哈希表進行增刪查改操作時,逐步將舊哈希表的節點遷移到新哈希表中。每次操作遷移一部分節點,直到舊哈希表的所有節點都遷移完成。
  2. 維護 rehash 狀態:在進行 rehash 過程中,Redis 維護一個 rehash 索引(rehashidx),表示當前遷移到哪個位置。每次操作完成后,rehash 索引會相應更新,直到所有節點遷移完成。
  3. 完成 rehash:當所有節點遷移完成后,釋放舊哈希表的內存,將新哈希表替換為當前哈希表。
void?_dictRehashStep(dict *d) {if?(d->iterators ==?0) dictRehash(d,?1);
}
漸進式 rehash 的優點
  1. 平滑過渡:通過逐步遷移節點,避免了一次性 rehash 帶來的性能波動。
  2. 低延遲:每次操作只遷移一部分節點,保證了每次操作的時間復雜度不會過高,從而降低延遲。
5. 哈希觸發條件

哈希表的 rehash 觸發條件主要有兩個:

  1. 負載因子:當哈希表的負載因子超過一定閾值時,觸發 rehash 操作。負載因子等于已使用的節點數量除以哈希表的大小。Redis 的默認閾值是 1,當負載因子超過 1 時,會觸發 rehash。
  2. 內存使用情況:當 Redis 內存使用情況達到

一定水平時,也會觸發 rehash 操作,以保證內存的高效使用。

通過這兩個條件,Redis 可以在適當的時候進行哈希表的 rehash,從而維持哈希表的性能和內存利用率。

結論

通過上述解析,我們可以更好地理解哈希表的設計思想和實現原理,從而在實際開發中更好地利用哈希表提供的優勢。在 Redis 中,哈希表通過高效的鍵值對存儲和漸進式 rehash 機制,實現了高性能和低延遲的操作,適用于多種場景如鍵值存儲、緩存等。了解這些優化策略,可以幫助我們在實際應用中更好地利用 Redis 的性能和功能。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/42788.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/42788.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/42788.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

深入源碼,探究#、$號替換符的區別

在Mybatis的日常使用過程中以及在一些技術論壇上我們都能常常聽到,不要使用$符號來進行SQL的編寫,要使用#符號,否則會有SQL注入的風險。那么,為什么在使用$符號時會有注入的風險呢,以及#號為什么不會有風險呢&#xff…

C/C+++服務器之libuv的使用實戰

libuv libuv簡介 1: 開源跨平臺的異步IO庫, 主要功能有網絡異步,文件異步等。 2: libuv主頁: http://libuv.org/ 3: libuv是node.js的底層庫; 4: libuv的事件循環模型: epoll, kqueue, IOCP, event ports; 異步 TCP 與 UDP sockets; DNS 解析 異步文件讀寫; 信號處…

Python結合MobileNetV2:圖像識別分類系統實戰

一、目錄 算法模型介紹模型使用訓練模型評估項目擴展 二、算法模型介紹 圖像識別是計算機視覺領域的重要研究方向,它在人臉識別、物體檢測、圖像分類等領域有著廣泛的應用。隨著移動設備的普及和計算資源的限制,設計高效的圖像識別算法變得尤為重要。…

設計模式-結構型-08-組合模式

文章目錄 1、學校院系展示需求2、組合模式基本介紹3、組合模式示例3.1、 解決學校院系展示(透明模式1)3.2、高考的科目(透明模式2)3.3、高考的科目(安全組合模式) 4、JDK 源碼分析5、注意事項和細節 1、學校…

存儲過程編程-創建(CREATE PROCEDURE)、執行(EXEC)、刪除(DROP PROCEDURE)

一、定義 1、存儲過程是在SQL服務器上存儲的已經編譯過的SQL語句組。 2、存儲過程分為三類:系統提供的存儲過程、用戶定義的存儲過程和擴展存儲過程 (1)系統提供的存儲過程:在安裝SQL Server時,系統創建了很多系統存…

AI機器人在企業拓客上常見的功能有哪些

AI機器人具備多種功能,這些功能主要基于其被設計和訓練的目的。整理了一些常見的AI機器人功能: 1. 語音識別與自然語言處理: - 語音識別:將用戶的語音輸入轉換為文本,以便機器人可以理解和處理。 - 自然語言處理…

QCC5181 歌詞歌曲名多國語言顯示替代QCC5125 CSR8675

QCC518X作為Qualcomm新一代藍牙技術芯片,支持最新藍牙協議V5.4,較QCC512X系列,它有更強大的DSP、CPU。除支持USB、I2S、SPDIF等接口外,還擴展了LE Audio功能,擴展支持AptX Lossless。以5181為例,我們還擴展…

vscode語言模式

1.背景 寫vue3ts項目的時候,用到了volar插件,在單文件使用的時候,鼠標懸浮在代碼上面會有智能提示; 但是最近volar插件提示被棄用了,然后我按照它的官方提示,安裝了Vue-official擴展插件,但是…

Banana Pi BPI-M5 Pro 低調 SBC 采用 Rockchip RK3576 八核 Cortex-A72/A53 AIoT SoC

Banana Pi BPI-M5 Pro,也稱為 Armsom Sige5,是一款面向 AIoT 市場的低調單板計算機 (SBC),由 Rockchip RK3576 八核 Cortex-A72/A53 SoC 驅動,提供Rockchip RK3588和RK3399 SoC 之間的中檔產品。 該主板默認配備 16GB LPDDR4X 和…

如何大幅減少 Vue.js 中的包大小和加載時間,提升用戶體驗!

大家好,我是CodeQi! 一位熱衷于技術分享的碼仔。 你知道嗎,根據Google 的一項研究,如果網站加載時間超過 3 秒,53% 的移動用戶會離開該網站? 性能優化是一個經常討論的話題,但很多開發人員并不關心提高應用的速度。 在前端開發中,優化包大小和加載時間對于提升用戶體…

下一代 CLI 工具,使用Go語言用于構建令人驚嘆的網絡應用程序

大家好,今天給大家分享一個創新的命令行工具Gowebly CLI,它專注于使用Go語言來快速構建現代Web應用程序。 Gowebly CLI 是一款免費開源軟件,有助于在后端使用 Go、在前端使用 htmx 和 hyperscript 以及最流行的 CSS 框架輕松構建令人驚嘆的 W…

入門PHP就來我這(高級)15 ~ 圖書刪除功能

有膽量你就來跟著路老師卷起來! -- 純干貨,技術知識分享 路老師給大家分享PHP語言的知識了,旨在想讓大家入門PHP,并深入了解PHP語言。 今天給大家接著上篇文章實現圖書刪除功能,來實現刪除圖書信息記錄行的功能。 1 刪…

高顏值官網(3):家居用品網站12個,好的創意都在這里。

hello,大家好,我是大千UI工場,本文為大家帶來家居用品網站UI,供大家欣賞。

項目代碼優化(1)——下單邏輯

給一個電商開發的系統排查,發現漏洞很多。很多經驗不夠的開發者很容易忽視的邏輯錯誤陷阱。在給一個項目做二次開發時候,檢測到的相關經典案例。這里整理支付和產品相關的邏輯,方便后續查看。,這里進行一些簡單的邏輯漏洞梳理與修…

Ubuntu 22.04 LTS 上安裝 MySQL8.0.23(在線安裝)

目錄 在線安裝MySQL 步驟1:更新軟件包列表 步驟2:安裝MySQL服務器 步驟3:啟動MySQL服務 步驟4:檢查MySQL狀態 步驟5:修改密碼、權限 在線安裝MySQL 步驟1:更新軟件包列表 在進行任何軟件安裝之前&a…

p9函數(1)

int Add(int x,int y) { int z0; zxy; return z; } int main() { int a10; int b20; int sumAdd(a,b); printf("%d\n",sum); return 0; } 字符串求長度 int main() { char arr1[]"bit"; char arr2[20]"###…

移動UI: 什么特征會被認為是簡潔風格,用案例告訴你

什么是簡潔風格,恐怕一百個人有一百個是理解,本文通過理論分析案例的方式進行探討。 移動 UI 中的簡潔風格通常具有以下幾個特征: 1. 平面化設計: 簡潔風格的移動 UI 善于運用平面化設計,即去除過多的陰影、漸變和立…

水冷液冷負載系統的六種基本類型

您可以選擇六種基本類型的冷卻系統,以滿足負載的冷卻需求。每個人都有其優點和缺點。本文旨在識別不同類型的冷卻系統并確定它們的優缺點,以便您可以根據自己的需求做出明智的選擇。 液體冷卻系統有六種基本類型: 1.液對液 2.閉環干燥系統…

聚類標簽的藝術:SKlearn中的數據聚類標簽分配策略

聚類標簽的藝術:SKlearn中的數據聚類標簽分配策略 在機器學習領域,聚類是一種無監督學習方法,旨在將數據集中的樣本劃分為若干個簇,使得同一簇內的樣本相似度高,而不同簇之間的樣本相似度低。聚類標簽分配是聚類過程中…

深度講解 UUID/GUID 的結構、原理以及生成機制

目錄 一. 前言 二. 被廣泛使用 三. UUID 的結構 3.1. 必須了解的 3.2. 十六進制數字字符(hexDigit) 3.3. UUID 基本結構 3.4. 類型(變體)和保留位 3.5. 版本(子類型) 3.6. 時間戳 3.7. 時鐘序列 …