redis定義
redis是一個(gè)KV(鍵值)類型的數(shù)據(jù)庫。redis的值可以有多種類型(string,array,list),鍵總是字符串對(duì)象惨好。
字符串對(duì)象
一個(gè)字符串對(duì)象里如果包含了字符串值啃勉,那么這個(gè)字符串的類型稱為sds類型。(對(duì)象里可能還會(huì)有l(wèi)ong類型的值双妨,那個(gè)就不是sds類型)淮阐。
SDS類型
SDS(Simple Dynamic String)簡單動(dòng)態(tài)字符串,是C語言char*的替代品。能高效地支持長度計(jì)算和追加(append)這兩種操作刁品。
SDS類型高效的原因
從數(shù)據(jù)結(jié)構(gòu)來進(jìn)行分析
struct sdshdr {
// buf 已占用長度
int len;
// buf 剩余可用長度
int free;
// 實(shí)際保存字符串?dāng)?shù)據(jù)的地方
char buf[];
};
首先我們發(fā)現(xiàn)里結(jié)構(gòu)體里面有存儲(chǔ)長度字段泣特,那么它取長度的時(shí)間復(fù)雜度就是O(1),而C原生的長度查詢依賴strlen(),復(fù)雜度是O(N)挑随。又因?yàn)椴灰蕾嘋的函状您,直接取長度,所以說它是二進(jìn)制安全的兜挨。
二進(jìn)制安全(不對(duì)數(shù)據(jù)做解析膏孟,將數(shù)據(jù)當(dāng)作二進(jìn)制數(shù)據(jù)流處理。如C的char*拌汇,會(huì)將\0解析成結(jié)尾柒桑。而二進(jìn)制安全則不進(jìn)行任何轉(zhuǎn)義)。)
其次我們發(fā)現(xiàn)噪舀,C原生對(duì)字符串進(jìn)行 N 次追加魁淳,必定需要對(duì)字符串進(jìn)行 N 次內(nèi)存重分配(realloc)。而SDS結(jié)構(gòu)里帶有free和buf与倡,會(huì)在APPEND的時(shí)候給buf一些額外的空間界逛,并將可用內(nèi)存空間記錄在free里。這樣下次APPEND的時(shí)候纺座,就可以直接進(jìn)行追加息拜,不涉及內(nèi)存重新分配。
關(guān)于buf的空間分配規(guī)則:在目前版本的 Redis 中比驻, SDS_MAX_PREALLOC 的值為 1024 * 1024 该溯, 也就是說, 當(dāng)大小小于 1MB 的字符串執(zhí)行追加操作時(shí)别惦, sdsMakeRoomFor 就為它們分配多于所需大小一倍的空間狈茉; 當(dāng)字符串的大小大于 1MB , 那么 sdsMakeRoomFor 就為它們額外多分配 1MB 的空間掸掸。
優(yōu)化:將SDS_MAX_PREALLOC的值調(diào)低氯庆。
優(yōu)點(diǎn):節(jié)省內(nèi)存蹭秋,比如調(diào)整到一半512kb,那么每多一個(gè)APPEND超過512kb的操作,就會(huì)節(jié)省超出512kb部分的內(nèi)存空間出來堤撵。
缺點(diǎn):可能的性能損耗(如果再次APPEND超過512kb的時(shí)候仁讨,會(huì)不走buf,進(jìn)行內(nèi)存重分配)
所以還是要結(jié)合業(yè)務(wù)实昨,根據(jù)APPEND的頻率和大小來選出最適合的buf空間洞豁。
buf的內(nèi)存回收規(guī)則:可以修改配置,定時(shí)釋放(或者重啟redis會(huì)自動(dòng)釋放)
思考:
所有處理 sdshdr 的函數(shù)荒给,都必須正確地更新 len 和 free 屬性丈挟,否則就會(huì)造成 bug。(這里就會(huì)導(dǎo)致redis暫時(shí)只能單線程執(zhí)行志电,如果多線程的話曙咽,要考慮len和free在并發(fā)操作下的一致性)。