面試官:你說說redis經常使用數據結構有哪些呢?面試
面試者:String(字符串),list(鏈表),hash(哈希),set(集合),zset(有序集合)redis
面試官:那你說說redis的string的底層具體實現呢?json
面試者:額?數組
首先咱們知道redis全部的數據結構都是以一個惟一的key來做爲名稱,而後經過這個惟一的key去獲取相應的value數據,不一樣的數據結構的差異就在於value的數據結構不同。緩存
今天咱們就先從最簡單的string來作一些深刻的瞭解。字符串的使用很是的普遍,好比咱們常常用來緩存用戶的信息,將用戶的信息經過JSON序列化成一個字符串,而後經過用戶的id或者code來將序列化之後的json字符串緩存到redis中,當要使用的時候將json字符串從redis中獲取到,再通過一次反序列化之後就能夠獲取到用戶的信息了。 網絡
一:string相關的經常使用命令數據結構
二:上面咱們對redis的string數據結構的使用和常見命令有了一些瞭解以後,咱們接下來就能夠對string的底層數據結果作一些深刻了解了。分佈式
struct SDS<T> { T capacity; // 數組分配的容量 T len; // 實際數據的長度 byte flags; byte[] content; // 真的的數據是存在這個字節數組裏面的。函數
}spa
capacity表示的是當前分配的字節數組的長度,而len表示的是當前字符串的真實長度,當初始化的時候,capacity和len是同樣大的,可是在後期進行分配的時候,就會出現下圖的狀況capacity大於len,具體緣由是當字符串的長度小於1M的時候,容量增長都是加倍現有的空間,當實際長度超過1M之後,實際擴容的時候,只會增長1M的空間,爲何這麼操做呢?是由於若是數組沒有多餘的冗餘空間,那麼當在追加內容的時候,就必需要分配新的數組,而且將舊數組的內容拷貝過去,再追加內容,這樣就分配內存和複製的開銷就會比較大,因此採起了一種空間換時間的作法。可是須要咱們注意的是,字符串的容量最大不能超過512M。
上面咱們說到的是redis的string的數據結構,其實只是它的具體數據的存儲結構,對於redis來講,每種數據結構都有一個相同的結構頭,結構頭的具體內容以下:
struct RedisObject { int4 type; // 4bits,用來表示不一樣的對象,好比字符串,鏈表,集合,哈希,有序集合等 int4 encoding; // 4bits ,用來表示不一樣的存儲結構,好比一樣的都是字符串,
可是若是字符串的大小不等,那麼結構也會有些差異 int24 lru; // 24bits 用來記錄當前對象的lru信息 int32 refcount; // 4bytes 對象的引用計數,
當對象的引用計數爲0的時候,對象就會被銷燬,內存被回收 void *ptr; // 8bytes,64-bit system} robj; //指向實際存儲數據的指針,
好比指向咱們上面提到的那個sds
那麼爲何要設計一個對象頭呢?實際上是由於,咱們對於一樣的數據結構,
若是它存儲的內容的大小不同,咱們須要有不一樣的存儲方式,存儲方式須要有個地方
來保存,那麼咱們天然就想到設計一個公共頭,來保存這些信息,可能這麼說,
你們不是很理解,不要緊我給你們舉個實際例子,一下就明白了。
咱們看到上面,咱們都是string數據結構,可是當咱們的vlaue的大小不同的時候,conten的內容不同,表示存儲的結構不同。
從上圖咱們能夠看到,embstr這種存儲結構,是將對象頭和數據部分直接分配在一個連續內存空間裏面的,而raw這種數據結構,是分配了兩個內存空間,它們在內存上是不連續的。那麼問題來了?何時使用embstr存儲格式,何時使用raw存儲格式呢?答案下面揭曉。
首先咱們先計算一下即便不存儲任何內容的embstr字符串須要多少內存呢?RedisObject+SDS,首先RedisObject須要(4+4+24+32+64)/8=16個字節,而SDS即便不存儲任何內容也須要3個字節的內容,最少也須要16+3=19個字節.其次咱們知道操做系統使用jmalloc和tmalloc進行內存的分配,而內存分配的單位都是2的N次方,因此是2,4,8,16,32,64等字節,爲了可以容納一個embstr,因此操做系統最少也要分配32個字節的空間,可是這樣的話,可以存儲的內容也太少了,根本知足不了平常使用,因此默認直接分配64個字節,在redis中,若是一個總體佔用超過了64個字節空間的字符串,redis就認爲是一個大字符串,使用raw格式了,那麼在64個字節的狀況下,除去其餘部分佔用的19個字節,還剩下45個字節可以使用,可是redis方便glibc函數的使用,以及方便調試和打印,在字符串內容的末尾加上了一個\0的佔位符,因此實際可以使用的只有44個字節。因此咱們得出結論,embstr可以最多存儲4個字符串的內容。固然這些都是基於redis4.0版本獲得的,其餘版本由於結構不同,可能獲得的結論不同,可是上面的內容你們不用刻意去記憶,瞭解一下就好。
三:總結一下,redis的string底層數據結構使用的是sds,可是sds有兩種存儲方式,一種是embstr,一種是raw,它們的區別在於,是否和redisObject對象頭分配在一個連續的空間裏面。當總體佔用空間超過1M的時候,使用raw格式,當小於1M的時候使用embstr,而embstr最多可以存儲44個字節的內容。固然對於redis來講,string的value最大不能超過512M。