原來Redis的五種數(shù)據(jù)類型數(shù)底層結(jié)構(gòu)是這樣的
在Redis中會涉及很多數(shù)據(jù)結(jié)構(gòu),比如SDS,雙向鏈表、字典、壓縮列表、整數(shù)集合等等。Redis會基于這些數(shù)據(jù)結(jié)構(gòu)自定義一個對象系統(tǒng),而且自定義的對象系統(tǒng)有很多好處。
通過對以下的Redis對象系統(tǒng)的學(xué)習(xí),可以了解Redis設(shè)計原理以及初衷,為了我們在使用Redis的時候,更加能夠理解到其原理和定位問題。
Redis 對象
Redis基于上述的數(shù)據(jù)結(jié)構(gòu)自定義一個Object 系統(tǒng),Object結(jié)構(gòu):
- redisObject結(jié)構(gòu):
- typedef struct redisObject{
- //類型
- unsigned type:4;
- //編碼
- unsigned encoding:4;
- //指向底層實現(xiàn)數(shù)據(jù)結(jié)構(gòu)的指針
- void *ptr;
- …..
- }
Object 系統(tǒng)包含五種Object:
- String:字符串對象
- List:列表對象
- Hash:哈希對象
- Set:集合對象
- ZSet:有序集合
Redis使用對象來表示數(shù)據(jù)庫中的鍵和值,即每新建一個鍵值對,至少創(chuàng)建有兩個對象,而且使用對象的具有以下好處:
1. redis可以在執(zhí)行命令前會根據(jù)對象的類型判斷一個對象是否可以執(zhí)行給定的命令
2. 針對不同的使用場景,為對象設(shè)置不同的數(shù)據(jù)結(jié)構(gòu)實現(xiàn),從而優(yōu)化對象的不同場景夏的使用效率
3. 對象系統(tǒng)還可以基于引用計數(shù)計數(shù)的內(nèi)存回收機制,自動釋放對象所占用的內(nèi)存,或者還可以讓多個數(shù)據(jù)庫鍵共享同一個對象來節(jié)約內(nèi)存。
4. redis對象帶有訪問時間記錄信息,使用該信息可以進行優(yōu)化空轉(zhuǎn)時長較大的key,進行刪除!
對象的ptr指針指向?qū)ο蟮牡讓蝇F(xiàn)實數(shù)據(jù)結(jié)構(gòu),而這些數(shù)據(jù)結(jié)構(gòu)由對象的encoding屬性決定,對應(yīng)關(guān)系:
每種Object對象至少有兩種不同的編碼,對應(yīng)關(guān)系:
String對象
字符串對象編碼可以int 、raw或者embstr,如果保存的值為整數(shù)值且這個值可以用long類型表示,使用int編碼,其他編碼類似。
比如:int編碼的String Object
- redis> set number 520
- ok
- redis> OBJECT ENCODING number
- "int"
String Object結(jié)構(gòu):
String 對象之間的編碼轉(zhuǎn)換
int編碼的字符串對象和embstr編碼的字符串對象在條件滿足的情況下,會被轉(zhuǎn)換為raw編碼的字符串對象。
比如:對int編碼的字符串對象進行append命令時,就會使得原來是int變?yōu)閞aw編碼字符串
List對象
list對象可以為ziplist或者為linkedlist,對應(yīng)底層實現(xiàn)ziplist為壓縮列表,linkedlist為雙向列表。
- Redis>RPUSH numbers “Ccww” 520 1
用ziplist編碼的List對象結(jié)構(gòu):
用linkedlist編碼的List對象結(jié)構(gòu):
List對象的編碼轉(zhuǎn)換:
當list對象可以同時滿足以下兩個條件時,list對象使用的是ziplist編碼:
1. list對象保存的所有字符串元素的長度都小于64字節(jié)
2. list對象保存的元素數(shù)量小于512個,
不能滿足這兩個條件的list對象需要使用linkedlist編碼。
Hash對象
Hash對象的編碼可以是ziplist或者hashtable
其中,ziplist底層使用壓縮列表實現(xiàn):
- 保存同一鍵值對的兩個節(jié)點緊靠相鄰,鍵key在前,值vaule在后
- 先保存的鍵值對在壓縮列表的表頭方向,后來在表尾方向
hashtable底層使用字典實現(xiàn),Hash對象種的每個鍵值對都使用一個字典鍵值對保存:
- 字典的鍵為字符串對象,保存鍵key
- 字典的值也為字符串對象,保存鍵值對的值
比如:HSET命令
- redis>HSET author name "Ccww"
- (integer)
- redis>HSET author age 18
- (integer)
- redis>HSET author sex "male"
- (integer)
ziplist的底層結(jié)構(gòu):
hashtable底層結(jié)構(gòu):
Hash對象的編碼轉(zhuǎn)換:
當list對象可以同時滿足以下兩個條件時,list對象使用的是ziplist編碼:
1. list對象保存的所有字符串元素的長度都小于64字節(jié)
2. list對象保存的元素數(shù)量小于512個,
不能滿足這兩個條件的hash對象需要使用hashtable編碼
Note:這兩個條件的上限值是可以修改的,可查看配置文件hash-max-zaiplist-value和hash-max-ziplist-entries
Set對象:
Set對象的編碼可以為intset或者hashtable
- intset編碼:使用整數(shù)集合作為底層實現(xiàn),set對象包含的所有元素都被保存在intset整數(shù)集合里面
- hashtable編碼:使用字典作為底層實現(xiàn),字典鍵key包含一個set元素,而字典的值則都為null
inset編碼Set對象結(jié)構(gòu):
- redis> SAD number 1 3 5
hashtable編碼Set對象結(jié)構(gòu):
- redis> SAD Dfruits “apple” "banana" " cherry"
Set對象的編碼轉(zhuǎn)換:
使用intset編碼:
1. set對象保存的所有元素都是整數(shù)值
2. set對象保存的元素數(shù)量不超過512個
不能滿足這兩個條件的Set對象使用hashtable編碼
ZSet對象
ZSet對象的編碼 可以為ziplist或者skiplist
ziplist編碼,每個集合元素使用相鄰的兩個壓縮列表節(jié)點保存,一個保存元素成員,一個保存元素的分值,然后根據(jù)分數(shù)進行從小到大排序。
ziplist編碼的ZSet對象結(jié)構(gòu):
- Redis>ZADD price 8.5 apple 5.0 banana 6.0 cherry
skiplist編碼的ZSet對象使用了zset結(jié)構(gòu),包含一個字典和一個跳躍表
- Type struct zset{
- Zskiplist *zsl;
- dict *dict;
- ...
- }
skiplist編碼的ZSet對象結(jié)構(gòu)
ZSet對象的編碼轉(zhuǎn)換
當ZSet對象同時滿足以下兩個條件時,對象使用ziplist編碼
1. 有序集合保存的元素數(shù)量小于128個
2. 有序集合保存的所有元素的長度都小于64字節(jié)
不能滿足以上兩個條件的有序集合對象將使用skiplist編碼。
Note:可以通過配置文件中zset-max-ziplist-entries和zset-max-ziplist-vaule