redis原理篇--Dict

Dict數據結構

一、Redis字典的核心組件

Redis字典由三部分構成:

  1. dictht(哈希表):存儲桶數組與元數據
  2. dictEntry(哈希節點):存儲鍵值對
  3. dict(字典主體):包含雙哈希表(用于漸進式重哈希)和類型函數

二、哈希表結構 dictht(Dict HashTable)

typedef struct dictht {dictEntry **table;        // 桶數組指針unsigned long size;       // 哈希表總槽位數(桶大小)unsigned long sizemask;   // 掩碼(size-1)unsigned long used;       // entry的數量
} dictht;
字段詳解:
  1. dictEntry **table

    • 核心存儲結構:指向指針數組(entry數組)的指針
    • 數組元素為dictEntry*類型(鏈表頭指針)
  2. size

    • 哈希表的大小(總是2^n)
    • 通過_dictNextPower函數動態擴容(如4→8→16)
  3. sizemask

    • 核心優化設計:值為size-1
    • 作用:替代取模運算?index = hash & sizemask
  4. used

    • 當前有效節點數量(非空桶計數)
    • 縮容條件:used/size < 0.1

這里的 used即標識entry數量的字段可能比哈希表大小字段更大,因為可能計算出同樣的hash值,所以value就放在同一個位置


三、哈希節點 dictEntry

typedef struct dictEntry {void *key;          // 鍵指針union {             // 聯合值域(節約內存)void *val;      // 通用值指針uint64_t u64;   // 無符號64位整數int64_t s64;    // 有符號64位整數double d;       // 雙精度浮點} v;struct dictEntry *next;  // 沖突鏈表指針
} dictEntry;
字段詳解:
  1. void *key

    • 鍵的指針(支持任意數據類型)
    • 實際存儲類型取決于Redis對象系統(SDS/INET等)
    • 通過dictType中的哈希函數計算索引
  2. 值聯合體?v

    • 創新設計:共用內存空間(每次只用一種類型)
    • val:通用對象指針(如RedisObject)
    • u64/s64:直接存儲整數(避免內存碎片)
    • d:直接存儲浮點數(如ZSET的score)
    • 典型內存優化:8字節空間覆蓋所有類型
  3. struct dictEntry *next

    • 沖突解決方案:單向鏈表
    • 頭插法添加新節點(O(1)復雜度)
    • 鏈表長度>64觸發樹化(在rehash時轉換)

Dict添加鍵值對過程

步驟1:計算哈希值與索引
// 示例:添加鍵值對 k1:v1
hash = dict->type->hashFunction(k1);  // 計算鍵k1的SipHash值
index = hash & dict->ht[0].sizemask;  // 圖中sizemask=3

步驟2:解決哈希沖突
  1. 創建新entry
    entry = zmalloc(sizeof(dictEntry));
    entry->key = k1;
    entry->v.val = v1;
    
  2. 頭插法鏈入
    entry->next = dict->ht[0].table[index]; 
    // 原table[1]指向k2節點,新entry指向k2
    

步驟3:更新哈希表狀態
dict->ht[0].table[index] = entry;  // 桶指針指向新插入的entry
dict->ht[0].used++;                // used計數+1(used從1→2)

Dict數據結構中,默認使用Dict中的第一個哈希表作為數據的存儲,圖片中

  • ht[0]被初始化為一個dictht結構體(table數組大小為4,存儲兩個鍵值對k1:v1k2:v2)。
  • ht[1]null,所有字段(tablesize等)為空或0,表明當前未進行rehash。

Dict的擴容

每次擴容時都是擴容都是擴容到2**n,并且是第一個大于等于used+1的2**n
條件一:LoadFactor ≥ 1 且無后臺進程運行
if (d->ht[0].used >= d->ht[0].size && dict_can_resize)  // 等效于"LoadFactor≥1"
  • 設計目的
    平衡內存效率和性能:當平均每個桶至少存儲一個元素時(LoadFactor=1),沖突概率顯著增加。此時擴容可降低沖突率,但需考慮系統負載。

  • 后臺進程限制原因
    保護持久化操作

    1. 執行BGSAVEBGREWRITEAOF時,Redis使用寫時復制(Copy-on-Write)?機制
    2. 若此時擴容(分配新哈希表),會觸發大量內存頁復制
    3. 可能耗盡內存或造成性能抖動(圖示代碼注釋明確提到此保護邏輯)

條件二:LoadFactor > 5
// dict_force_resize_ratio 在源碼中定義為5
if (d->ht[0].used/d->ht[0].size > dict_force_resize_ratio)  
  • 設計目的
    緊急避險機制
    當哈希沖突極度嚴重時(平均每個桶鏈長超過5),即使有后臺進程運行也強制擴容,避免查詢性能雪崩(鏈表遍歷從O(1)退化為O(n))。

  • 臨界值選擇依據

    1. 經驗值:桶鏈超過5個節點,查找效率明顯下降(實測性能衰減曲線)
    2. 內存容忍上限:5倍空間浪費是可承受極限(例:16個桶存80個元素)

Dict的收縮

收縮大小為第一個大于等于used的2**n,此時的size遠比used大,之所以是大于等于要保證實際

當刪除元素后,Redis 會調用?htNeedsResize(dict)?檢查是否滿足收縮條件:

size = dictSlots(dict);   // 哈希表總槽位數
used = dictSize(dict);    // 已使用槽位數(鍵值對數量)
if (size > DICT_HT_INITIAL_SIZE && (used * 100 / size < HASHTABLE_MIN_FILL)) {  // 默認HASHTABLE_MIN_FILL=10return 1;  // 需要收縮
}

條件解讀

  1. 哈希表大小超過初始值DICT_HT_INITIAL_SIZE,默認為4)
  2. 負載因子 < 10%(即已用槽位數不足總槽位的10%)
  3. 特殊保護:若?used < 4,強制按4計算,避免過度收縮

?收縮執行流程

1.?調用入口

刪除元素后觸發檢查:

// t_hash.c
if (dictDelete(dict, field) == C_OK) {if (htNeedsResize(o->ptr)) dictResize(o->ptr);  // 滿足條件則執行收縮
}
2.?計算目標大小
// dictResize() 邏輯
minimal = dict->ht[0].used;  // 當前實際元素數量
if (minimal < DICT_HT_INITIAL_SIZE)minimal = DICT_HT_INITIAL_SIZE;  // 最小收縮到初始大小
return dictExpand(dict, minimal);

可以看到這里不論是收縮還是擴容最后都調用了dictExpand方法 下面是該源碼

int dictExpand(dict *d, unsigned long size) {return _dictExpand(d, size, NULL);
}/* * 擴展或收縮字典的哈希表* * @param d: 目標字典指針* @param size: 期望的新哈希表大小* @param malloc_failed: 內存分配失敗標志(輸出參數)* @return: 操作狀態(DICT_OK 或 DICT_ERR)*/
int _dictExpand(dict *d, unsigned long size, int* malloc_failed) {// 初始化內存分配失敗標志if (malloc_failed) *malloc_failed = 0;/* * 有效性檢查:* 1. 如果字典正在 rehash 中,禁止擴展* 2. 如果當前元素數量大于目標大小,禁止收縮,這里的size就是目標擴容大小*/if (dictIsRehashing(d) || d->ht[0].used > size)return DICT_ERR;dictht n; /* 新哈希表 */// 計算新的size(調整為大于等于size的最小2的冪)unsigned long realsize = _dictNextPower(size);/* 如果新size與當前size相同,無需操作 */if (realsize == d->ht[0].size) return DICT_ERR;/* 分配新哈希表并初始化所有指針為NULL */n.size = realsize;n.sizemask = realsize-1;  // 掩碼用于快速計算索引/* * 內存分配策略:* - 如果允許內存分配失敗(malloc_failed != NULL),使用嘗試分配* - 否則使用強制分配(失敗會panic)*/if (malloc_failed) {n.table = ztrycalloc(realsize*sizeof(dictEntry*));*malloc_failed = n.table == NULL;  // 設置分配失敗標志if (*malloc_failed)return DICT_ERR;} else {n.table = zcalloc(realsize*sizeof(dictEntry*));}n.used = 0;  // 初始化已用槽位數為0/* * 首次初始化處理(非rehash場景):* 當ht[0]為空時,直接使用新哈希表作為主表*/if (d->ht[0].table == NULL) {d->ht[0] = n;return DICT_OK;}/* * 準備漸進式rehash:* 1. 將新哈希表放入ht* 2. 設置rehashidx=0標記開始rehash* (圖片中dictResize()最終會調用此邏輯)*/d->ht[1] = n;d->rehashidx = 0;  // 從索引0開始遷移數據return DICT_OK;
}

如圖會更具當前的used來去判斷此時的dictEntry的大小 并且為二的n次方,遷移的時候會將dictEntry從原本的地方一個一個遷移過來,全部遷移完過后ht[0]的table指針會指向新的dictEntry,ht[1]指向null

但是如果說一次性完成的話?如果Dict中包含的數據太多了,可能會導致主線程阻塞 因此DIct的rehash是多次完成的,如果是查詢和修改那么ht[0]的dictEntry和ht[1]的dictEntry都需要去查詢

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

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

相關文章

靜態路由主備切換

在網絡中&#xff0c;靜態路由的主備切換是實現網絡冗余的基礎方案之一&#xff0c;通過配置不同優先級的靜態路由&#xff0c;確保主用路徑故障時&#xff0c;流量能自動切換到備用路徑&#xff0c;提升網絡可靠性。以下從知識講解和實驗配置兩部分詳細說明。一、靜態路由主備…

PDF處理控件Aspose.PDF教程:在C#、Java、Python中快速縮小PDF

如果您的PDF太大&#xff0c;無法通過電子郵件發送&#xff0c;或者在線加載時間過長&#xff0c;您可以在幾秒鐘內縮小 PDF 大小。本教程介紹了借助Aspose.PDF使用 C#、Java 和 Python 編程快速縮小PDF的方法。 Aspose.PDF官方試用版下載 通過編程縮小 PDF 尺寸 如果您需要…

AWS EKS 常用命令大全:從基礎管理到高級運維

前言 Amazon Elastic Kubernetes Service (EKS) 是 AWS 提供的托管 Kubernetes 服務,大大簡化了 K8s 集群的部署和管理工作。作為 EKS 管理員或開發者,熟練掌握 kubectl 命令是日常工作的基礎。本文將詳細介紹 EKS 環境中常用的 kubectl 命令,涵蓋集群管理、工作負載操作、…

GitHub Browser-Use 的部署失敗記錄:失敗了,失敗了。。。。

一、項目背景與核心作用 browser-use 是一個開源的瀏覽器自動化工具&#xff0c;通過集成 AI 智能體&#xff08;如 GPT、Claude、DeepSeek 等大型語言模型&#xff09;&#xff0c;實現用自然語言控制瀏覽器操作。其核心目標是 簡化網頁交互自動化&#xff0c;尤其適合復雜、…

調用springboot接口返回403,問題定位及總結

背景在一次與前端聯調后端接口時前端返回接口返回狀態碼是403&#xff0c;前端返回說已經帶了請求token。排查 查看后端控制臺沒有出現任何錯誤信息。自己postman手動調用接口&#xff0c;發現接口正常。仔細核對前端調用接口與postman請求的區別&#xff0c;沒有發現任何問題。…

布隆過濾器原理分析、應用場景、與redis使用案例

一、核心結構與工作原理1.1 數據結構布隆過濾器由以下兩部分組成&#xff1a;位數組&#xff08;Bit Array&#xff09;&#xff1a;一個長度為 m 的二進制數組&#xff0c;初始所有位為0。哈希函數組&#xff1a;k 個獨立的哈希函數&#xff0c;每個函數將輸入元素映射到位數組…

異步并發×編譯性能:Dart爬蟲的實戰突圍

Dart憑借其高效的異步并發模型、AOT編譯性能和現代化的語法&#xff0c;正成為爬蟲開發中值得關注的新選擇。特別是對于Flutter應用開發者而言&#xff0c;Dart提供了一種"全棧同語言"的獨特優勢。 本文我將通過實戰代碼展示如何利用Dart的核心優勢——包括基于Futur…

Day 8: 深度學習綜合實戰與進階技術 - 從優化到部署的完整流程

Day 8: 深度學習綜合實戰與進階技術 - 從優化到部署的完整流程 ?? 學習目標: 掌握深度學習模型優化、調試、遷移學習等工業級技能,能夠構建高性能的深度學習應用 ?? 核心概念概覽 核心概念解釋: 模型優化: 通過正則化、學習率調度等技術提升模型性能和泛化能力 為什么需…

特征工程--機器學習

1、特征工程1.1 概念特征工程&#xff08;Feature Engineering&#xff09;是機器學習項目中非常關鍵的一步&#xff0c;它是指通過領域知識來選擇、創建或修改能夠使機器學習模型更好地工作的特征&#xff08;即輸入變量&#xff09;。特征工程的目標是提高模型的性能&#xf…

支持任意 MCP 協議的客戶端

支持任意 MCP 協議的客戶端&#xff08;如&#xff1a;Cursor、Claude、Cline&#xff09;可方便使用高德地圖 MCP server。目前支持Streamable HTTP, SSE 和 Node.js I/O 三種接入方式(推薦用戶使用Streamable HTTP)。 快速接入-MCP Server|高德地圖API

【線性代數】目錄

【線性代數】線性方程組與矩陣——&#xff08;1&#xff09;線性方程組與矩陣初步【線性代數】線性方程組與矩陣——行列式【線性代數】線性方程組與矩陣——&#xff08;2&#xff09;矩陣與線性方程組的解【線性代數】線性方程組與矩陣——&#xff08;3&#xff09;線性方程…

豆包新模型+PromptPilot:AI應用開發全流程實戰指南

> 當深度推理的豆包大模型遇上智能提示詞引擎,傳統AI開發中**70%的調試時間被壓縮至幾分鐘**,一場從“手工調參”到“智能優化”的開發范式革命正在發生。 ## 一、技術架構解析:雙引擎驅動智能進化 ### 1.1 豆包新模型的技術突破 2025年火山引擎推出的**豆包1.6系列模型…

Day13 Vue工程化

1.介紹&環境準備 npm兩項全局配置2.項目介紹&開發流程 npm create vue3.3.4 / install / run dev3.API風格 setup ref() onMounted()兩種風格選項式API寫法轉為組合式API寫法在根組件App.vue中引用寫好的xxx.vue4.案例1.引入組件2.完整代碼<script></script&g…

Linux中配置DNS

Linux中配置DNS服務 一、什么是DNS DNS (Domain Name System) 是域名服務 &#xff0c;它是由解析器和域名服務器組成的。 域名服務器是指保存有該網絡中所有主機的域名和對應IP地址&#xff0c; 并具有將域名轉換為IP地址功能的服務器。&#xff08;將網址解析成IP&#xff…

Redis應?-緩存與分布式鎖

&#x1f308; 個人主頁&#xff1a;Zfox_ &#x1f525; 系列專欄&#xff1a;Redis &#x1f525; 什么是緩存 緩存(cache)是計算機中的?個經典的概念.在很多場景中都會涉及到 核?思路就是把?些常?的數據放到觸?可及 (訪問速度更快) 的地?,?便隨時讀取 對于計算機…

TCP、HTTP/HTTPS、FTP 解析 + 面試回答參考

TCP、HTTP/HTTPS、FTP 解析 面試回答參考 在后端開發、網絡編程以及運維面試中&#xff0c;TCP 協議、HTTP/HTTPS、FTP 是高頻考點。本文將從原理、流程、面試常問問題出發&#xff0c;幫你一次性搞懂這些核心知識點。一、TCP 三次握手 1. 作用 建立可靠連接&#xff0c;確保雙…

ATF(TF-A)安全通告 TFV-13(CVE-2024-7881)

安全之安全(security)博客目錄導讀 ATF(TF-A)安全通告匯總 目錄 一、漏洞描述 二、緩解措施與建議 三、補丁修改 關于該漏洞的具體細節,可參考【CVE-2024-7881】ARM CPU漏洞安全通告】 Title 非特權上下文可以觸發數據相關的預取引擎,從而獲取特權位置的內容,并將這些…

Pytorch深度學習框架實戰教程-番外篇02-Pytorch池化層概念定義、工作原理和作用

相關文章 視頻教程 《Pytorch深度學習框架實戰教程01》《視頻教程》 《Pytorch深度學習框架實戰教程02&#xff1a;開發環境部署》《視頻教程》 《Pytorch深度學習框架實戰教程03&#xff1a;Tensor 的創建、屬性、操作與轉換詳解》《視頻教程》 《Pytorch深度學習框架實戰…

常見通信協議詳解:TCP、UDP、HTTP/HTTPS、WebSocket 與 GRPC

常見通信協議詳解&#xff1a;TCP、UDP、HTTP/HTTPS、WebSocket 與 RPC 在現代網絡通信中&#xff0c;各種協議扮演著至關重要的角色&#xff0c;它們決定了數據如何在網絡中傳輸、控制其可靠性、實時性與適用場景。對于開發者而言&#xff0c;理解這些常見的通信協議&#xff…

部署一個自己的音樂播放器教程

以下以部署 YesPlayMusic 為例&#xff0c;介紹兩種常見的部署方法&#xff0c;一種是通過 Node.js 和 Git 在 Windows 系統上部署&#xff0c;另一種是通過 Docker 在 Linux 系統上部署。具體步驟如下&#xff1a;Windows 系統部署&#xff08;基于 Node.js 和 Git&#xff09…