本博客将顺着自顶向下的思路梳理一下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(); } }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对象的数据结构编码切换方式也与之类似。
底层数据结构指的是ptr指向的对象的内部结构,在Redis中,包含6种底层数据结构:
SDS动态字符串
ziplist压缩列表
list链表
dict字典
skiplist跳跃表
intset整数集合
熟悉Redis的同学来说,这些都是耳熟能详的数据结构,就不一一去介绍源码了,项目daredis中都有具体实现。
之前写过一篇介绍跳跃表的博客,也可以看看:
【Redis】跳跃表原理分析与基本代码实现(java)
对象系统与各种底层数据结构映射关系如下: