Redis-如何減少修改字串帶來的記憶體分配過程消耗-深入理解
阿新 • • 發佈:2021-02-08
1、Redis字串的特點
- 已知Redis的鍵值對的鍵是字串物件,值也可以是字串物件,值還可以是列表物件、雜湊物件、集合物件、有序集合物件總共這五種物件。
- Redis的底層實現語言是經典的C語言,Redis的字串定義與C語言的字串定義有一些區別
– 1、C語言的字串物件是以空字元結尾的字元型別陣列,有多少個字元該陣列就是多大的
– 2、Redis使用的是簡單動態字串(SDS, simple dynamic string)的抽象型別,SDS除了用來儲存Redis的字串物件之外,還可以用作緩衝區。
SDS的資料結構定義如下:
strcut sdshdr{
int len;//用來儲存陣列中已使用位元組的數量=SDS所儲存的字串的長度
int free;//用來儲存陣列中未使用位元組的數量
int buf[];//位元組陣列,用來儲存字串。大小=len + free
}
SDS的結構用影象表示如下,以儲存字串"Redis"的一個SDS為例:
- 從中可以看出,free=0,表示該SDS的未使用空間大小為0;
- len=5,表示SDS的儲存的字串的大小為5位元組;
- buf的屬性是一個char型別的陣列,其中儲存瞭如上所示的字元,0個未使用字元空間,還要’結尾處的/0‘空字元。
- 如果要獲取該SDS儲存的字串的長度,不用遍歷字串,直接使用len的值就行。獲取長度方便,就能有效防止緩衝區溢位的風險,還能減少修改字串(如拼接等操作)帶來的記憶體重分配資源消耗
2、SDS如何實現減少修改字串帶來的記憶體重分配
結論:SDS使用空間預分配和惰性空間釋放兩種機制來避免標題所示問題
記憶體重分配:字串拼接可能帶來緩衝區溢位,字串縮短可以造成記憶體洩露,這就需要對記憶體空間進行重分配,這種操作涉及到的演算法複雜,可以需要執行系統級別的呼叫,所需要的資源和時間比較多,要儘量避免,可以使用空間換時間的策略。
(1)空間預分配
當對SDS的API對SDS進行修改,需要對SDS進行空間擴充套件的時候,會帶來兩個空間的擴充套件:所必須的字串空間,額外的未使用空間
- len的長度<1MB時,分配的未使用空間大小=len
- len的長度>=1MB時,分配的未使用空間大小=1MB
- 也就是len<1MB的時候free=len,大於的時候free=1MB
通過空間預分配策略,Redis可以減少連續執行字串增長操作所需要的記憶體重分配次數。
//示例:
sdscat(s,"Tutorial");//將s和"Tutorial"字串拼接
對標題1的SDS執行以上程式時,因為未使用字串空間不夠,將執行一次記憶體重分配操作:len修改為拼接之後的大小並填入資料,free變為13位元組。
如果再次執行上述拼接程式碼,free空間夠用,將不會產生記憶體重分配。
(2)惰性空間釋放
惰性空間釋放針對優化SDS的字串縮短操作,當SDS的API需要縮短SDS所儲存的字串的時候,並不一定要立刻進行記憶體重分配來釋放空閒出來的位元組
- 將釋放出來的位元組轉換為空閒位元組,同時更新free屬性的大小
- 此操作能避免縮短字元帶來的記憶體重分配操作,還為可能的增長操作帶來了快取的空間
參考資料:<Redis設計與實現>第一章