前言
來掘進都有兩年多了一直當個小透明,今天終于發一次文章了.
最近在看 Redis,感覺收獲很多,寫篇博客記錄一下.
Redis 有五種基礎數據結構:string,list,set,zset,hash.其中 string是最最最簡單的也是最常用的.這個數據類型雖然簡單但是內部的結構設計卻很是精致.
基本介紹
相比于 Java,在 Redis 中 string 是可以修改的,是動態字符串(Simple Dynamic String 簡稱 SDS)他的內部結構更像是一個 ArrayList,維護一個字節數組并預分配冗余空間以減少內存的頻繁分配.當字符串的長度小于 1MB時,每次擴容都是加倍現有的空間,如果字符串長度超過 1MB 時,每次擴容時只會擴展 1MB 的空間.
ps:字符串長度為最大長度 512MB.
> set name testOK> get name"test"> mset name1 test1 name2 test2OK> mget name1 name21) "test1"2) "test2"> del name(integer) 1
上面是字符串的基本操作 命令mset 和 mget 可以對多個字符串讀寫 節省網絡開銷
不僅如此redis 的字符串還可以用來儲存整數(更不像Java 的字符串了),并且可以自增操作.字符串保存整數類型的的范圍在 至
如果保存的數大于這個取值范圍就會變成普通字符類型 無法自增操作.這將由字符串編碼格式決定.
字符串由多個字節組成,每個字節有 8bit.這樣的數據結構還可以當做 bitmap 去使用.
> set foo 1OK> 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 1OK> object encoding foo"int"> set foo2 1OK> debug object fooValue at:0x7f44b020aca0 refcount:2147483647 encoding:int serializedlength:2 lru:14691591 lru_seconds_idle:72588> debug object foo2Value 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 字節了.
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對武林網的支持。
|
新聞熱點
疑難解答