Redis笔记

SDS

SDS是Redis里的动态字符串表示方式,但是,查看Redis6.0.5源码SDS定义:

1
typedef char *sds;

what?这不就是简单的字符串指针吗?怎么就动态了?且慢,往后看。

Redis里面定义五种类型的SDS,他们的区别是header大小不一样,字符串指针sds始终指向buf的位置,然后通过指针移位来操作header里的字段。这里有个gcc c语言的属性__attribute__ ((__packed__))设置,表示结构体内存布局紧凑排列,按1字节对齐,以节省内存。

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
/* Note: sdshdr5 is never used, we just access the flags byte directly.
* However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
uint64_t len; /* used */
uint64_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};

Redis里面的sds始终指向动态字符串buf位置,在创建字符串类型的redisObject时,会分配实际buf长度的内存加上sds header的内存和一个字符串结束符位置的内存。然后在创建redisObject之前会根据实际字符串大小来选择需要添加的sds header

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 根据字符串大小,选择sds类型(区别在header大小不一样)
static inline char sdsReqType(size_t string_size) {
if (string_size < 1<<5)
return SDS_TYPE_5;
if (string_size < 1<<8)
return SDS_TYPE_8;
if (string_size < 1<<16)
return SDS_TYPE_16;
#if (LONG_MAX == LLONG_MAX)
if (string_size < 1ll<<32)
return SDS_TYPE_32;
return SDS_TYPE_64;
#else
return SDS_TYPE_32;
#endif
}

以类型SDS_TYPE_8为例,将为这种字符串类型的redisObject分配sizeof(redisObject) + sdshdr8 + len +1大小的内存,并设置sds的header指针位置,然后设置header属性,设置buf的值,最后添加字符串结束符’\0’

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
/* Create a string object with encoding OBJ_ENCODING_EMBSTR, that is
* an object where the sds string is actually an unmodifiable string
* allocated in the same chunk as the object itself. */
robj *createEmbeddedStringObject(const char *ptr, size_t len) {
// 分配sizeof(robj)+sizeof(struct sdshdr8)+len+1大小的内存
robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1);
// 设置sds header指针位置,跳过redisObject大小
struct sdshdr8 *sh = (void*)(o+1);
// 设置redisObject属性
o->type = OBJ_STRING;
o->encoding = OBJ_ENCODING_EMBSTR;
// redisObject->ptr 指向buf位置
o->ptr = sh+1;
o->refcount = 1;
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
} else {
o->lru = LRU_CLOCK();
}
// 设置sds header属性
sh->len = len;
sh->alloc = len;
sh->flags = SDS_TYPE_8;
// 添加字符串结束符
if (ptr == SDS_NOINIT)
sh->buf[len] = '\0';
else if (ptr) {
memcpy(sh->buf,ptr,len);
sh->buf[len] = '\0';
} else {
memset(sh->buf,0,len+1);
}
return o;
}

Redis数据类型

字符串String

Hash表

List列表:

LPUSH + BRPOP 实现消息队列,支持消息持久化,保证消息顺序性;不足的地方:不支持消息的消费确认,不能做广播模式,不能重复消费,不支持分组消费

LPUSH + LPOP 实现栈,先进后出

LPUSH + RPOP 实现队列,先进先出

LPUSH + LTRIM 实现有限队列

Set集合:

数据去重,交集、并集操作,实现共同关注等功能

Sorted Set有序集合:

各类排行榜,范围查找

HyperLogLog:

不精确(0.81%的错误率)的去重统计,比如:网站UV数、访问IP数等

Geo:

Stream:用于实现消息队列(支持消息ID自动生成,消息遍历,消息的阻塞和非阻塞读取,消息的分组消费,消息的消费确认,未完成消息的处理,消息队列监控)参考 http://www.hellokang.net/redis/stream.html#_10-%E5%91%BD%E4%BB%A4%E4%B8%80%E8%A7%88

Stream底层数据结构Radix树(基数树),可以看作是Trie树(字典树)的变种

基数树(长整型到对象的映射)

字典树(字符串到对象的映射)

PUB/SUB 订阅/发布 (用作消息队列,支持广播模式,支持消息即时推送,支持消费者同时订阅多个信道从而接受多类消息,不足的地方:不支持消息的消费确认,不能重复消费)

Redis每种数组类型在底层都是动态编码的,比如默认zset是以ziplist编码的,当zset个数超过128时,zset转换编码为skiplist。但是这种编码转换是不可逆的。比如再将zset元素减少到小于128时,zset编码依然是skiplist

Redis查看帮助

命令行下输入help @,再按tab键会提示可以查看help的数据类型

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> help @string
127.0.0.1:6379> help @hash
127.0.0.1:6379> help @list
127.0.0.1:6379> help @set
127.0.0.1:6379> help @sorted_set
127.0.0.1:6379> help @geo
127.0.0.1:6379> help @pubsub
127.0.0.1:6379> help @transactions
127.0.0.1:6379> help @connection
127.0.0.1:6379> help @generic

Redis配置信息内存占用查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
INFO

MEMORY HELP、MEMORY USAGE key、redis-cli --bigkeys、DEBUG OBJECT key

CONFIG HELP、CONFIG GET *、INFO Commandstats命令获取,包含每个命令调用次数,总耗时,平均耗时,单位微秒

CONFIG GET zset-max-ziplist-value

CONFIG GET zset-max-ziplist-entries

当满足任意条件:
value最大空间(字节)>zset-max-ziplist-value
有序集合长度>zset-max-ziplist-entries

zset使用skiplist,否则使用ziplist
-------------本文结束感谢您的阅读-------------
Good for you!