本部落格將順著自頂向下的思路梳理一下Redis的資料結構體系,從資料庫到物件體系,再到底層資料結構。我將基於我的一個專案的程式碼來進行介紹:daredis。該專案中,使用Java實現了Redis中所有的資料結構,思想與Redis大致類似,各種變數的命名與Redis原始碼基本一致,只是將結構體換成了類來實現。
Redis資料庫
Redis伺服器在初始化時,會建立一個db陣列,大小預設是16,即建立16個資料庫。如下所示:
public class RedisServer {
private static int dbNum = 16;
public static RedisDB[] db;
public static void init(){
db = new RedisDB[dbNum];
}
public static void initDB(int index){
db[index] = new RedisDB();
}
}
實際上,每個客戶端都會擁有一個目標資料庫,預設情況下為db[0]。客戶端執行命令時,目標資料庫會成為其操作物件。
RedisDB型別包含一個字典,程式碼如下:
public class RedisDB {
//資料庫的鍵空間
Dict<SDS, RedisObject> dict;
public RedisDB() {
this.dict = new Dict<>();
}
}
資料庫RedisDB實際上包含一個Dict型別,即字典(Redis中尤為關鍵的底層資料結構),是一個鍵值對集合,鍵名是SDS字串,鍵值是RedisObject。Dict是後面要講的一種底層資料結構,在資料庫體系中也是用到了Dict。
可以這樣理解,Redis的所有物件體系都是掛在一個Dict字典下的。這也體現了Redis非關係型的特點。
Redis物件系統
繼續向下探索,看一看資料庫鍵值RedisObject是什麼。RedisObject表示Redis中的物件。Redis包含五種物件,統稱物件系統
- RedisHash雜湊物件
- RedisList列表物件
- RedisSet集合物件
- RedisString字串物件
- RedisZSet有序集合物件
RedisObject包含一個型別欄位type和一個編碼欄位encoding,以及一個底層資料結構的引用ptr。如下所示:
public abstract class RedisObject {
protected int type;
protected int encoding;
protected RedisObj ptr;
}
type的值由一個列舉型別來維護,表示上述五種型別中的某種型別,如下所示
public enum RedisType {
STRING(0),
LIST(1),
HASH(2),
SET(3),
ZSET(4);
private final int val;
RedisType(int VAL) {
this.val = VAL;
}
public int VAL(){
return val;
}
}
encoding同樣由一個列舉型別來維護,表示ptr指向的資料結構的型別,如下所示
public enum RedisEnc {
RAW(0),
INT(1),
HT(2),
LINKEDLIST(3),
ZIPLIST(4),
INTSET(5),
SKIPLIST(6),
EMBSTR(7);
private final int val;
RedisEnc(int VAL) {
this.val = VAL;
}
public int VAL(){
return val;
}
}
RedisObject中的ptr引用的物件可以是多種型別。例如列表物件可由壓縮列表ziplist或者雙端連結串列linkedlist來編碼。兩種編碼可以轉換,當滿足以下兩個條件時,使用ziplist編碼
- 列表物件儲存的所有字串元素長度都小於64位元組;
- 列表物件儲存的元素數量小於512個;
兩個條件有一項不滿足,會將壓縮列表轉化為雙端連結串列。
其它Redis物件的資料結構編碼切換方式也與之類似。
Redis底層資料結構
底層資料結構指的是ptr指向的物件的內部結構,在Redis中,包含6種底層資料結構:
- SDS動態字串
- ziplist壓縮列表
- list連結串列
- dict字典
- skiplist跳躍表
- intset整數集合
熟悉Redis的同學來說,這些都是耳熟能詳的資料結構,就不一一去介紹原始碼了,專案daredis中都有具體實現。
之前寫過一篇介紹跳躍表的部落格,也可以看看:
【Redis】跳躍表原理分析與基本程式碼實現(java)
物件系統與各種底層資料結構對映關係如下: