Redis构建了自己的类型系统
- redisObject 对象
- 基于 redisObject 对象的类型检查
- 基于 redisObject 对象的显式多态函数
- 对 redisObject 进行分配、共享和销毁的机制
RedisObject 编码结构
redisObject 是 Redis 类型系统的核心,数据库中的每个键、值,以及 Redis 本身处理的参数,都表示为这种数据类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
typedef struct redisObject { unsigned type:4; unsigned encoding:4; unsigned lru:REDIS_LRU_BITS; int refcount; void *ptr; } robj;
|
说明:type 、 encoding 和 ptr 是最重要的三个属性
- type(类型)
- REDIS_STRING 0 // 字符串
- REDIS_LIST 1 // 列表
- REDIS_SET 2 // 集合
- REDIS_ZSET 3 // 有序集
- REDIS_HASH 4 // 哈希表
- encoding(编码)
- REDIS_ENCODING_RAW 0 // 编码为字符串
- REDIS_ENCODING_INT 1 // 编码为整数
- REDIS_ENCODING_HT 2 // 编码为字典
- REDIS_ENCODING_ZIPMAP 3 // 编码为 zipmap
- REDIS_ENCODING_LINKEDLIST 4 // 编码为双端链表
- REDIS_ENCODING_ZIPLIST 5 // 编码为压缩列表
- REDIS_ENCODING_INTSET 6 // 编码为整数集合
- REDIS_ENCODING_SKIPLIST 7 // 编码为跳跃表
- ptr
- 指向实际保存值得数据结构
- 这个数据结构由type属性 和encoding 属性决定
- 例子
- redisObject 的 type属性为REDIS_LIST,encoding属性为REDIS_ENCODING_LINKEDLIST
- 那么这个对象就是一个Redis列表,它的值保存在一个双端链表内,而ptr指针就指向这个双端链表
命令的类型检查和多态
当执行一个处理数据类型的命令时,Redis执行以下步骤:
- 根据给定 key ,在数据库字典中查找和它像对应的 redisObject ,如果没找到,就返回NULL 。
- 检查 redisObject 的 type 属性和执行命令所需的类型是否相符,如果不相符,返回类型错误。
- 根据 redisObject 的 encoding 属性所指定的编码,选择合适的操作函数来处理底层的数据结构。
- 返回数据结构的操作结果作为命令的返回值
REDIS_ENCODING_INT
如果一个字符串对象保存的是整数值,并且这个整数值可以用long类型来表示,那么字符串对象会将整数值保存在字符串对象结构 的ptr属性里面(将void* 转换成long),并将字符串对象的编码设置为int。
REDIS_ENCODING_RAW
如果字符串对象保存的是一个字符串字符串值, 并且这个字符串值的长度大于32字节,那么字符串对象将使用一个简单动态字符串(SDS) 来保存这个字符串值,并将 对象的编码设置为raw。
REDIS_ENCODING_EMBSTR
如果字符串对象保存的是一个字符串值,并且这个字符串值的长度小于等于32字节,那么字符串对象将使用embstr编码的方式来保存这个字符串值。
- embstr 编码将创建字符串对象所需的内存分配次数从raw编码的两次降低为一次。
- 释放embstr编码的字符串对象只需要调用一次内存释放函数,而释放raw编码的字符串对象需要调用两次内存释放函数。
- 因为embstr编码的字符串对象的所有数据都保存在一块连续的内存里面,所以这种编码的字符串对象比起raw编码的字符串对象能够 更好地利用缓存带来的优势。
REDIS_ENCODING_ZIPLIST
当列表对想可以同时满足以下两个条件时,列表对象使用ziplist编码:
- 列表对象保存的所有字符串元素的长度都小于64字节
- 列表对象保存的元素数量小于512个,不能满足这两个条件的列表对象需要使用linkedlist编码
当哈希对象可以同时满足以下两个条件时,哈希对象使用ziplist编码
- 哈希对象保存所有键值对的键和值的字符串长度都小于64字节
- 哈希对象保存的键值对梳理小于512个,不能满足这两个条件的哈希对象需要使用hashtable编码
REDIS_ENCODING_LINKEDLIST
REDIS_ENCODING_HT
REDIS_ENCODING_SKIPLIST
REDIS_ENCODING_INTSET
当集合对象可以同事满足以下两个条件时,对象使用intset编码:
- 集合对象保存的所有元素都是整数值
- 集合对象保存的元素数量不超过512个
不满足这两个条件的集合对象需要使用hashtable编码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
#define REDIS_STRING 0 #define REDIS_LIST 1 #define REDIS_SET 2 #define REDIS_ZSET 3 #define REDIS_HASH 4
#define REDIS_ENCODING_RAW 0 #define REDIS_ENCODING_INT 1 #define REDIS_ENCODING_HT 2 #define REDIS_ENCODING_ZIPMAP 3 #define REDIS_ENCODING_LINKEDLIST 4 #define REDIS_ENCODING_ZIPLIST 5 #define REDIS_ENCODING_INTSET 6 #define REDIS_ENCODING_SKIPLIST 7 #define REDIS_ENCODING_EMBSTR 8
|
Redis 数据结构和常见数据结构的实现对比
|
分类(常见算法) |
Redis(实现算法) |
是否有序 |
运行元素重复 |
字符串 |
字符串 |
String——(整数,动态字符串) |
* |
* |
列表 |
列表——(数组/链表) |
List——(双向链表,压缩列表) |
是 |
是 |
集合 |
集合——(基于字典) |
Set ——(基于字典,数组) |
否 |
否 |
有序集合——(二叉树) |
Zset——(跳跃表,压缩列表) |
是 |
|
|
字典 |
字典——(哈希表) |
Hash——(哈希表,压缩列表) |
否 |
key不能重复 |
有序字典——(二叉树) |
* |
是 |
|
|
数据结构
simple dynamic string(SDS)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
struct sdshdr { int len; int free; char buf[]; };
struct __attribute__ ((__packed__)) sdshdr5 { unsigned char flags; char buf[]; };
struct __attribute__ ((__packed__)) sdshdr8 { uint8_t len; uint8_t alloc; unsigned char flags; char buf[]; }; 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[]; }; struct __attribute__ ((__packed__)) sdshdr64 { uint64_t len; uint64_t alloc; unsigned char flags; char buf[]; };
|
压缩列表-ziplist
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
typedef struct ziplist{ uint32_t zlbytes; uint32_t zltail; uint16_t zllen; unsigned char* entryList[]; unit8_t char zlend; }ziplist;
typedef struct zlentry { unsigned int prevrawlensize, prevrawlen;
unsigned int lensize, len; unsigned int headersize;
unsigned char encoding; unsigned char *p; }zlentry;
|
双端链表-list
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
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;
typedef struct listNode { struct listNode *prev; struct listNode *next; void *value; } listNode;
|
字典-DICT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
typedef struct dict { dictType *type; void *privdata; dictht ht[2]; int rehashidx; int iterators; } dict;
typedef struct dictht { dictEntry **table; unsigned long size; unsigned long sizemask;
unsigned long used; } dictht;
typedef struct dictEntry { void *key; union { void *val; uint64_t u64; int64_t s64; } v; struct dictEntry *next; } dictEntry;
|
整数集合-intset
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
typedef struct intset { uint32_t encoding; uint32_t length; int8_t contents[]; } intset;
#define INTSET_ENC_INT16 (sizeof(int16_t)) #define INTSET_ENC_INT32 (sizeof(int32_t)) #define INTSET_ENC_INT64 (sizeof(int64_t))
|
跳跃表-skiplist
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
typedef struct zset { dict *dict; zskiplist *zsl; } zset;
typedef struct zskiplist { struct zskiplistNode *header, *tail; unsigned long length; int level; } zskiplist;
typedef struct zskiplistNode { robj *obj; double score; struct zskiplistNode *backward; struct zskiplistLevel { struct zskiplistNode *forward; unsigned int span; } level[]; } zskiplistNode;
int zslRandomLevel(void) { int level = 1; while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF)) level += 1; return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL; }
|