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