Redis為kv的,而Redis底層又是由c語言寫成的,一切皆字典dict,和java的一切皆對象Object
Redis的key類型一般為字元串,value為redis類型RedisObject這裡的kv稱為dictEntry
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CM0UTN5QjYiVWOwcDZmRGOyYzX0AzN1cTM1IzLcVDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
相當與java中的Map<String, redisObject>
bitmap底層為String類型,hyperloglog底層為String,GEO底層為zset
文章目錄
- 1.上帝視角
- 2.disctEntry
- 3.redisObject
- 4.string的type和3大編碼轉換
- 5.Redis底層的資料結構
- 5.1.sds:simple dynamic string
- 5.2 hash
- 5.2.1 ziplist跳表
- 5.2.2 hashtable字典dict
- 5.3 quicklist
- 5.4 inset
- 5.5 skiplist跳表
1.上帝視角
redisServer -> redisDB -> dict -> dictht -> dictEntry -> {String, list, hash, set, zset}
從硬體,網絡到資料庫到内部資源表到資源表到落地是實體
2.disctEntry
上文的kv鍵值對,所有的key為String,所有的value為redisObject
3.redisObject
存在的結構體的内容為type,encoding,lru,refcount
type:string.list.set.zset.hash
encoding:目前值對象底層存儲的編碼類型
lru:采用lru算法清除記憶體的對象
refcount:記錄對象引用次數
4.string的type和3大編碼轉換
這裡set hello world,key為String,但是String是存儲在redis自定義的sds,動态字元串中,value為儲存在redisObject中
string的encoding:
1.int
2.raw
3.emstr
如果為數字的話encoding為int,如果不是數字的話為embstr,如果字元長度大于44的話為raw
長度0 ~ 19的話為int, 19 ~ 44 為str, >44 為raw
數字的話為int,不是數字為embstr,但是如果數字大于19 小于44的話為embstr。
全部的encoding:
如果指令行為set age 17的話底層的C語言:
set age 17
{
type:string
encoding:int
lru
refcount
}
5.Redis底層的資料結構
5.1.sds:simple dynamic string
sds.h
flags可以為sdshdr5,sdshdr8,sdshdr16,sdshdr32,sdshdr54
sds優點
1.記錄了長度資訊,strlen指令的開銷變小
2.alloc:空間預配置設定:
如果SDS占用的空間小于1MB,那麼每次擴充空間的時候會額外為SDS多配置設定一倍的空間,如果SDS占有的空間大于1MB,那麼每次擴充之後額外配置設定1MB。
惰性釋放指sds占用的空間縮小之後不會馬上回收,而是标記起來,等下次要用的時候就直接取消标記,這樣就省去了回收和重新配置設定的開銷。
3.SDS二進制安全,C字元串使用空字元來标志字元串結束,那麼就無法表示含有空字元的字元串,而SDS使用長度來标志結束,可以含有空字元。
4.防止了緩沖區溢出,如果使用C的字元數組儲存資料,那麼當字元串的長度超過數組時會覆寫掉後面的資料,造成溢出。而SDS在儲存字元串的時候會首先檢查空間是否足夠,不夠的話會先補足缺少的空間。
這裡的int.embstr.raw:
1.對于embstr來說,由于實作為隻讀,是以修改的話,會變為raw,無論超沒超過44位元組
2.一開始判斷是不是embstr或者raw,如果是的話,看長度是否大于20,如果不是則判斷是否配置maxmemory且調整[0, 10000],然後直接從共享數拿到。
3.為何有3種編碼格式,為的是極度的減少記憶體碎片
4.浮點數為emstr,raw
5.embstr:為嵌入式string,隻是記憶體+1位元組然後後面的記憶體嵌入到最後一位元組中
如果是raw的話則另外開辟記憶體,如果是int的話,不用額外開啟記憶體
5.2 hash
hash-max-ziplist-enties,使用壓縮清單儲存哈希集中最大元素個數,512
hash-max-ziplist-value,使用壓縮清單儲存哈希集中單個元素的最大長度,64byte
hash底層為ziplist+hashtable
當hash的數量小于hash-max-ziplist-enties,且字段小于hash-max-ziplist-value的時候為ziplist。
任意一個不滿足的話改為hashtable
ziplist轉為hashtable後不可以轉回ziplist
5.2.1 ziplist跳表
zip + list
雙向清單
壓縮清單,思想為為了多花時間換取空間,以部分讀寫為代價換取極高的記憶體空間使用率
可以看到,壓縮清單的格式很類似一些檔案的編碼方式,在檔案開頭有一些中繼資料,記錄資料區域的位置和資料的條數。這種緊湊的格式避免了記憶體對齊這樣的為了友善通路而引發的記憶體開銷,壓縮清單是在資料結構中元素較少時采取的資料結構,使用壓縮清單節約了記憶體開支,但是降低了通路的速度,不過對于規模較小的對象,這樣的損失可以忽略不記。
壓縮清單節點構成
前一個節點長度+encoding+entry-data
previous_entry_length
encoding
5.2.2 hashtable字典dict
字典類似于Java中的Map,Python中的dict,是一種儲存鍵值對的資料結構。Redis中的Hash由字典實作。其實Redis自身可以看成一個大的dict。
dict的實作如下:
table屬性是一個數組,裡面包含多個dictEntry,每個dictEntry儲存了一個鍵值對。下面是一個空的dict。
dictEntry是一個如下的結構體:
dictEntry裡面包含一個dictEntry*的next,可以看出,這裡是使用的拉鍊法來解決鍵沖突的,即hash值相同的鍵會儲存在一個連結清單中。最後在dictht之上,Redis還做了一層封裝,真正使用的dict是這樣的:
5.3 quicklist
list用quicklist存儲的,quicklist存儲一個雙端連結清單,每一個節點為一個ziplist
低版本的redis,list用的為ziplist+linkedlist
高版本的redis,list用的為quicklist,為ziplist+linkedlist
5.4 inset
set用的為inset和hashtable底層的。
如果元素都為整數的話為Insert,如果不是整數的話為hashtable
5.5 skiplist跳表
zset底層為skiplist和ziplist
當server:zset_max_ziplist_entries > 128 或 server:zset_max_ziplist_value > 64時候.使用跳躍表,否則使用ziplist
skiplist = list + 多級索引