redis沒有直接使用c語言傳統的字串表示(空字元結尾的字元陣列), 而是自己構建了一種名為簡單動態字串的抽象型別, 並將sds用作redis的預設字串表示
在redis裡面,c字串只會作為字串字面量, 用在一些無須對字串值進行修改的地方,比如列印日誌.
但當可能會修改字串值時,就需要sds來實現字串, 比如,包含字串值的鍵值對在底層就是由sds實現的.
除此之外, sds還被用作緩衝區 : aof模組中的aof緩衝區, 以及客戶端狀態中的緩衝區,都是由sds實現的.
struct sdshdr ;
1.常數複雜度獲取字串長度
因為c字串並不記錄自身的長度資訊,所以為了獲取乙個c字串的長度,必須遍歷整個字串,對遇到的每個字元進行計數,直到遇到代表字串結尾的空字元為止, 這個操作的複雜度為o(n)
而sds在len屬性中記錄了sds本身的長度,所以獲取乙個sds長度的複雜度為o(1)
設定和更新sds長度的工作是由sds的api在執行時自動完成的,使用sds無須手動修改長度
2.杜絕緩衝區溢位
除了獲取字串長度的複雜度高, c字串不記錄自身長度的另乙個問題是容易造成緩衝區溢位(buffer overflow), 比如說用的strcat函式,可以將src字串中的內容拼接到dest字串的末尾
char
*strcat
(char
*dest,
char
*src)
但如果在記憶體中, dest字串後面緊緊連線著另外乙個字串s, 如果呼叫此函式,就會導致dest溢位到原來s的位置, 導致s字串被意外修改
但sds的空間分配策略完全杜絕了發生緩衝區溢位的可能性; 當sds api需要對sds進行修改時, api會先檢查sds的空間是否滿足修改所需的要求, 如果不滿足,api會自動將sds的空間擴充套件至執行修改所需的大小, 然後才能執行實際的修改操作
3.減少修改字串時帶來的記憶體重分配次數
c語言修改字串時,比如拼接,或擷取,會進行記憶體重分配操作,擴充套件或釋放字串的空間.如果修改字串長度的情況不太常出現,那麼每次修改都執行一次記憶體充分配是可以接受的.
但是redis作為資料庫, 經常被用於速度要求嚴苛, 資料被頻繁修改的場合,如果每次修改字串就會執行一次記憶體重分配的話, 會影響系統的效率和效能
為了避免這種缺陷,sds通過未使用空間解除了字串長度和底層陣列長度的關聯; 在sds中, buf陣列的長度不一定就是字元數量加一, 陣列裡面可以包含未使用的位元組, 而這些位元組的數量就由sds的free屬性記錄
通過未使用空間,sds實現了空間預分配和惰性空間釋放兩種優化策略
在擴充套件sds空間之前, sds api會先檢查未使用空間是否足夠, 如果足夠的話, api就會直接使用未使用空間, 而無須執行記憶體重分配
4.二進位制安全
為了確保redis可以適用於各種不同的使用場景, sds的api都是二進位制安全的,所有sds api都會以處理二進位制的方式來處理sds存放在buf陣列裡的資料.
這也就是將sds的buf稱為位元組陣列的原因—redis不是用這個陣列來儲存字元, 而是用來儲存一系列二進位制資料; 例如: sds使用len屬性的值而不是空字元來判斷字串是否結束
雖然sds的api都是二進位制安全的, 但他們一樣遵循c字串以空字元結尾的管理, 這些api總會將sds儲存的資料的末尾設定為空字元, 並且總會為bubf陣列分配空間的時候多分配乙個位元組來容納這個空字元,這是為了讓那些儲存文字資料的sds可以重用一部分庫定義的函式
redis資料結構 SDS
在使用中,redis有五種物件 string hash list set sorted set 在redis中有以下幾種資料結構 sds 鍊錶 字典 跳躍表 整數集合 壓縮列表,它們在不同的條件下實現了redis的五種物件。先來看sds的結構 在sds.h中定義了幾種不同的結構用來存放不同型別的資料...
redis資料結構 SDS
sds結構體組成 struct sdshdr sds相比普通字串的好處 redis 只會使用c字串作為字面量,在大多數情況下,redis 使用sds dynamic string,簡單動態字串 作為字串表示。比起 c 字串,sds 具有以下優點 常數複雜度獲取字串長度。杜絕緩衝區溢位。減少修改字串長...
有趣的Redis 資料結構詳解 sds
我們知道redis是乙個鍵值對資料庫,當你執行如下命令時 set testkey testvalue 其中的鍵就是用sds dynamic string,簡單動態字串 來實現的,redis中string類的值也是用sds實現的 如上面的testvalue 我們來看一下sds的底層資料結構是啥?str...