Redis 學習筆記《一》

2021-09-23 23:50:05 字數 3646 閱讀 3127

最近突然對redis感興趣,整了本書學習一下,順便把看到的東西和自己的想法做個筆記記錄下來。當然初學肯定會有理解的不充分或者錯誤的地方,希望以後能慢慢改進。

這篇主要記錄一下書上的第一部分,講述了redis的各種資料結構和底層實現,這是redis的核心底層,也是redis的基礎,所以還是值得多看幾遍的。

第一部分前面都在講redis中的各種資料結構,準確一點應該是adt,因為還有這些資料結構的操作演算法。需要注意區別於人們常說的redis的五種物件結構(字串string,列表list,雜湊hash,集合set,有序集合zset),而是實現這五種物件的更基本的資料結構。而第一部分的最後一章才是講述了這五種物件的具體實現。所以這裡先來依次講述6種基本的資料結構。分別是簡單動態字串sys,鍊錶linklist,字典dictionary,跳躍表skiplist,整數集合intset,壓縮列表ziplist

顧名思義,就是表示字串的。redis是用c語言寫的,c裡沒有字串型別,只有用空字元結尾的字元陣列,確實不好用。所以redis就不用這玩意,然後自己造了乙個adt,就是簡單動態字串(****** dynamic string, sds),sds是redis的預設字串表示型別。

而c字元陣列就用來做字串字面量,表示不需要進行修改的地方(比如列印日誌),而需要可以被修改的字串的時候,就會用sds,我想這也是為什麼叫動態字串的原因吧。

這裡需要注意一下,redis是kv型資料庫,所以它的所有key都是字串型別,我們說什麼型別的key的這個型別都是指這個key對應的value,因為key都是字串。

接下來書上給了一些sds的操作,比較簡單就不粘了…

然後還提了一下sds還會用作緩衝區(buffer),比如aof模組中的aof緩衝區以及客戶端的輸入緩衝區,我目前還不知道這個aof是什麼,所以留個印象,以後整明白了再來補充
下面才是關於這個的重點了,就是來分析sds的結構

原始碼在sds.h/sdshdr

struct sdshdr
結構體的意義我都在**後面注釋了,我在想書上這裡為什麼要寫用到的位元組數量,還有為什麼是位元組陣列,我的理解是因為char型別大小是乙個位元組,所以這就是位元組數量吧。書上給了個圖更容易理解

其實這個sds就是用c語言表示的乙個字串的結構體,只不過多記錄一下空餘的陣列容量而已。這個buf就是個字元陣列,所以保留了c串的風格,以乙個空字元結尾,我想這樣的乙個好處是能夠使sds直接用一些c的字串處理函式,比如printf,沒想到書上真就這麼說了,而且說這個空字元都會由sds給自動完成。

-1.2.1 效能

redis用這樣乙個結構肯定是有好處的,首先就是獲取字串長度,因為記錄了len這個值,所以不用再像c語言的字元陣列一樣遍歷一遍字元陣列才能得到這個值,時間複雜度從o(n)一下就降到了o(1)。效能也比strlen好,而且比strlen安全。

這時我就想到了strlen函式的問題,但是strlen其實是不安全的函式,因為它是遍歷字元陣列到空字元,但是如果惡意的串不是以空字元來結尾的話,就可能得到乙個非常大的錯誤的值。

另外redis取字串的長度命令就是strlen

- 1.2.2 空間分配

書上說到的第二個問題就是c串陣列的溢位問題,我想到的就是strcat這個函式的問題,c中strcat會預設認為dest指標已經有足夠多的記憶體,但是如果不夠就會產生溢位。

但是sds就不用擔心這個問題,當sds需要進行修改的時候,首先會檢查sds的空間是否滿足修改所需要的要求,如果不滿足就會拓展到所需要的大小再修改。這些都是api自動完成的。

書上這裡對sds的空間調整做了詳細的說明,這也是redis提高效率的乙個手段。剛才說到sds會根據所需要的空間大小進行動態的空間調整,但是對於char陣列,因為不記錄長度,所以每次都需要重新分配記憶體,如果每次修改都進行記憶體重新分配,勢必會影響效率,為了減少記憶體分配的次數,sds採取了兩種策略,分別是空間預分配惰性空間釋放

空間預分配

當需要對sds進行空間拓展的時候,程式會為sds分配額外的未使用空間,具體計算方法如下:

1.如果修改之後sds的長度小於1mb 那麼sds將會多分配和sds當前長度相等大小的空閒空間

2.如果修改之後sds的長度大於1mb 那麼sds將會多分配1mb的空閒空間

這個是比較好理解的,就是redis對拓展空間的大小做了乙個權衡。如果串長度不大(不超過1mb)那麼就再把空間double一下,因為串不長,所以double不會帶來很大的浪費風險。但是如果串很長(超過1mb),比如10mb,double 的話就比較奢侈了,所以直接多加1mb,就比較合理一點。這種思想和cpp的vector分配空間的思想有點類似。然後書上給了例子

此時這個sds的free已經為0了,如果連線上「cluster」 這個串就需要重新分配空間變成這樣

sds的空間不僅變成了13,而且free也變成了13,也就是說多給分配了當前長度的大小空間,這樣下次再需要新增長度不超過13的字元或者串就不需要再進行一次記憶體分配了

惰性空間釋放:

當需要對sds進行縮短的時候,sds並不會立即重新分陪記憶體來**縮短後多出來的位元組
這也很好理解,因為當前不需要的空間不意味著將來就不需要用了,而且根據區域性性原理更有可能使用到,不及時釋放能省去下次的再次釋放。

但也有個問題,如果這個串真的不再用了,難道就一直留著嗎,或者說有時候如果已經知道不用了,也不能釋放掉嗎?

對於前者,我猜可能會有某種機制根據修改的時間來進行調整,如果學習到後面有所發現這裡會進行補充

對於後者,書上說sds提供了釋放sds未使用空間的api

-1.2.3二進位制安全

書上說到c語言中的字元是按照某種編碼方式,比如ascii,對於字元陣列表示的字串,字串裡是不能有空字元的,這是因為c根據這個來決定字串的末尾。所以c字串往往只能存文字,對於,音訊這些二進位制資料束手無策。但是redis是可以適用於各種場景,所以sds的api是可以處理二進位制的,就是以二進位制的方式來處理buf中的資料,稱為二進位制安全。也就是存進去是什麼樣的二進位制,讀取的時候還是什麼樣。

比如這個串,在c裡是不正確的,只會讀成redis,因為後面是空字元結尾了。

但是對於sds,因為儲存了長度,它只會以len來判斷字串是否結束

我想到剛才在說sds的結構的時候,提出的疑問,為什麼是位元組陣列,書上恰好提了一下這個原因,redis不是用這個陣列來存字元,而是存二進位制

最後書上簡單提了一下sds相容一些c的串操作,然後給出了一些總結,這裡也記錄一下

寫了這麼多只是對redis的資料結構的表面理解,具體的細節和深入理解還是需要去看原始碼,希望以後能在這補充原始碼的部分。

Redis學習筆記 一

工作百無聊賴,準備把自己想學的東西挨個學一遍,最近突然發現如果你學了乙個東西,但是毫無記錄只是看了一些書的話,這樣過去後很快就忘記了。所以我覺得很必要採用一種方式把看到的東西記下來,這樣可以加深記憶,也可以在以後回顧的時候一眼看到一項技術中的重點,方便回顧和查詢。最近首先想學一下redis,因為專案...

redis學習筆記(一)

版本 4.0.2 安裝 yum install gcc c 若連線失敗,可能是linux未開啟ssh服務,開啟命令 需要root賬戶 如下 開啟服務 service sshd start 關閉防火牆 etc init.d iptables stop ps 此期間可能linux網路連線中斷,請注意 下...

redis學習筆記一

最近開始儲備一些知識點,以下為redis的學習筆記。簡介 remote dictionary server redis 是乙個由salvatore sanfilippo寫的key value儲存系統。redis是乙個開源的使用ansi c語言編寫 遵守bsd協議 支援網路 可基於記憶體亦可持久化的日...