我們知道redis資料庫是使用c語言寫的,然而其內部的字串的儲存卻並不是使用傳統的c語言字串表示,而是使用一種名為簡單動態字串(****** dynamic string,sds)的抽象資料型別。
首先我們來對sds有乙個大概的認識
如果我們客戶端執行如下命令
127.0.0.1:6381> set msg "hello world"
那麼redis將會在資料庫中建立乙個新的鍵值對,其中:
鍵值對的鍵是乙個字串物件,物件的底層的實現是乙個儲存著"msg"
的sds
同樣,鍵值對的值的底層的實現就是乙個儲存著"hello world"
的sds
sds除了用來儲存資料庫中的字串之外,sds還被用作緩衝區(buffer),如aof模組中的aof緩衝區,以及客戶端狀態中的輸入緩衝區接下來我們來看一看sds的定義
struct sdshdr;
其中buf陣列的大小等於len+free+1,其中加一的為末尾的標識 『\0』如下sds的儲存示例
其中free=3
就是還有三個位元組的空間未分配,len=5
就是儲存的字串redis
的長度,而我們可以很明顯的看出buf
的長度為free+len+1=9
那麼sds與傳統的c語言字串的區別有哪些呢?
首先由於sds的結構中有對應的free
和len
屬性來記錄相應的字串的長度,所以獲取sds字串長度的時間複雜度為o(
1),而傳統的c語言字串則需要遍歷一遍字串從而得到其長度(時間複雜度為o(
n))。
sds並不會使得緩衝區溢位
我們知道傳統的c語言字串會導致溢位,因為其不會檢查當前字元陣列剩餘空間的大小。
而sds的記憶體分配策略則解決了這一問題,比如說當我們使用sdscat()
函式在當前的字串後面拼接字串時,會檢查給定的空間是否夠用,如果不夠則會事先擴充sds的空間大小。
頻繁的記憶體分配和記憶體**就會帶來效能問題,為此,redis分別實現了空間預分配和惰性空間兩種優化策略。
空間預分配
空間預分配用於優化sds的字串的增長操作:當sds的api對乙個sds字串進行修改,並且此時sds的空間不夠的時候,系統不僅會為sds分配增長所必要的空間,還會分配額外的未使用的空間,其規則如下
我們拿上面的第一點規則進行圖示講解
如有如下的sds字串,其使用空間為5,未使用空間為3
現在使用sdscat(str," dbase")
進行拼接操作,這時候就需要分配額外空間
這時候分配記憶體後len=11
,所以系統就會再額外的分配11
位元組的未使用空間,於是free=11
惰性空間釋放
空間預分配用於優化sds的字串的縮短操作:當sds的api對乙個sds字串進行縮短時,程式並不會立即**多餘的未使用的空間,而是通過會增加free
屬性的值,將未使用的空間記錄下來,並等待將來使用。
我們使用sdstrim()
函式來做乙個示例
現在執行sdstrim(str,"xy")
,來移除str
中的所有'x'
和'y'
字元
這樣得到的結果為
看到上面我們就知道通過sdstrim(str,"xy")
縮短字串後多餘的8個位元組的空間並沒有被**掉,而是將free
的值加了8從而記錄下了多餘的空間,從而可以再次利用。
此外,sds還是二進位制安全的,因為sds字串是通過len
的大小判斷字串結束的(不是通過'\0'
),從而其中可以儲存'\0'
,這就是說其可以儲存任意格式的二進位制資料。
那麼為什麼還要在sds字串的末尾加上乙個'\0'
呢?
這是因為為了使sds字串相容部分的c語言字串函式,比如說中過的函式,從而避免了**的重複,達到重用的效果。
Redis字串的底層設計
redis的底層是用c語言來實現的,在c語言中字串的預設是以 0 標識結束,而redis並沒有採用這種傳統的方法表示,而是自己構建了一種簡單動態字串 sds 來表示。下面看一下什麼是sds,sds的 定義,以及在儲存乙個字串 redis 時sds和傳統c的表示分別是怎麼樣,使用sds的好處是什麼 s...
Redis的底層實現 字串章節
不要遺忘最初的目標。ruider 總結 about me redis的命令如下 set x hello get x helloredis作為一種儲存字串的快取結構,其具體實現是由c語言完成,在c語言中,字串是通過字元陣列實現的,即char,那麼redis對於字串的實現是不是也是基於字元陣列嗎?不是的...
Redis儲存型別 字串
string 是 redis 最基本的型別,string 型別是二進位制安全的。意思是 redis 的 string 可以包含任何資料。比如jpg或者序列化的物件。string 型別是 redis 最基本的資料型別,string 型別的值最大能儲存 512mb。設定指定 key 的值 set 獲取指...