标签:blog ace user encode wstring 计数 port abs 快速
Redis中每个键值对都是由对象组成:
redis官方网站中对其数据类型的简单介绍:
An introduction to Redis data types and abstractions
摘抄一段关于redis key的介绍:
Redis keys
Redis keys are binary safe, this means that you can use any binary sequence as a key, from a string like "foo" to the content of a JPEG file. The empty string is also a valid key.
A few other rules about keys:
在redis源码中,对象的数据结构定义在redis.h文件中
#define REDIS_LRU_BITS 24
typedef struct redisObject {
// 类型
unsigned type:4;
// 编码
unsigned encoding:4;
// 对象最后一次被访问的时间
unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
// 引用计数
int refcount;
// 指向实际值的指针
void *ptr;
}
下面分别介绍以下对象中定义的属性定义:
// 对象类型
#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 /* Raw representation */
#define REDIS_ENCODING_INT 1 /* Encoded as integer */
#define REDIS_ENCODING_HT 2 /* Encoded as hash table */
#define REDIS_ENCODING_ZIPMAP 3 /* Encoded as zipmap */
#define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */
#define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
#define REDIS_ENCODING_INTSET 6 /* Encoded as intset */
#define REDIS_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
#define REDIS_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
盗取一张图(懒的画图):
各种对象的创建可以参考redis源码中的object.c文件,如:
根据传入的整数型,创建一个字符串:
/*
* 根据传入的整数值,创建一个字符串对象
*
* 这个字符串的对象保存的可以是 INT 编码的 long 值,
* 也可以是 RAW 编码的、被转换成字符串的 long long 值。
*/
robj *createStringObjectFromLongLong(long long value) {
robj *o;
// value 的大小符合 REDIS 共享整数的范围
// 那么返回一个共享对象
if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
incrRefCount(shared.integers[value]);
o = shared.integers[value];
// 不符合共享范围,创建一个新的整数对象
} else {
// 值可以用 long 类型保存,
// 创建一个 REDIS_ENCODING_INT 编码的字符串对象
if (value >= LONG_MIN && value <= LONG_MAX) {
o = createObject(REDIS_STRING, NULL);
o->encoding = REDIS_ENCODING_INT;
o->ptr = (void*)((long)value);
// 值不能用 long 类型保存(long long 类型),将值转换为字符串,
// 并创建一个 REDIS_ENCODING_RAW 的字符串对象来保存值
} else {
o = createObject(REDIS_STRING,sdsfromlonglong(value));
}
}
return o;
}
线面简单看看基本类型type对应的底层数据结构:
sting在redis底层对应三种编码方式,两种数据结构。
如果一个字符串内容可以转成long,那么编码方式为int,底层数据结构为int.
如果普通字符串对象的长度小于39字节,就用embstr对象。否则用的raw对象,底层数据结构为简单动态字符串。
/* Create a string object with EMBSTR encoding if it is smaller than
* REIDS_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is
* used.
*
* The current limit of 39 is chosen so that the biggest string object
* we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */
#define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 39
robj *createStringObject(char *ptr, size_t len) {
if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT)
return createEmbeddedStringObject(ptr,len);
else
return createRawStringObject(ptr,len);
}
简单动态字符串(simple dynamic string)的数据结构为:
/*
* 保存字符串对象的结构
*/
struct sdshdr {
// buf 中已占用空间的长度
int len;
// buf 中剩余可用空间的长度
int free;
// 数据空间
char buf[];
};
有两种编码实现,链表linkedlist和压缩列表ziplist,当当list元素少且元素内容长度不大时,使用ziplist,否则使用linkedlist.
/* Use a real list when there are too many entries
*
* 根据节点数,创建对象的编码
*/
if (len > server.list_max_ziplist_entries) {
o = createListObject();
} else {
o = createZiplistObject();
}
/*
* 创建一个 LINKEDLIST 编码的列表对象
*/
robj *createListObject(void) {
list *l = listCreate();
robj *o = createObject(REDIS_LIST,l);
listSetFreeMethod(l,decrRefCountVoid);
o->encoding = REDIS_ENCODING_LINKEDLIST;
return o;
}
/*
* 创建一个 ZIPLIST 编码的列表对象
*/
robj *createZiplistObject(void) {
unsigned char *zl = ziplistNew();
robj *o = createObject(REDIS_LIST,zl);
o->encoding = REDIS_ENCODING_ZIPLIST;
return o;
}
其底层数据结构list位于adlist.h中:
/*
* 双端链表节点
*/
typedef struct listNode {
// 前置节点
struct listNode *prev;
// 后置节点
struct listNode *next;
// 节点的值
void *value;
} listNode;
/*
* 双端链表结构
*/
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;
类似数组,但是每个节点存储的数据大小不同,节点上有length属性。
有两种数据结构实现,压缩列表ziplist和字典dict.
相当于java中的HashMap。解决hash冲突使用的是链表法,好像没有上升到红黑树。
如果是整数类型,直接使用整数集合intset,如果不是,就用字典,和java的set一样。
有序的set,元素个数少且不大,就用压缩列表ziplist,否则就用跳跃表skiplist.
跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。
跳跃表是一种随机化的数据,跳跃表以有序的方式在层次化的链表中保存元素。
定义位于redis.h中。
/* ZSETs use a specialized version of Skiplists */
/*
* 跳跃表节点
*/
typedef struct zskiplistNode {
// 成员对象
robj *obj;
// 分值
double score;
// 后退指针
struct zskiplistNode *backward;
// 层
struct zskiplistLevel {
// 前进指针
struct zskiplistNode *forward;
// 跨度
unsigned int span;
} level[];
} zskiplistNode;
/*
* 跳跃表
*/
typedef struct zskiplist {
// 表头节点和表尾节点
struct zskiplistNode *header, *tail;
// 表中节点的数量
unsigned long length;
// 表中层数最大的节点的层数
int level;
} zskiplist;
深入浅出Redis-redis底层数据结构(上)
深入浅出Redis-redis底层数据结构(下)
Redis基本类型及其数据结构
Redis的五种对象类型及其底层实现
Redis-基本数据类型与内部存储结构
redis的五种基本数据类型及其内部实现
《Redis设计与实现》黄健宏
标签:blog ace user encode wstring 计数 port abs 快速
原文地址:https://www.cnblogs.com/clawhub/p/12019847.html