11. Redis数据类型底层实现 String 底层实现

整体看

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脚本

redis 是 key-value 存储系统

其中key类型一般为字符串,value 类型则为redis对象(redisObject)
11. Redis数据类型底层实现 String 底层实现_第1张图片
bitmap--------------------------实质String
hyperLogLog-----------------实质String
GEO----------------------------实质Zset
11. Redis数据类型底层实现 String 底层实现_第2张图片

11. Redis数据类型底层实现 String 底层实现_第3张图片
Redis 中每个对象都是一个 redisObject 结构
11. Redis数据类型底层实现 String 底层实现_第4张图片


/*
 * 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;

11. Redis数据类型底层实现 String 底层实现_第5张图片

set hello word

key 是字符串,但是 Redis 没有直接使用 C 的字符数组,而是存储在redis自定义的 SDS中。
value 既不是直接作为字符串存储,也不是直接存储在 SDS 中,而是存储在redisObject 中。
实际上五种常用的数据类型的任何一种,都是通过 redisObject 来存储的。

11. Redis数据类型底层实现 String 底层实现_第6张图片
看看类型:----------------type 键
看看编码:----------------object encoding hello
debug结构:--------------debug object person
11. Redis数据类型底层实现 String 底层实现_第7张图片
encoding:编码方式
11. Redis数据类型底层实现 String 底层实现_第8张图片
11. Redis数据类型底层实现 String 底层实现_第9张图片

String

  1. int
    保存long 型(长整型)的64位(8个字节)有符号整数
    9223372036854775807
    11. Redis数据类型底层实现 String 底层实现_第10张图片
    如果是浮点数, Redis 内部其实先将浮点数转化为字符串值,然后再保存

  2. embstr:
    代表 embstr 格式的 SDS(Simple Dynamic String 简单动态字符串),保存长度小于等于44字节的字符串
    EMBSTR 顾名思义即:embedded string,表示嵌入式的String

  3. raw
    保存长度大于44字节的字符串

11. Redis数据类型底层实现 String 底层实现_第11张图片

SDS

Redis没有直接复用C语言的字符串,而是新建了属于自己的结构-----SDS
在Redis数据库里,包含字符串值的键值对都是由SDS实现的(Redis中所有的键都是由字符串对象实现的即底层是由SDS实现,Redis中所有的值对象中包含的字符串对象底层也是由SDS实现)。
11. Redis数据类型底层实现 String 底层实现_第12张图片
11. Redis数据类型底层实现 String 底层实现_第13张图片

11. Redis数据类型底层实现 String 底层实现_第14张图片

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 表示字符串数组,真存数据的。

11. Redis数据类型底层实现 String 底层实现_第15张图片

Redis为什么重新设计一个 SDS 数据结构?

11. Redis数据类型底层实现 String 底层实现_第16张图片
空间预分配:

1)如果sds修改后,sds长度(len的值)小于1mb,那么会分配与len相同大小的未使用空间,此时len与free值相同。例如,修改之后字符串长度为100字节,那么会给分配100字节的未使用空间。最终sds空间实际为 100 + 100 + 1(保存空字符’\0’);

2)如果大于等于1mb,每次给分配1mb未使用空间
惰性空间释放:对字符串进行缩短操作时,程序不立即使用内存重新分配来回收缩短后多余的字节,而是使用 free 属性将这些字节的数量记录下来,等待后续使用(sds也提供api,我们可以手动触发字符串缩短);

set k1 123

11. Redis数据类型底层实现 String 底层实现_第17张图片
Redis 启动时会预先建立 10000 个分别存储 0~ 9999 的 redisObject 变量作为共享对象,这就意味着如果 set字符串的键值在 0~10000 之间的话,则可以 直接指向共享对象 而不需要再建立新对象,此时键值不占空间!

  • set k1 123
  • set k2 123
  • 11. Redis数据类型底层实现 String 底层实现_第18张图片
    Object.c
    11. Redis数据类型底层实现 String 底层实现_第19张图片
    11. Redis数据类型底层实现 String 底层实现_第20张图片

set k1 abc

11. Redis数据类型底层实现 String 底层实现_第21张图片
11. Redis数据类型底层实现 String 底层实现_第22张图片
11. Redis数据类型底层实现 String 底层实现_第23张图片
11. Redis数据类型底层实现 String 底层实现_第24张图片

set k1 大于44长度的一个字符串

11. Redis数据类型底层实现 String 底层实现_第25张图片
OBJ_ENCODING_RAW格式,这与OBJ_ENCODING_EMBSTR编码方式的不同之处在于,
此时动态字符串sds的内存与其依赖的redisObject的内存不再连续了

11. Redis数据类型底层实现 String 底层实现_第26张图片
11. Redis数据类型底层实现 String 底层实现_第27张图片

只有整数才会使用int,浮点数先转为字符串。
embstr和raw底层都是SDS

int : Long类型的整数时,RedisObject的ptr指针直接赋值为整数数据,不在额外的指针再指向整数了,节省了指针的空间开销。
embstr : 当保存的是字符串数据且字符串小于等于44字节时,embstr类型将会调用内存分配函数,只分配一块连续的内存空间,空间中依次包含的redisObject与sdshdr两个数据结构,
让元数据、指针和SDS是一块连续的内存区域,这样就可以避免内存碎片

raw: 当字符串大于44字节时,SDS的数据量变多变大了,SDS和RedisObject布局分家各自过,会给SDS分配多个空间并用指针指向SDS结构,raw类型将会调用两次内存分配函数,
分配两块内存空间,一块用于包含redisObject结构,而另一块用于包含sdshdr结构。
11. Redis数据类型底层实现 String 底层实现_第28张图片
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字节

你可能感兴趣的:(redis6,redis,数据库,database)