標籤:
幾個概念
1:key對象 資料庫儲存索引值對的鍵,總是一個字串對象。
2:value對象 資料庫儲存索引值對的值,可以是字串對象,list對象,hash對象,set對象,sorted set對象。
例如:
set msg "hello world" 則redis在資料庫中建立一個新的索引值對,鍵和值都是一個字串對象,底層實現都是一個sds對象。
rpush fruits "apple" "banana" "cherry" 則redis在資料庫中建立一個新的索引值對,鍵是一個字串對象,底層實現是一個sds對象;值是一個列表對象,列表對象包含了三個字串對象,由三個sds對象實現。
3:sds (sample dynamic string)
定義:
struct sdshdr{
//記錄buf數組中已使用位元組的數量
//等於sds所儲存字串的長度
int len;
//記錄buf數組中未使用的位元組數量
int free;
//位元組數組用來儲存字串
char buf[];
}
4:C語言的簡單字串的表示方式不能滿足redis對字串在安全性、效率以及功能上的要求,故引用sds字串
4.1 sds字串擷取字串長度效率更高,時間複雜度為o(1),c字串的為o(n),保證擷取長度的工作不會成為redis的效能瓶頸。(更新sds長度工作是由sds的api在執行時自動完成的);
4.2 杜絕緩衝區溢位 s1如果沒有分配足夠的記憶體空間,擴充時s1的資料就會溢出到下一個相鄰的s2空間中,而sds的空間分配時,api會先檢查sds的空間是否滿足修改所需的要求,如果不滿足,api會自動將sds空間擴充到執行修改所需的大小,然後在執行修改操作;
4.3 減少修改字串時帶來的記憶體重新分配次數
1) c字串執行加長操作,如果忘記重新分配記憶體就會出現緩衝區溢位,如果執行縮短操作,如果忘了重新分配記憶體就會出現記憶體泄露,由於redis是資料庫,對速度要求苛刻,如果資料頻繁修改,使用c字串就需要頻繁執行記憶體配置,執行記憶體配置就會佔據修改字串的大部分時間,如果頻繁發生,還會對效能造成影響,故redis使用sds字串作為底層的實現。
2) sds字串 buf數組長度不一定等於字元數量加一,數組包含未使用位元組,這個長度儲存在free屬性上
3) 空間預分配:最佳化sds字串增長操作,api對sds進行空間擴充的時候,程式不僅會為sds修改所需的空間,還會為sds分配額外的未使用空間 公式:擴充後小於1m,分配和len屬性相同的未使用空間,大於等於1m分配1m的未使用空間。這樣在執行擴充操作時,api會先檢查未使用空間是否足夠,如果足夠就直接使用未使用空間,減少記憶體配置次數,基於這種策略,在執行n次修改操作時,重新分配記憶體次數從n次降為最多n次。
4) 惰性空間釋放:最佳化字串縮短操作,在進行縮短操作時,程式不會立即重新分配記憶體回收縮短後多餘出來的位元組,而是使用free屬性記錄起來,等待將來使用,如果有增長操作可以直接使用。當然sds也提供了釋放sds未使用空間的api,所以不必擔心惰性釋放帶來的記憶體浪費問題。
4.4 二進位安全 c字串中必須符合某種編碼,出來字串末尾,不能包含Null 字元,所以c字串只能儲存文本資料,不能儲存圖片、音頻、視頻等位元據,sds的api會以二進位的方式處理buf數組裡的資料。使得redis可以儲存任意格式的二進位。
4.5 相容部分c函數 sds遵循Null 字元結尾的慣例,api會將sds儲存資料的末尾設定為空白字元,並且會在buf分配空間時多分配一個位元組,這樣可以讓儲存文本資料的sds重用string.h庫定義的一些函數。
小結:
c字串擷取長度複雜度o(n),sds為o(1)
api不安全可能造成緩衝區溢位,api安全不會造成緩衝區溢位
修改字串長度n次會執行n次記憶體配置,最多n次
只能儲存文本資料,可以儲存文本或位元據
可使用所有string.h庫函數,只能使用部分函數
Redis 資料結構之簡單動態字串SDS