Redis 的字典:像智能文件柜一樣高效的哈希表實現

目錄

一、從傳統查找的痛點到哈希表的優勢?

二、哈希表的核心結構:文件柜的構成?

2.1、 dictht 結構體:文件柜本體?

2.2、dictEntry 結構體:帶鏈條的文件夾?

2.2.1、 哈希沖突的解決:抽屜里的鏈條?

2.3、字典的高層封裝:文件柜管理系統?

2.4、dictType:文件處理工具包?

三、哈希計算:如何確定文件位置?

四、動態擴容與收縮:文件柜的大小調整?

4.1、何時需要調整大小??

4.2、調整大小的規則?

4.3、漸進式 rehash:邊營業邊搬家?

4.4、寫時復制優化:拍照時不整理房間?

五、字典與 SDS 的完美配合?

六、字典設計的核心智慧?

如果說 SDS 是 Redis 的 "智能筆記本",鏈表是 "活頁文件夾",那么字典就是把這些筆記和文件夾有序管理起來的 "智能文件柜系統"。當你在 Redis 中使用哈希類型存儲用戶信息(如HSET user:1 name "張三" age 30)時,背后正是字典結構在高效運作。?

一、從傳統查找的痛點到哈希表的優勢?

想象你在圖書館找書的三種方式:?

  • 按書架順序查找:像數組遍歷一樣,逐個查看每個書架,效率極低?
  • 按分類目錄查找:像鏈表遍歷一樣,雖然有分類但仍需逐個翻閱?
  • 按編號定位查找:像哈希表一樣,通過書號直接計算出所在書架和位置,一步到位?

Redis 的字典采用哈希表作為底層實現,正是為了實現這種 "一步到位" 的高效查找能力。哈希表就像配備了智能索引系統的文件柜,能在 O (1) 時間復雜度內完成數據的查找、添加和刪除操作。?

二、哈希表的核心結構:文件柜的構成?

Redis 的哈希表結構由三個關鍵部分組成,就像文件柜的不同組件:?

2.1、 dictht 結構體:文件柜本體?

struct dictht {?dictEntry **table; // 抽屜數組(哈希表節點數組)?unsigned long size; // 抽屜總數(哈希表大小)?unsigned long sizemask; // 定位掩碼(總是等于size-1)?unsigned long used; // 已使用抽屜數(節點數量)?};?

這個結構體就像一個文件柜:?

  • table是一排抽屜,每個抽屜可以放多個文件夾(通過鏈表連接)?
  • size記錄總共有多少個抽屜?
  • sizemask是定位工具,用來計算文件應該放入哪個抽屜?
  • used記錄已經放了多少文件?

2.2、dictEntry 結構體:帶鏈條的文件夾?

struct dictEntry {?void *key; // 文件標簽(鍵)?union { // 文件內容(值)?void *val;?uint64_t u64;?int64_t s64;?} v;?struct dictEntry *next; // 下一個文件夾(解決沖突的鏈表)?};?

每個節點就像一個帶鏈條的文件夾:?

  • key是文件的標簽,在 Redis 中通常用 SDS 存儲(比如 "name"、"age")?
  • v是文件內容,可以是字符串指針(void *val)、無符號整數(u64)或有符號整數(s64)?
  • next是連接鏈條,當多個文件需要放入同一個抽屜時,用鏈條將它們串起來(哈希沖突解決)?

2.2.1、 哈希沖突的解決:抽屜里的鏈條?

當兩個不同的鍵計算出相同的抽屜編號時(哈希沖突),就像兩個文件要放進同一個抽屜,這時next指針就發揮作用了 —— 它把這些文件串成一個鏈表,這種方法稱為 "鏈地址法"。?

比如 "張三" 和 "張三豐" 計算出相同的抽屜編號,它們會通過next指針連接在一起,查找時只需遍歷這個小鏈表即可。?

2.3、字典的高層封裝:文件柜管理系統?

Redis 的字典結構在哈希表之上增加了管理層,就像文件柜的智能管理系統:?

struct dict {?dictType *type; // 操作工具集?void *privdata; // 工具參數?dictht ht[2]; // 兩個哈希表(主表和備用表)?int rehashidx; // 搬家進度(-1表示未進行)?};?

這個管理系統的核心功能:?

  • 維護兩個哈希表ht[0](日常使用)和ht[1](搬家時使用)?
  • 通過rehashidx記錄數據遷移進度?
  • 提供type中定義的工具函數處理不同類型的數據?

2.4、dictType:文件處理工具包?

struct dictType {?unsigned int (*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); // 銷毀標簽的工具?};?

這些函數就像文件柜的配套工具:?

  • hashFunction:根據文件名計算抽屜編號(哈希函數)?
  • keyDup:復制文件標簽(比如復制 SDS 字符串)?
  • keyCompare:比較兩個文件標簽是否相同?
  • 其他工具負責數據的復制和銷毀?

三、哈希計算:如何確定文件位置?

把文件放入文件柜需要兩步:計算哈希值(文件名編碼)和計算索引(抽屜編號):?

  1. 計算哈希值:用hashFunction對鍵(key)進行計算,比如對 SDS 字符串 "name" 計算得到一個大整數?
  1. 計算索引:用哈希值與sizemask(size-1)做與運算,得到抽屜編號:?

?索引 = 哈希值 & sizemask?

例如:當size=8(sizemask=7),哈希值 = 10(二進制 1010)時:?

1010 & 0111 = 0010(二進制)→ 索引=2?

這種計算方式要求size必須是 2 的冪次方,這樣sizemask的二進制表示全是 1,能保證索引均勻分布在0到size-1之間。?

四、動態擴容與收縮:文件柜的大小調整?

隨著文件增減,文件柜需要動態調整大小以保持效率,這個過程稱為 "rehash"(重新散列):?

4.1、何時需要調整大小??

  • 擴展條件:當文件太多導致擁擠時?
  • 無備份任務時,負載因子≥1(used/size ≥1)?
  • 有備份任務時,負載因子≥5(避免頻繁復制內存)?
  • 收縮條件:當文件太少導致空間浪費時?
  • 負載因子≤0.1(used/size ≤0.1)?

負載因子就像 "擁擠度":比如 8 個抽屜放了 10 個文件,擁擠度 = 10/8=1.25。?

4.2、調整大小的規則?

  • 擴展:ht[1]的大小是第一個大于等于ht[0].used*2的 2 的冪次方?
    • 例:used=10 → 10*2=20 → 下一個 2 的冪次方是 32?
  • 收縮:ht[1]的大小是第一個大于等于ht[0].used的 2 的冪次方?
    • 例:used=10 → 下一個 2 的冪次方是 16?

4.3、漸進式 rehash:邊營業邊搬家?

如果文件柜需要從 8 個抽屜擴到 32 個抽屜,不可能一次性搬完所有文件 —— 這會導致圖書館暫時停業。Redis 采用 "漸進式 rehash" 策略,就像邊營業邊搬家:?

  1. 準備階段:為ht[1]分配新空間,rehashidx=0(開始搬家)?
  1. 漸進遷移:每次有讀寫操作時,順帶遷移ht[0]中rehashidx索引處的所有文件到ht[1],然后rehashidx加 1?
  1. 雙表并行:遷移期間,查找操作會同時檢查ht[0]和ht[1];新增操作直接放入ht[1],保證ht[0]只減不增?
  1. 完成遷移:當ht[0]所有文件都遷移到ht[1]后,ht[1]成為新的ht[0],rehashidx=-1(搬家完成)?

這種策略把巨大的遷移工作分散到每次操作中,避免了服務中斷。?

4.4、寫時復制優化:拍照時不整理房間?

當 Redis 執行 BGSAVE(備份)或 BGREWRITEAOF(日志重寫)時,會創建子進程。為了減少內存復制開銷,Redis 采用 "寫時復制" 技術:?

  • 子進程創建時共享父進程內存,只有當數據修改時才復制相關內存頁?
  • 因此在備份期間,Redis 提高擴展閾值(負載因子≥5 才擴展),減少內存寫入操作?
  • 這就像拍照時不整理房間,等照片拍完再整理,避免重復勞動?

五、字典與 SDS 的完美配合?

SDS 在字典中扮演重要角色:?

  • 鍵(key)的存儲:字典的鍵幾乎都是用 SDS 存儲的,比如HSET user:1 name "張三"中的 "name"?

?key → SDS結構:len=4, free=4, buf=['n','a','m','e','\0']?

  • 字符串值的存儲:當值是字符串時,也用 SDS 存儲,比如 "張三"?

v.val → SDS結構:len=6, free=6, buf=['張','三','\0']?

  • 數值的優化存儲:當值是整數時,直接用u64或s64存儲,避免 SDS 的內存開銷?

這種組合讓字典既能高效存儲字符串,又能優化數值類型的存儲。?

六、字典設計的核心智慧?

特性?

生活比喻?

技術價值?

哈希表結構?

智能文件柜?

O (1) 時間復雜度的基本操作?

鏈地址法?

抽屜里的鏈條?

優雅解決哈希沖突?

雙哈希表?

新舊文件柜?

支持動態擴容 / 收縮?

漸進式 rehash?

邊營業邊搬家?

避免操作阻塞?

類型特定函數?

專用工具包?

支持多類型數據處理?

寫時復制適配?

拍照時不整理?

優化備份時的性能?

Redis 字典通過這些設計,完美平衡了性能、靈活性和內存效率。它不僅是哈希類型的底層實現,還被用于 Redis 數據庫的核心(鍵空間)、集群節點通信等關鍵場景,是 Redis 高效運作的基石之一。?

理解了字典結構,你就掌握了 Redis 數據管理的核心機制 —— 從簡單的鍵值對到復雜的哈希對象,都依賴這個精妙的 "智能文件柜系統" 來實現高效管理。

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

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

相關文章

FAST API部署和使用

第一部分:FastAPI 的使用(開發環境) 1. 安裝 首先,你需要安裝 FastAPI 和一個 ASGI 服務器,最常用的是 Uvicorn。 pip install "fastapi[standard]"這個命令會安裝 FastAPI 以及所有推薦的依賴,包…

【JavaWeb】之HTML(對HTML細節的一些總結)

大家天天開心! 文章目錄 前言一、HTML的簡介二、HTML運行方式三、html 的標簽/元素-說明四、表單注意事項總結 前言 首先我們在把Java基礎學習完之后,我們就要進行網站方面的開發了,我們要了解網頁的組成,而網頁的組成有HTML,CSS,…

互聯網醫院品牌IP的用戶體驗和生態構建

一、患者體驗與信任構建互聯網醫院品牌IP的價值核心在于獲得患者的深度信任,而卓越的用戶體驗是實現這一目標的關鍵路徑。在醫療服務同質化嚴重的當下,患者體驗已成為醫療機構差異化競爭的重要維度。研究表明,良好的用戶體驗能夠提高用戶滿意…

【Node.js教程】Express框架入門:從搭建到動態渲染商品列表

前言 Visual Studio Code(簡稱VSCode)是微軟開發的一款免費開源跨平臺代碼編輯器,憑借其免費、開源、跨平臺的特性,以及豐富的插件生態和美觀的界面,成為前端開發者的首選工具。 本文將帶你從零開始學習Express框架,包括搭建項目、配置路由、使用中間件以及實現動態渲染…

眾擎機器人開源代碼解讀

一,綜述 EngineAI ROS 包: 高層開發模式:用戶可通過發布身體速度指令,直接調用 EngineAI 機器人的行走控制器。底層開發模式:用戶可通過發布關節指令,自主開發專屬的控制器。 ROS2 package:全…

Windows系統安裝Git詳細教程

文章目錄步驟 1:下載 Git 安裝包步驟 2:運行安裝程序步驟 3:選擇安裝路徑步驟 4:選擇組件步驟 5:選擇默認編輯器步驟 6:選擇路徑環境變量步驟 7:選擇 HTTPS 協議的傳輸方式步驟 8:配…

leetcode 3446. 按對角線進行矩陣排序 中等

給你一個大小為 n x n 的整數方陣 grid。返回一個經過如下調整的矩陣:左下角三角形(包括中間對角線)的對角線按 非遞增順序 排序。右上角三角形 的對角線按 非遞減順序 排序。示例 1:輸入: grid [[1,7,3],[9,8,2],[4,…

攜程旅行 web 驗證碼 分析

聲明 本文章中所有內容僅供學習交流使用,不用于其他任何目的,抓包內容、敏感網址、數據接口等均已做脫敏處理,嚴禁用于商業用途和非法用途,否則由此產生的一切后果均與作者無關! 逆向分析 部分python代碼 result cp…

JavaEE 進階第一期:開啟前端入門之旅(上)

專欄:JavaEE 進階躍遷營 個人主頁:手握風云 一、HTML基礎 1.1. 什么是HTML HTML(Hyper Text Markup Language),超文本標記語言。 超文本:比文本要強大,通過鏈接和交互式方式來組織和呈現信息的文本形式。不僅僅有文本…

4.5 PBR

1.PBR簡介 2.高光工作流 3.金屬工作流1.PBR簡介 PBR(Physically Based Rendering, 基于物理的渲染)的工作流分為金屬工作流和高光工作流2.高光工作流 高光工作流是一種傳統的工作流, 現在用的相對較少, 但是在某些特定情況下能提供更精細的控制a.核心思想它不區分金屬和非金屬,…

09.《路由基礎知識解析和實踐》

09.路由基礎 文章目錄09.路由基礎核心概念路由關鍵組成部分三層轉發原理介紹(通信流程)路由類型及配置直連路由(direct)實驗示例**靜態路由(Static)****實驗示例****動態路由****RIP(routing information protocol---路…

websocket建立連接過程

1. 客戶端發送一個GET的http請求,請求頭要包含connection: upgradehost:localhost:8000。表明地址upgrade: websocket。指明升級的協議sec-websocket-key 。 安全驗證密鑰sec-websocket-version。 協議版本sec-websocket-accept 。對傳過來的key進行加密…

Simulink庫文件-一種低通濾波模塊搭建方法

在汽車電控系統應用層開發中,經常會用到低通濾波模塊,其主要作用是去除輸入信號中的高頻干擾,防止由于輸入信號的干擾引起后續執行系統的非預期頻繁波動。本文介紹簡要介紹低通濾波的定義及作用,并介紹一種低通濾波模塊simulink搭…

【C++游記】AVL樹

楓の個人主頁 你不能改變過去,但你可以改變未來 算法/C/數據結構/C Hello,這里是小楓。C語言與數據結構和算法初階兩個板塊都更新完畢,我們繼續來學習C的內容呀。C是接近底層有比較經典的語言,因此學習起來注定枯燥無味&#xf…

音視頻學習(六十二):H264中的SEI

什么是SEI? 在 H.264 視頻編碼標準中,補充增強信息(Supplemental Enhancement Information,SEI) 是一種特殊的 NAL(網絡抽象層)單元。它不像序列參數集(SPS)或圖像參數集&#xff0…

docker run 后報錯/bin/bash: /bin/bash: cannot execute binary file總結

以下方法來源于AI&#xff0c;個人僅驗證了第三條便成功執行 1. 鏡像與宿主機架構不匹配 比如&#xff1a; 你是 x86_64 的機器&#xff0c;但鏡像是 ARM64 的&#xff08;或反之&#xff09;。在 PC 上拉了樹莓派用的鏡像。查看鏡像架構 docker inspect <image_name> | …

【Redisson 加鎖源碼解析】

Redisson 源碼解析 —— 分布式鎖實現過程 在分布式系統中&#xff0c;分布式鎖 是非常常見的需求&#xff0c;用來保證多個節點之間的互斥操作。Redisson 是 Redis 的一個 Java 客戶端&#xff0c;它提供了對分布式鎖的良好封裝。本文將從源碼角度剖析 Redisson 的分布式鎖實現…

uni-app支持單多選、搜索、查詢、限制能否點擊組件

<template><view class="multi-select-container" :class="{ single-select: !multiple, no-search: !searchable }"><!-- 當組件被禁用時,直接顯示選中的內容 --><view class="disabled-display" v-if="disabled &a…

TFT屏幕:STM32硬件SPI+DMA+隊列自動傳輸

看了網上的很多的SPIDMA的代碼&#xff0c;感覺都有一些缺陷&#xff0c;就是基本都是需要有手動等待DMA完成的這個操作&#xff0c;我感覺這種等待操作在很大程度上浪費了時間&#xff0c;那么我加入的“隊列”就是一種將等待時間利用起來的方法。原本的SPIDMA的操作邏輯如下圖…

AI操作系統語言模型設計 之1 基于意識的Face-Gate-Window的共軛路徑的思維-認知-情感嵌套模型

摘要&#xff08;AI生成&#xff09;本文提出了一種創新的AI操作系統語言模型設計框架&#xff0c;將人類意識活動的分層結構映射到人工智能系統中。該模型包含三個嵌套層次&#xff1a;理性思維層&#xff08;Face層&#xff09;&#xff1a;采用雙面膠隱喻&#xff08;A/B面&…