https://mp.weixin.qq.com/s/7ct-mvSIaT3o4-tsMaKRWA
不同数据结构在源码中的名称
1.简单动态字符串sds.c
2.整数集合intset.c
3.压缩列表ziplist.c
4.快速链表quicklist.c
5.字典dict.c
6.Streams的底层实现结构listpack.c和rax.c
不同数据类型在源码中的名称
Redis对象object.c
字符串t_string.c
列表t_list.c
字典t_hash.c
集合及有序集合t_set.c和t_zset.c
数据流t_stream.c
Redis数据库的实现:
1.数据库的底层实现db.c
2.持久化rdb.c和aof.c
Redis服务端和客户端实现:
事件驱动ae.c和ae_epoll.c
网络连接anet.c和networking.c
服务端程序server.c
客户端程序redis-cli.c
主从复制replication.c
哨兵sentinel.c
集群cluster.c
其他数据结构,如hyperloglog.c、geo.c等
其他功能,如pub/sub、Lua脚本
其中key类型一般为字符串,value 类型则为redis对象(redisObject)
bitmap--------------------------实质String
hyperLogLog-----------------实质String
GEO----------------------------实质Zset
Redis 中每个对象都是一个 redisObject 结构
/*
* Redis 对象
*/
typedef struct redisObject {
// 类型 4bits
unsigned type:4;
// 编码方式 4bits
unsigned encoding:4;
// LRU 时间(相对于 server.lruclock) 24bits
unsigned lru:22;
// 引用计数 Redis里面的数据可以通过引用计数进行共享 32bits
int refcount;
// 指向对象的值 64-bit
void *ptr;
} robj;
key 是字符串,但是 Redis 没有直接使用 C 的字符数组,而是存储在redis自定义的 SDS中。
value 既不是直接作为字符串存储,也不是直接存储在 SDS 中,而是存储在redisObject 中。
实际上五种常用的数据类型的任何一种,都是通过 redisObject 来存储的。
看看类型:----------------type 键
看看编码:----------------object encoding hello
debug结构:--------------debug object person
encoding:编码方式
int
保存long 型(长整型)的64位(8个字节)有符号整数
9223372036854775807
如果是浮点数, Redis 内部其实先将浮点数转化为字符串值,然后再保存
embstr:
代表 embstr 格式的 SDS(Simple Dynamic String 简单动态字符串),保存长度小于等于44字节的字符串
EMBSTR 顾名思义即:embedded string,表示嵌入式的String
raw
保存长度大于44字节的字符串
Redis没有直接复用C语言的字符串,而是新建了属于自己的结构-----SDS
在Redis数据库里,包含字符串值的键值对都是由SDS实现的(Redis中所有的键都是由字符串对象实现的即底层是由SDS实现,Redis中所有的值对象中包含的字符串对象底层也是由SDS实现)。
sdshdr5、(2^5=32byte)
sdshdr8、(2 ^ 8=256byte)
sdshdr16、(2 ^ 16=65536byte=64KB)
sdshdr32、 (2 ^ 32byte=4GB)
sdshdr64,2的64次方byte=17179869184G用于存储不同的长度的字符串。
len 表示 SDS 的长度,使我们在获取字符串长度的时候可以在 O(1)情况下拿到,而不是像 C 那样需要遍历一遍字符串。
alloc 可以用来计算 free 就是字符串已经分配的未使用的空间,有了这个值就可以引入预分配空间的算法了,而不用去考虑内存分配的问题。
buf 表示字符串数组,真存数据的。
1)如果sds修改后,sds长度(len的值)小于1mb,那么会分配与len相同大小的未使用空间,此时len与free值相同。例如,修改之后字符串长度为100字节,那么会给分配100字节的未使用空间。最终sds空间实际为 100 + 100 + 1(保存空字符’\0’);
2)如果大于等于1mb,每次给分配1mb未使用空间
惰性空间释放:对字符串进行缩短操作时,程序不立即使用内存重新分配来回收缩短后多余的字节,而是使用 free 属性将这些字节的数量记录下来,等待后续使用(sds也提供api,我们可以手动触发字符串缩短);
Redis 启动时会预先建立 10000 个分别存储 0~ 9999 的 redisObject 变量作为共享对象,这就意味着如果 set字符串的键值在 0~10000 之间的话,则可以 直接指向共享对象 而不需要再建立新对象,此时键值不占空间!
OBJ_ENCODING_RAW格式,这与OBJ_ENCODING_EMBSTR编码方式的不同之处在于,
此时动态字符串sds的内存与其依赖的redisObject的内存不再连续了
只有整数才会使用int,浮点数先转为字符串。
embstr和raw底层都是SDS
int : Long类型的整数时,RedisObject的ptr指针直接赋值为整数数据,不在额外的指针再指向整数了,节省了指针的空间开销。
embstr : 当保存的是字符串数据且字符串小于等于44字节时,embstr类型将会调用内存分配函数,只分配一块连续的内存空间,空间中依次包含的redisObject与sdshdr两个数据结构,
让元数据、指针和SDS是一块连续的内存区域,这样就可以避免内存碎片
raw: 当字符串大于44字节时,SDS的数据量变多变大了,SDS和RedisObject布局分家各自过,会给SDS分配多个空间并用指针指向SDS结构,raw类型将会调用两次内存分配函数,
分配两块内存空间,一块用于包含redisObject结构,而另一块用于包含sdshdr结构。
redisObject占用空间
4 + 4 + 24 + 32 + 64 = 128bits = 16字节
sdshdr8占用空间
1(uint8_t) + 1(uint8_t)+ 1 (unsigned char)+ 1(buf[]中结尾的’\0’字符)= 4字节
初始最小分配为64字节,所以只分配一次空间的embstr最大为 64 - 16- 4 = 44字节