Redis之簡單動態字串SDS
1.用途
Redis並未使用c語言的string,而是自己構建簡單動態字串(SDS,simple dynamic string)抽象型別表示字串.
c語言string只是用在不需要修改的地方,例如日誌列印.其他地方基本都是SDS,例如:
redis>RPUSH fruit "apple" "banana" "cherry"
除了儲存字串值外,還用於緩衝區buffer:
AOF中AOF緩衝區,客戶端狀態中輸入緩衝區
2.定義
SDS: struct sdshdr{ int len; //記錄buf陣列已使用位元組數=SDS儲存字串長度 int free; //buf陣列中未使用位元組數 char buf[]; //位元組陣列char型別陣列,儲存字串 }
key(就是sdshdr),free,len,buf. 其中buf指向char陣列,其中儲存sds字串內容,以\0結尾,free和len都不計算\0所佔空間
例如free=5,len=5,buf指向的為sds字串佔用5個,\0佔用1個,空佔用5個.有空畫圖
3.SDS與c字串區別
### 3.1 常數複雜度獲取字串長度
由於記錄了len,所有strlen獲取字串長度O(1),c中遍歷O(n)
3.2 杜絕緩衝區溢位
c中字串不記錄長度容易造成緩衝區溢位,sds api修改sds時,api先檢查空間是否足夠,不夠自動空間擴充套件後再修改(全自動避免緩衝區溢位)
3.3減少修改字串時帶來的記憶體重分配次數
SDS通過未使用空間接觸了字串長度和底層陣列長度之間的關聯.有了free屬性在,buf陣列長度不一定是+1,因為其中可以包含未使用位元組.
未使用空間幫助SDS實現了 1.空間預分配 2.惰性空間釋放 兩種優化策略
3.3.1.空間預分配(增)
用於優化SDS字串增長操作.
對SDS進行空間擴充套件時,不僅給SDS分配修改所必需的空間,還會給SDS分配額外未使用空間:
如果修改後SDS長度(len)小於1MB,分配與len同樣的未使用空間(len*2).實際長度變成len(SDS修改後實際長度)+len(未使用空間)+1byte,不計入len長度
如果修改後SDS長度(len)大於等於1MB,分配1MB未使用空間,實際長度變成len(SDS修改後實際長度)+1MB(未使用空間)+1byte,1byte就是\0
多分配的未使用空間使增長n次字串需要的記憶體重分配次數從必定n次變成最多n次
3.3.2.惰性空間釋放(減)
用於優化SDS字串縮短操作
需要縮短SDS儲存的字串時,不立即使用記憶體重分配縮短多餘位元組,而是使用free記錄多餘位元組等以後使用.
sdstrim(s,"xy")==>a,b,c,\0移動到最左,x,y空間清空,保留未使用空間.len=3,free=9,\0
以後擴增SDS時,如果free足夠,不需要執行記憶體重分配,直接使用未使用空間.
3.4 二進位制安全
c語言字串不能存圖形等,如果包含空字元,會被認為字串結尾,限制C語言字串不能存圖片等.
SDS處理存放在buf陣列中的資料不會做任何改變,所以buf屬性被稱為位元組陣列--->redis不是用buf位元組陣列儲存字元,而是儲存二進位制資料.
1.儲存二進位制資料,存入資料不變化.
2.用len屬性而不是空字串或者判斷字串是否結束
4.SDS API
sdsnew:建立包含給定c字串的sds
sdsempty:建立空sds
sdsfree:釋放sds
sdslen:返回len(byte數)
sdsavail:返回free(byte)
sdsdup:建立給定sds副本(copy)
sdsclear:清空sds中字串
sdscat:將給定c字串拼接到sds字串末尾
sdscatsds:將給定sds字串拼接到sds字串末尾
sdscpy:將給定c字串複製到sds,覆蓋sds原有字串
sdsgrowzero:用空字串將sds擴充套件到給定長度
sdsrange:保留sdsd給定區間內資料,不在區間內資料會被覆蓋或清除
sdstrim:從給定sds左右兩端移除所有在給定c字串中出現過的字元
sdscmp:對比兩個sds字串是否相同