1. 程式人生 > 資料庫 >Redis字串原理的深入理解

Redis字串原理的深入理解

前言

來掘進都有兩年多了一直當個小透明,今天終於發一次文章了.

最近在看 Redis,感覺收穫很多,寫篇部落格記錄一下.

Redis 有五種基礎資料結構:string,list,set,zset,hash.其中 string是最最最簡單的也是最常用的.這個資料型別雖然簡單但是內部的結構設計卻很是精緻.

基本介紹

相比於 Java,在 Redis 中 string 是可以修改的,是動態字串(Simple Dynamic String 簡稱 SDS)他的內部結構更像是一個 ArrayList,維護一個位元組陣列並預分配冗餘空間以減少記憶體的頻繁分配.當字串的長度小於 1MB時,每次擴容都是加倍現有的空間,如果字串長度超過 1MB 時,每次擴容時只會擴充套件 1MB 的空間.

ps:字串長度為最大長度 512MB.

> set name test
OK
> get name
"test"
> mset name1 test1 name2 test2
OK
> mget name1 name2
1) "test1"
2) "test2"
> del name
(integer) 1

上面是字串的基本操作 命令mset 和 mget 可以對多個字串讀寫 節省網路開銷

不僅如此redis 的字串還可以用來儲存整數(更不像Java 的字串了),並且可以自增操作.字串儲存整數型別的的範圍在 至
如果儲存的數大於這個取值範圍就會變成普通字元型別 無法自增操作.這將由字串編碼格式決定.

字串由多個位元組組成,每個位元組有 8bit.這樣的資料結構還可以當做 bitmap 去使用.

> set foo 1
OK
> get foo 
"1"
> incr foo
(integer) 2
> get foo
"2"

內部原理

基本實現

上圖所示為字串的基本結構,其中 content 裡面儲存的是字串內容,和 c 一樣用 0x\0作為結束字元.這個結束字元不會被計算len 中.程式碼如下:

struct SDS{
  T capacity;		//陣列容量
  T len;			//實際長度
  byte flages;	//標誌位,低三位表示型別
  byte[] content;	//陣列內容
}

可以看到 capacity和len 都是泛型,為什麼不直接使用 int 呢?因為 Redis 內部做了很多優化,為了減少記憶體的使用不同長度的字串會使用不同的資料型別去表示.並且在建立字串的時候 len 會和 capacity 一樣大,沒有冗餘的空間,因為修改字串的場景很少.(Redis 真的將記憶體優化到了極致)

編碼格式

Redis 字串編碼格式有這麼幾種:int 編碼、embstr編碼和raw 編碼 下面就詳細介紹下這幾種編碼的區別.

在這之前先要說說RedisObject. Redis 的物件頭,所有的 Redis 物件都有下面這個頭部結構.

struct RedisObject{
  int4 type;		//資料型別 5 種
  int4 encoding;	//鍵值內部編碼格式 int 或 embstr 等等
  int24 lru;		// 當記憶體超限時採用LRU演算法清除記憶體中的物件
  
  int32 refcount;	//改鍵值被引用的數量
  void *ptr;		//物件內容
}

int 編碼

當儲存的值是64 位有符號整數型別的時候將會採用 int 編碼,這時可以使用鍵值自增操作.Redis 在啟動時會建立1w 個redisObject共享物件下文會講到,值在[0,1000)之間.如果存入整數的值在[0,1000)中Redis將不會建立新的物件,而是直接指向共享物件,鍵值不額外佔用空間.

使用 object encoding命令可以檢視編碼格式 使用 debug object命令可以檢視更多資訊

> set foo 1
OK
> object encoding foo
"int"
> set foo2 1
OK
> debug object foo
Value at:0x7f44b020aca0 refcount:2147483647 encoding:int serializedlength:2 lru:14691591 lru_seconds_idle:72588
> debug object foo2
Value at:0x7f44b020aca0 refcount:2147483647 encoding:int serializedlength:2 lru:14691591 lru_seconds_idle:72594

可以看到 foo 和 foo2 都在0x7f44b020aca0這裡指向的是同一個物件

embstr 編碼

當儲存的字串長度較短時(len<=44 位元組),Redis將會採用 embstr 編碼.embstr 即embedded string 嵌入式的字串.將SDS結構體嵌入RedisObject物件中,使用 malloc 方法一次分配記憶體地址是連續的.

如圖所示:

raw 編碼

當儲存的字串長度較長時(len>44 位元組),Redis 將會採用 raw 編碼,和 embstr 最大的區別就是 RedisObject 和 SDS 不在一起了,記憶體地址不再連續了.

如圖所示:

思考

為什麼字串會有兩種格式 embstr 和格式和 raw分界線是 44 個位元組?

Redis 預設的記憶體分配器jemalloc分配記憶體大小的單位是次方,為了容納一個完整的 embstr 物件,最少會分配 32 位元組的空間,再長些就是 64 位元組,再之後就認為這是一個大字串不適合用 embstr 儲存,而改用 raw 編碼了.

那麼問題來了,64 位元組的空間字串長度是多少呢?答案就是 44 位元組.

下圖中 content 的長度為 45 位元組減去結尾的 0x\0,就剩下 44 位元組了.


總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對我們的支援。