数据类型 | 数据结构 | 数据结构 | 应用场景 |
---|---|---|---|
String | SDS | 简单动态字符串 | 存储字符串信息 |
List | Linkedlist、Ziplist | 双向链表、压缩列表 | 消息队列或栈的实现 |
Hash | Hashtable、Ziplist | 哈希表、压缩列表 | 存储键值对信息 |
Set | Hashtable、Intset | 哈希表、整数集合 | 存储非重复元素 |
Zset | Ziplist、SkipList | 压缩列表、跳表 | 热搜榜、基于Timeline的排序 |
Geospatial | —— | —— | 用于获取地理位置信息 |
Hyperloglog | —— | —— | 统计基数个数 |
Bitmap | —— | —— | 活跃(非活跃)、在线(离线)等只有两种状态的情景 |
-
SDS:简单动态字符串
-
INT:使用整数数值实现的字符串对象
-
RAW:使用简单动态字符串实现的字符串对象
-
EMBSTR:使用embstr编码的简单动态字符串实现的字符串对象
一、SDS
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len;
uint16_t alloc;
unsigned char flags;
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len;
uint32_t alloc;
unsigned char flags;
char buf[];
};
二、Linkedlist
1.链表结点
typedef struct listNode {
//前置节点
struct listNode *prev;
//后置节点
struct listNode *next;
//节点的值
void *value;
} listNode;
2.链表结构
typedef struct list {
//链表头节点
listNode *head;
//链表尾节点
listNode *tail;
//节点值复制函数
void *(*dup)(void *ptr);
//节点值释放函数
void (*free)(void *ptr);
//节点值比较函数
int (*match)(void *ptr, void *key);
//链表节点数量
unsigned long len;
} list;
三、Ziplist
- zlbytes,记录整个压缩列表占用的内存字节数;
- zltail,记录压缩列表「尾部」节点的偏移量;
- zllen,记录压缩列表包含的节点数量;
- zlend,标记压缩列表尾部的结束点,固定值 0xFF(十进制255)
- prevlen,记录「前一个节点」的长度;
- encoding,记录当前节点的数据类型以及长度;
- data,保存当前节点的实际数据;
四、Hashtable
1.哈希表结点
typedef struct dictEntry {
//键值对中的键
void *key;
//键值对中的值
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
//指向下一个哈希表节点,形成链表
struct dictEntry *next;
} dictEntry;
2.哈希表结构
typedef struct dictht {
//哈希表数组
dictEntry **table;
//哈希表大小
unsigned long size;
//哈希表大小掩码,用于计算索引值
unsigned long sizemask;
//该哈希表已有的节点数量
unsigned long used;
} dictht;
五、Intset
typedef struct intset {
//编码方式
uint32_t encoding;
//集合包含的元素数量
uint32_t length;
//保存元素的数组
int8_t contents[];
} intset;
六、Skiplist
1.跳表结点
typedef struct zskiplistNode {
//Zset 对象的元素值
sds ele;
//元素权重值
double score;
//后向指针
struct zskiplistNode *backward;
//节点的level数组,保存每层上的前向指针和跨度
struct zskiplistLevel {
struct zskiplistNode *forward;
unsigned long span;
} level[];
} zskiplistNode;
2.跳表结构
typedef struct zskiplist {
struct zskiplistNode *header, *tail;
unsigned long length;
int level;
} zskiplist;
七、Quicklist
在 Redis 3.0 之前,List 对象的底层数据结构是双向链表或者压缩列表。在 Redis 3.2 的时候,List 对象的底层改由 quicklist 数据结构实现。其实 quicklist 就是「双向链表 + 压缩列表」组合,因为一个 quicklist 就是一个链表,而链表中的每个元素又是一个压缩列表。
1.quicklist结点
typedef struct quicklistNode {
//前一个quicklistNode
struct quicklistNode *prev; //前一个quicklistNode
//下一个quicklistNode
struct quicklistNode *next; //后一个quicklistNode
//quicklistNode指向的压缩列表
unsigned char *zl;
//压缩列表的字节大小
unsigned int sz;
//压缩列表的元素个数
unsigned int count : 16; //ziplist中的元素个数
....
} quicklistNode;
2.quicklist结构
typedef struct quicklist {
//quicklist的链表头
quicklistNode *head; //quicklist的链表头
//quicklist的链表头
quicklistNode *tail;
//所有压缩列表中的总元素个数
unsigned long count;
//quicklistNodes的个数
unsigned long len;
...
} quicklist;
八、Listpack
quicklist 虽然通过控制 quicklistNode 结构里的压缩列表的大小或者元素个数,来减少连锁更新带来的性能影响,但是并没有完全解决连锁更新的问题。因为 quicklistNode 还是用了压缩列表来保存元素,压缩列表连锁更新的问题,来源于它的结构设计,所以要想彻底解决这个问题,需要设计一个新的数据结构。于是,Redis 在 5.0 新设计一个数据结构叫 listpack,目的是替代压缩列表,它最大特点是 listpack 中每个节点不再包含前一个节点的长度了。
- encoding,定义元素的编码类型,会对不同长度的整数和字符串进行编码
- data,存储的数据
- len,encoding+data字段的长度