Redis 八股

目錄

數據類型

字符串:

List:

HASH:

Set:

Zset:

BitMap:(這個及以下是后來新增的數據結構)

HyperLogLog:

GEO:

Stream:

主要數據結構

SDS

壓縮列表

HASH

整數結合

跳表

quickList

listpack?編輯

持久化

AOF(Append only FIle)

AOF寫回策略

AOF重寫

aof后臺重寫

rdb快照

混合持久化

?布隆過濾器

分布式鎖


數據類型

五種常用數據類型:字符串,List,HASH,Set,Zset(也叫sorted Set)

字符串:

key-Value形式儲存,key可以理解為字符串的名字,Value是實際字符串的內容

底層有SDS實現(SDS:簡單動態字符串),個人理解是基本字符串的擴展。短的時候是embstr,長了是raw。簡單理解就是,Redis額外開辟了一或二個額外空間來儲存字符串相關的內容,比如字符串的長度,字符串的實際地址等。

基本的使用語句是set,get,setNX等。

字符串可以用來當做分布式鎖:很多個客戶端共同使用一個Redis,在進行一個操作的時候,設置一個字符串:set lock_key unique_value NX PX 10000 后面的PX 10000 是超過這個時間自動釋放鎖(防止死鎖)NX是not exit 不存在這個字符串時才會加鎖。

字符串也可以用作緩存:儲存數據就是了;也可以用作計數器:字符串有自增指令INCR和INCRBY,可以使字符串的值加1或加多,并且由于是單線程實現的,不需要考慮競態問題

List:

Key-value儲存形式,是一個雙端隊列

內部結構listPack

基本語法LPUSH,LPOP,RPUSH,RPOP等,并且有阻塞獲取的語法BRPOP:阻塞,右邊,取

可以用作簡單的消息隊列,但有些問題:List實現的消息隊列不能實現持久化;不能多個消費者處理同一個消息;需要手動維護每個消息的唯一id

HASH:

Key-field-value儲存形式, key是HASH的名稱,filed可以類比哈希表的key,Value可以類比哈希表的Value

內部儲存結構是listPack和哈希表

基本語法是HSET、HMSET等(M指的是Multi,一次處理多個)

Set:

Key-filed儲存形式

內部實現時整數集合和哈希表

和普通的set一樣,每個元素唯一,但是是無序存儲。

基本語法是SADD,SREM,SCARD(cardinality,集合的勢),SMEMBERS(獲取集合中的全部元素)。并且SET支持SUNION,SINTER,SDIFF(并,交,差)集合運算

Zset:

key-score-value儲存形式,集合中的元素按照score的順序排序。

內部實現時listPack和跳表

基本語法是ZADD,ZREM等。支持交、并操作(不支持差了)

BitMap:(這個及以下是后來新增的數據結構)

每個元素都是二進制的(相當于維護了一個二進制的數組,但不需要實現設定大小)

基本語法SETBIT,GETBIT等,支持二進制運算:與&,或|,異或^,反~

主要用于制作布隆過濾器或記錄一些二進制的狀態(比如簽到等)

HyperLogLog:

用處就是可以用一個相當小(12k)的內存來統計具體有多少個元素(能用,但是有誤差0.81%)

常用指令PFADD(添加),PFCOUNT(計數),PFMERGE(合并)

比如向這個結構中PFADD100w個數據,可以用PFCOUNT快速得到大致的數量

GEO:

地圖存儲,底層用的是ZSET

Stream:

主要解決list實現消息隊列時不能持久化,不能多個消費者處理的問題

基本語法XADD,XREAD等。可以定義XGROUP(消費者組),用消費者組讀取內容(一組可以有多個消費者)。組內消費者不能讀取同一條數據,但不同組的消費者可以處理同一條內容。

處理完成后向Stream返回XACK確認消息已經處理完畢。沒處理完的數據可以用SPENDING獲取,這樣當Redis意外關閉時未處理的消息也不會丟失。

但是Stream存儲的消息中間件可能會丟失,并且大小受內存大小的限制。如果有高要求的話還是用kafka等專業消息隊列比較好

主要數據結構

我們之前說的,上面的數據類型都有一個key,以及對應的value。(key是數據的名稱,value就是對應的數據)。而Redi是用一個哈希表來保存這些全部的數據的。在下圖中可以看到,Redi維護了一個dictEntry,就是哈希表中儲存的鍵值對。每個對有一個key指針和value指針,key指針指向一個String,value指針指向對應的具體數據類型。

Redis中存儲的都是Redis對象。有一個type標志這個對象是哪一種數據類型(比如Zset),encoding表示底層數據結構。比如SDS會有embstr和raw兩種選擇。最后的就是一個指針,指向真正的數據

SDS

就是設計了一個頭結構,記錄了字符串的長度和已經分配的內存空間大小。這樣獲取長度的復雜度為O(1),并且執行字符串拼接的時候不用再考慮內存分配(C的問題),最后,由于已經標明了字符串的長度,因此不需要再為字符串添加‘/0’的終止符,使得字符串現在不止可以儲存文本,也可以儲存二進制數據了

壓縮列表

結構的會儲存結構整體占用的字節數,列表尾的偏移量,節點數和結束點。(不再是普通鏈表的分塊存儲,這樣提高了CPU緩存的命中率)

每個節點會儲存上一個節點的長度,本節點的數據類型和長度,實際數據內容。

壓縮列表的兩個問題就是:查詢中間節點需要的復雜度是O(n);連鎖更新問題(主要是在改變節點內容的時候需要改變下一個節點的prevlen值,可能會出現改一個之后后面的很多個都要改的情況。)

HASH

和java7及以前的邏輯是基本上一樣的。不過是擴容的時候不是一次性復制,而是用到哪個bin,就把哪個bin的內容復制過來

整數結合

就是一塊連續的內存空間,三個量:編碼方式,元素個數和保存元素的數組。通過二分查找實現有序數組進而實現唯一。不過每個數字占的大小有限制(比如都要小于16位),如果有一個數超過這個限制,那么所有的數都要大小升級,數組整體擴容

跳表

很多層,每層維護一個鏈表。目的是為了實現鏈表的更快查詢,大致結構長這個樣子。鏈表的層數要事先設計好。節點按照一定的概率向上升(這個概率要人為給定)

跳表的主要優勢是他結構簡單易于實現,范圍查詢更快,插入刪除需要的操作更少,每個節點儲存的指針更少(跳表平均1/(1-p),平衡樹兩個)

quickList

添加元素的時候,不再直接新建一個節點,而是先看這個節點對應的壓縮鏈表能否容納這個節點(就是看列表中是否還有足夠的空閑內存和可容納節點個數),如果可以的話就放進去,不能的話才新建一個節點

listpack

和壓縮鏈表相比,實際上就是把pre(上一個節點的長度)刪掉,換成了當前節點的長度。相當于壓縮鏈表的改進版。

持久化

Redis的持久化(其實就是將內容寫入到磁盤)有AOF和RDB兩種策略

AOF(Append only FIle)

就是寫一個aof文件,這個文件保存在磁盤中。具體來說有以下處理過程:

Redis接受到寫命令之后,先在內存中修改對應的數據,然后把這個修改數據的命令儲存在aof緩沖區中,等待合適的時機把緩沖區中的內容寫入到aof文件中。

AOF寫回策略

所謂的合適的時機就是aof的刷盤策略,總共有三種:always、everysec和no。第一種就是每次有修改命令,都把內容寫入到內核空間的page cache中同時寫入到磁盤中。everysec是每一秒寫一次,no是交給操作系統決定什么時候寫入磁盤。(其實就是aof的三種刷盤策略,主要是標明主進程什么時候調用操作系統的fsync方法進行刷盤,aof緩沖區中的內容都是寫完之后直接調用i/o系統的Write方法寫入到page cache中)

AOF重寫

如果aof文件太大,那么就會觸發aof文件的重寫。主進程先fork一個子進程(就是復制了一份頁面給子進程),子進程根據頁表內容讀取內存并寫入到新的aof文件中(實際上是生成一個寫這條內容的指令,并將這條指令保存下來)。這期間父進程和子進程是共享物理內存的,當父進程對某一塊內存進行修改的時候,才會將這塊內存原有的內容復制給子進程,父進程修改內存(所謂的寫時復制)。這樣就保證了子進程寫的內容都是父進程fork時內存的樣子。

aof后臺重寫

當子進程生成新的aof文件時,主線程會正常執行內容(并不會阻塞),這些正常執行的操作將不止被緩存在aof緩沖區中,還會被寫在aof重寫緩沖區中。子進程將頁表中的內容寫好后,會告訴主進程已經重寫好(發送一條信號)。主進程將aof重寫緩沖區中的內容追加到新的aof文件中,然后將老aof文件覆蓋。

rdb快照

有兩種策略:save和bgsave。save是主進程完成快照生成工作,此時Redis會被阻塞,bgsave是fork一個子進程生成對應的快照。

其實邏輯跟aof重寫差不多,只不過aof重寫的時候是生成語句并保存,rdb重寫是記錄對應的數據內容,用的時候直接讀取就可以了。并且rdb快照生成由于是全量復制,所以會很慢,需要平衡生成快照的時機和丟失內容多少的關系。(aof也是全量掃描,但有的內容可能在寫的時候可以由一個可重復指令完成,因此某些情況下會比rdb快)

混合持久化

在aof重寫時,不再生成讀取內容生成指令,而是直接按照rdb的模式復制,在處理aof重寫緩存的時候才是aof的記錄語句的形式。

過期刪除和內存淘汰

這兩個操作并不相同:過期刪除指的是我們可以對一個Redis對象設定存活時間,超出這個時間后刪除對象時過期刪除,內存淘汰則是當Redis的儲存空間不足時,需要對一些對象進行刪除

過期刪除:

我們知道在設定一個Redis對象的時候,可以設置他的過期時間,那么如何對過期對象進行刪除就有三種策略:

  1. 定時刪除。這種策略指的是在設定一個可以過期的Redis對象時,同時也啟動一個定時事件,事件到期后由一個專門的線程來對他進行刪除。這種方法思路很簡單,但實際使用會消耗太多資源,比如CPU時間等,并且也可能會導致一些熱點數據同時大量被刪除,因此這種刪除策略并沒有被采用
  2. 定期刪除。這種策略說的是每過一段時間檢查一部分的對象(一般是20個),刪除過期的對象,如果說過期對象大于五個,就繼續隨機抽取然后刪除,直到超時或者過期對象少于五個
  3. 惰性刪除。這種指的是完全不檢查,等到用的時候再看有沒有過期,過期的話刪除。這種策略的問題也十分明顯:會有大量的過期對象沒有被刪除,占用空間

Redis使用的是定期和惰性結合,一方面會定期檢查,另一方面使用對象的時候如果過期就刪除

Redis實現了一個HashMap來儲存每個可過期對象的對象地址和對應的創建時間,使用的時候比較這個創建時間。需要指明的是,這個HashMap和我們可使用的hash并不一樣:我們使用的hash數據結構要求key是字符串,這個HashMap存的是地址,并且這個HashMap是Redis底層自己實現的,我們用不了

內存淘汰

當內存不夠用的時候,就需要淘汰一些不常用的對象。淘汰手段一般是LRU和LFU。LRU指的是找到最久沒有使用的,LFU指的是使用次數最少的。Redis對這兩個都做了優化

  1. LRU:每個對象儲存最近被使用的時間,刪除的時候隨機采樣五個,刪除最久沒用的內個
  2. LFU:這個相對復雜一點,Redis給每個對象增加了一個LFU字段,這個字段記錄了上次使用的時間和一個標志使用頻次的數,這個頻次的數初始為5,對象每次被使用時會先減1,然后根據上次使用時間和現在的時間算一個權重出來,頻次再加上這個權重作為最終的結果

具體使用哪個Redis提供了一些參數,修改這些參數就行了

主從復制

具體應用:

?布隆過濾器

可以把他理解成一個只能添加和查找元素是否存在的數據結構(某種意義上的Hashmap?)

他的特點是可以實現快速查找,但查找只能保證他認為不在的不在,他認為在的也不一定在

布隆過濾器底層數據結構是一個長二值數組。同時設置了幾個hash函數。

????????每次插入元素時,就計算這個元素對應的hash值,并將關于數組長度取模后的對應位置的值設為1。

? ? ? ? 在查詢的時候,計算這個元素在數組中對應的幾個位置(也是根據hash值取模得到的),如果全為1,那么認為他存在(當然也可能不在)。

應用場景:解決redis緩存穿透

實際使用:Redis其實并沒有實現所謂的BitMap數據結構,他是依賴字符串實現的。在我的項目中,布隆過濾器是這樣設計的:

首先根據公式計算出需要的哈希函數數量和數組長度:n是預估的數量個數,p是誤判率

計算得到的m就是需要的布隆過濾器長度(指定的字符串所占的比特數),k是需要的哈希函數個數,取整。

選擇一個哈希函數(我選的是一個,不是k個!)比如md5,計算當前元素的哈希值。將計算的到的哈希值拆成k分,每份再計算一個hashCode(),對長度取模就得到一個數組,長度為k。加入的時候將這個數組每一位上的值設置為true。查找的時候就看對應位有沒有false,只要有一個就說明當前元素沒有被加入過。

分布式鎖

分布式鎖的實現也是字符串。具體來說,每個進程(或者線程)公用一個Redis的時候就可以設計分布式鎖,使得只有一個線程可以使用資源。

分布式鎖主要依賴字符串的setIfAbsent和get方法。當一個線程嘗試獲取鎖的時候,就會在Redis中嘗試設置一個字符串,設置成功的話就相當于成功獲取了鎖,否則就是獲取鎖失敗,任務結束之后刪除字符串就相當于釋放了鎖。

單一Redis節點需要考慮這幾個問題:

  1. 某個線程在刪除字符串時,要確保這個字符串是他自己設置的
  2. 要保證刪除操作是原子的
  3. 釋放鎖之前鎖不能自動過期

對于第一個問題,我們考慮這個場景:兩個線程都想要獲取鎖,線程A獲得鎖,但超時了,鎖自動釋放,線程B新建了鎖,線程A執行完任務刪除了鎖,但這時他刪除的是線程B新建的鎖。解決思路就是用字符串的key當做鎖名,value當做線程對鎖的持有證

第二個問題,要先檢查鎖存在才能釋放,這中間存在時間,這段時間里可能會出現鎖過期然后另外一個線程新建鎖的問題,因此要讓Redis一次性檢查并且釋放。具體實現思路就是使用lua腳本。Spring中,用字符串寫好腳本然后提交execute。

第三個問題,采用看門狗機制,對所有的任務線程設計一個定時任務線程池,新建任務線程的時候就把自己線程的信息傳遞給看門狗,看門狗定期用收到的信息執行鎖延時任務:只要線程還在運行,就延長鎖。任務線程結束之后再中斷這個定期任務。(注意這里要用到線程的ThreadLocal來記錄每個線程獨立的信息,比如他的鎖名,鎖持續時間等等)

如果多Redis節點的話需要考慮redLock,但我這里是單節點就沒有實現

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

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

相關文章

基于協同過濾的文學推薦系統設計【源碼+文檔+部署】

基于協同過濾的文學推薦系統設計 摘要 隨著信息技術的飛速發展和文學閱讀需求的日益多樣化,構建一個高效、精準的文學推薦系統變得尤為重要。本文采用Spring Boot框架,結合協同過濾算法,設計并實現了一個基于用戶借閱行為和社交論壇互動的文學…

鴻蒙電腦:五年鑄劍開新篇,國產操作系統新引擎

出品 | 何璽 排版 | 葉媛 前不久,璽哥發布的《鴻蒙電腦,刺向壟斷的利刃,將重塑全球PC市場格局》發布后,獲得了讀者朋友的積極反饋,不少都期望鴻蒙電腦早日發布。 如今,它真來了! 5月8日&…

EWOMAIL

1、錯誤 Problem: problem with installed package selinux-policy-targeted-3.14.3-41.el8.noarch package fail2ban-server-1.0.2-3.el8.noarch requires (fail2ban-selinux if selinux-policy-targeted), but none of the providers can be installed - package fail2ban-…

qt5.14.2 opencv調用攝像頭顯示在label

ui界面添加一個Qlabel名字是默認的label 還有一個button名字是pushButton mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <opencv2/opencv.hpp> // 添加OpenCV頭文件 #include <QTimer> // 添加定…

Spring三級緩存的作用與原理詳解

在Spring框架中&#xff0c;Bean的創建過程涉及到了三級緩存機制。這個機制主要是為了提高單例模式下bean實例化和依賴注入的效率。本文將深入探討Spring中的三級緩存&#xff0c;以及其在bean生命周期中的重要作用。 首先&#xff0c;讓我們理解什么是三級緩存。Spring中的三…

IoTDB集群的一鍵啟停功能詳解

IoTDB&#xff08;Internet of Things Database&#xff09;作為一種專為物聯網設計的高性能時序數據庫&#xff0c;支持單機與分布式等多種部署模式。隨著節點數量的增加&#xff0c;手動管理集群的啟動與停止過程變得繁瑣。為了提升部署效率&#xff0c;IoTDB 提供了一鍵啟停…

Oracle學習日記--Oracle中使用單個inert語句實現插入多行記錄

目錄 前言&#xff1a; 問題現象&#xff1a; 問題分析&#xff1a; 解決方法&#xff1a; 1、insert into ... union all句式 2、insert all into ...select 1 from dual句式 總結&#xff1a; 前言&#xff1a; 最近項目中使用到了Oracle數據庫&#xff0c;由于Oracle數…

LabVIEW 程序運行時內存不足報錯原因

在 LabVIEW 程序開發與運行過程中&#xff0c;內存不足報錯并退出是常見且棘手的問題。這不僅影響程序穩定性&#xff0c;還可能導致數據丟失與系統崩潰。以下從程序設計、硬件資源、系統環境等多維度深入剖析其成因&#xff0c;幫助開發者準確定位并解決問題。 ? 一、程序設…

【GAN網絡入門系列】一,手寫字MINST圖片生成

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習記錄博客&#x1f356; 原作者&#xff1a;K同學啊 博主簡介&#xff1a;努力學習的22級本科生一枚 &#x1f31f;?&#xff1b;探索AI算法&#xff0c;C&#xff0c;go語言的世界&#xff1b;在迷茫中尋找光芒…

Baklib加速企業AI數據智理轉型

Baklib智理AI數據資產 在AI技術深度滲透業務場景的背景下&#xff0c;Baklib通過構建企業級知識中臺架構&#xff0c;重塑了數據資產的治理范式。該平臺采用智能分類引擎與語義分析模型&#xff0c;將分散在郵件、文檔、數據庫中的非結構化數據轉化為標準化的知識單元&#xf…

如何在Windows右鍵新建菜單中添加自定義項,將notepad添加到新建菜單

一、簡介 Windows 右鍵新建菜單的核心管理機制隱藏在注冊表的 HKEY_CLASSES_ROOT 根鍵中。這里存在兩種關鍵注冊表項&#xff1a;文件擴展名項和文件類型項&#xff0c;它們共同構成了新建菜單的完整控制體系。 以常見的.txt文件為例&#xff0c;系統通過以下機制實現新建菜單…

中大型水閘安全監測系統建設實施方案

一、方案背景 隨著科技的不斷進步&#xff0c;水利工程的數字化轉型已經成為提升城市水資源管理效率和增強防洪能力的關鍵。今天&#xff0c;我們將引導您深入了解我國大中型水閘安全監測管理系統的構建方案&#xff0c;探討如何運用先進技術確保國家水安全&#xff0c;提升水利…

Gartner《如何有效融合Data Fabric 與Data Mesh數據戰略》學習心得

在當今數字化時代,數據已成為企業最為重要的戰略資產之一。企業對于高效的數據管理架構的需求日益迫切,以確保能夠從海量數據中提取有價值的信息,支持業務決策和創新。近年來,數據編織(Data Fabric)和數據網格(Data Mesh)成為了數據管理領域的兩個熱門概念,在行業內引…

matlab建立整車模型,求汽車的平順性

在MATLAB中建立整車模型評估汽車平順性&#xff0c;通常采用多自由度振動模型。以下是基于四分之一車模型的詳細步驟和代碼示例&#xff0c;可擴展至整車模型。 1. 四分之一車模型&#xff08;簡化版&#xff09; 模型描述 自由度&#xff1a;2個&#xff08;車身垂直位移 z2…

探究電阻分壓的帶負載能力

我們經常使用兩個電阻去分壓來獲得特定的電壓,那么我是兩個大阻值電阻分壓獲得的電壓驅動能力強,還是小阻值電阻分壓得到的電壓驅動能力強呢? 一、電壓相同時,電流的大小 下面是兩個阻值分壓得到的仿真圖 電路分析: VCC都是5V,探針1和探針2測到的電壓都是1.67V; 根據…

牛客網NC22222:超半的數

牛客網NC22222:超半的數 題目描述 輸入輸出格式 輸入格式&#xff1a; 第一行包含一個整數 n (1 ≤ n ≤ 1000)第二行包含 n 個整數 a_i (1 ≤ a_i ≤ 10^9) 輸出格式&#xff1a; 輸出一個整數&#xff0c;表示出現次數超過一半的那個數 解題思路 這道題目有多種解法&a…

開發日常中的抓包工具經驗談:Charles 抓包工具與其它選項對比

開發日常中的抓包工具經驗談&#xff1a;HTTPS調試怎么選&#xff1f; 在移動開發或Web API聯調時&#xff0c;網絡請求常常成為問題定位的第一難題。尤其是面對加密的 HTTPS 請求&#xff0c;傳統瀏覽器調試工具已顯得力不從心。 我們團隊最近在排查一個安卓應用中的支付延遲…

哈希表實現(1):

1. 哈希&#xff1a; 之前我們的紅黑數的查找是由于左邊小右邊大的原則可以快速的查找&#xff0c;我們這里的哈希表呢&#xff1f; 這里是用過哈希函數把關鍵字key和存儲位置建立一個關聯的映射。 直接定址法&#xff08;函數函數定義的其中一種&#xff09;&#xff1a; 直…

泰迪杯特等獎案例深度解析:基于多級二值化與CNN回歸的車牌識別系統設計

(第八屆泰迪杯數據挖掘挑戰賽特等獎案例全流程拆解) 一、案例背景與核心挑戰 1.1 行業痛點與場景需求 在智慧交通與無感支付場景中,車牌識別是核心環節。傳統車牌識別系統在復雜光照、污損車牌、多角度傾斜等場景下存在顯著缺陷。根據某智慧油站2024年運營數據顯示,高峰期…

光學變焦和數字變倍模塊不同點概述!

一、光學變焦與數字變倍模塊的不同點 1. 物理基礎 光學變焦&#xff1a;通過調整鏡頭組中鏡片的物理位置改變焦距&#xff0c;實現無損放大。例如&#xff0c;上海墨揚的MF-STAR吊艙采用30倍光學變焦鏡頭&#xff0c;焦距范圍6~180mm&#xff0c;等效焦距可達997mm。 數字…