【Redis】內部數據結構自頂向下梳理
本部落格將順著自頂向下的思路梳理一下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)
對象系統與各種底層數據結構映射關係如下: