本博客将顺着自顶向下的思路梳理一下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对象系统
继续向下探索,看一看数据库键值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)
对象系统与各种底层数据结构映射关系如下: