c語言沒有string類型,本質是char[]數組;而且c語言數組創建時必須初始化大小,指定類型後就不能改變,並且字元數組的最後一個元素總是空字 '\0' 。
以下顯示了一個值為 "Redis" 的C 字串:
#Redis沒有直接使用C語言的字串方式,而是建構了一種簡單動態字串(Simple dynamic string, SDS)的類型,Redis中的字串底層都是使用SDS結構進行存儲,例如包含字串的鍵值對底層都是使用SDS結構實現的。
SDS結構定義在sds.h中
struct sdshdr{ int len;//SDS保存的字符串长度 int free;//buf数组中未使用字节数量 char buf[];//字符数组,保存字符串 }
#最後一個位元組保存了空字元'\0',保留了C字串的規範,使得SDS結構的字串,可以重複使用一部分C函數庫的函數。
主要是因為C字串有以下缺點:
取得字串長度時間複雜度為O(N):C字串取得長度需遍歷整個字串,遇到'\0'空字元為止。如果在進行字串追加操作時沒有分配足夠的內存,就會發生緩衝區溢位。記憶體重分配:每次增長或截短字串,程式都要對保存C字串的數組進行記憶體重新分配操作,而記憶體重分配涉及複雜的演算法,並可能需要執行系統調用,所以它通常比較耗時。空字元問題:C字串中間不能保存空格,否則程式遍歷是會誤認為字串的結尾。由於此限制,C字串只能用於儲存文字數據,而不適用於保存二進位資料如圖片、音訊視訊和壓縮檔案等。
#1、SDS透過len屬性記錄了SDS長度,所以取得長度的時間複雜度為O( 1),即strlen指令的時間複雜度是O(1)。
2、SDS空間分配策略避免了緩衝區溢位:當SDS進行修改時,會先檢查SDS空間是否滿足修改,不滿足會自動擴展到所需大小,然後才執行修改。
3、較少修改字串時記憶體重新分配次數:SDS中的free記錄buf位元組數組中未使用的位元組。
redis透過free屬性實現空間預先分配、惰性空間釋放兩種最佳化策略。
空間預先分配:當對SDS進行成長操作時,程式不僅會分配修改所必須得空間,還會為SDS分配額外的未使用空間。記憶體重新分配次數在連續執行字串成長操作時得以減少,這是透過預先分配策略實現的。惰性空間釋放:當對SDS進行截短操作時,程式並不會立即回收縮短後多出來的位元組所佔用的內存,而是使用free屬性記錄多出來的位元組數,以供將來使用。未被使用的空間可能會在未來進行SDS成長時派上用場,此時成長作業不一定需要執行記憶體重新分配。
SDS結構中的buf位元組數組,是二進位安全的,不僅可以保存字符,也可以保存二進位資料。
SDS保留了C字串的慣例,將資料的末尾設定為空字元'\0',SDS中之所以保留這一規範是可以重用C字串函數庫的一部分函數,例如追加字串。
Redis string的三種編碼:
int 儲存8個位元組的長整數(long,2^63- 1 ) embstr, embstr格式的SDS (Simple Dynamic String) raw, raw格式的SDS,儲存大於44個位元組的長字串
int類型就是指的是數字,那麼raw、embstr都代表的是字串有什麼異同嗎,下面我們分析下。
圖中展示了兩者的區別,可以看到embstr將redisObject和SDS保存在連續的64位元組空間內,這樣可以只需要一次記憶體分配,而對raw來說,SDS和redisObject分離,需要兩次記憶體分配,而且佔用更多的記憶體空間。
可以看到embstr在3.2 中使用了叫做sdshdr8的結構,在這個結構下,元資料只需要3個位元組,而Redis需要8個位元組,所以總共64個字節,減去redisObject(16字節),再減去SDS的原信息,最後的實際內容就變成了44字節和39字節。
當字串小於等於 44 位元組時,Redis 就使用了嵌入式字串的建立方法,以此減少記憶體分配和記憶體碎片。
下面這張圖展示了createEmbeddedStringObject 創建嵌入式字串的過程:
總之,只要記住,Redis 會透過設計實現一塊連續的記憶體空間,把redisObject 結構體和SDS 結構體緊密地放在一起。
這樣一來,對於不超過 44 個位元組的字串來說,就可以避免記憶體碎片和兩次記憶體分配的開銷了。
SDS是Redis中一種高效的字串實作方式,它具有自動擴容、二進位安全性、O(1)長度取得和修改等優點。在實際的應用中,SDS可以幫助我們實現高效的字串操作,同時也可以避免一些常見的字串操作問題,例如緩衝區溢位等。透過深入了解SDS的內部結構與實作原理,我們可以更能理解Redis的底層機制,進一步提升我們的Redis應用能力。
以上是Redis中SDS簡單動態字串問題怎麼解決的詳細內容。更多資訊請關注PHP中文網其他相關文章!