七、原理解析

1. 数据结构

1.1 动态字符串SDS

Redis10 原理解析 - 图1

Redis10 原理解析 - 图2

为什么预分配?

  1. Linux分成了用户空间和内存空间的,应用程序无法直接操作硬件,需要和Linux内核进行交互,用户态切换到内核态,申请内存

学习 Linux 时,经常可以看到两个词:User space(用户空间)和 Kernel space(内核空间)。

简单说,Kernel space 是 Linux 内核的运行空间,User space 是用户程序的运行空间。为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不受影响。

注:虚拟内存被操作系统划分成两块:内核空间和用户空间,内核空间是内核代码运行的地方,用户空间是用户程序代码运行的地方。当进程运行在内核空间时就处于内核态,当进程运行在用户空间时就处于用户态。

相关🔗 https://www.linuxprobe.com/linux-kernel-user-space.html

Redis10 原理解析 - 图3

1.2 InSet

Redis10 原理解析 - 图4

Redis10 原理解析 - 图5

Redis10 原理解析 - 图6

问题: 上图的 5 10 20 都可以用一个字节表示 为什么要采用占两个字节的(INTSET_ENC_INT16)?\

为了方便inset基于数组角标去寻找, C语言基于指针(8字节大小的整数,映射到内存空间【类似内存地址】)查找

Redis10 原理解析 - 图7

起始地址 + (元素大小 * 角标)

Redis10 原理解析 - 图8

1.3 Dict

Redis10 原理解析 - 图9

Redis10 原理解析 - 图10

Redis10 原理解析 - 图11

Redis10 原理解析 - 图12

  1. using namespace std;
  2. #define Start 1
  3. int main()
  4. {
  5. for (int i = Start; i <=9 ; ++i) {
  6. for (int j = 1; j <= i; ++j) {
  7. cout << "|" << j << "*" << i << "=" << i*j << "| ";
  8. }
  9. cout << endl;
  10. }
  11. return 0;
  12. }

Redis10 原理解析 - 图13

Redis10 原理解析 - 图14

Redis10 原理解析 - 图15

Redis10 原理解析 - 图16

Redis10 原理解析 - 图17

1.4 ZipList

Redis10 原理解析 - 图18

Redis10 原理解析 - 图19

Redis10 原理解析 - 图20

Redis10 原理解析 - 图21
Redis10 原理解析 - 图22

Redis10 原理解析 - 图23

1、SDS 动态字符串

优点解析
  • 二进制安全

C语言的字符串[0个字节的无符号指针指向的char数组]是根据 \0结尾 内容无法容纳\0而SDS 是根据 len 长度判断字符串的 而扩容时不包括 \0 且C的字符串每次更改都是新的常量池

2、InSet

有序/内容倒序扩容 为了扩容(或者说扩容编码长度)防止先放比较小的数据 伸展(右边补0)导致后面准备放其他数据的字节位被占满,也就是先将大得数据放置到准备的位置 而扩容前将要添加的数据在扩容(改变编码 )之后添加

注意 判断的数据大小是所占用的比特位 也就是所负数所占用的字节是可以比正数大的,正数插入元素角标位置不变,负数向后移动一位

类似于TreeSet

根据len 可以根据公式 startIndex + (index * typesize)很快找到前后元素

适用于数据量少 多了便会慢 数组形式[连续内存空间]

3、Dict

初始化数组最小是4 size为2^n sizemask为2^n-Redis10 原理解析 - 图24

Redis10 原理解析 - 图25

Redis10 原理解析 - 图26

Redis10 原理解析 - 图27

Redis10 原理解析 - 图28

Redis10 原理解析 - 图29

Redis10 原理解析 - 图30

Redis10 原理解析 - 图31

Redis10 原理解析 - 图32

Redis10 原理解析 - 图33

Redis10 原理解析 - 图34

Redis10 原理解析 - 图35

4. ZipList

Dict不是连续性的 每个entity都是独立的 使用了大量指针 查询快但是内存占用大,可能造成大量的内存碎片

Redis10 原理解析 - 图36

Redis10 原理解析 - 图37

Redis10 原理解析 - 图38

Redis10 原理解析 - 图39

Redis10 原理解析 - 图40

Redis10 原理解析 - 图41

Redis10 原理解析 - 图42

Redis10 原理解析 - 图43

Redis10 原理解析 - 图44

Redis10 原理解析 - 图45

类似于链表,节点找中间的节点性能慢,但是舍弃了指针,占用内存空间更小,节点数会做限制,总节点长度 和节点数是小端字节序表示,低位在前

连续锁更新问题

假如说现在有几个连续且节点长度都是250~253的节点,他们记录前一个节点都是用一个字节表示,因为没超过254,这时有一个超过254字节插入到队首,那么之前的头节点表示上一个节点长度的所使用的字节数就要从1变成5 导致本身超过超过254字节数,导致后面几个节点超过阈值随之更新,申请内存空间切换态消耗很大

Redis10 原理解析 - 图46

Redis10 原理解析 - 图47

1.5 QuickList

Redis10 原理解析 - 图48

Redis10 原理解析 - 图49

Redis10 原理解析 - 图50

Redis10 原理解析 - 图51

Redis10 原理解析 - 图52

Redis10 原理解析 - 图53

1.6 SkipList

查询效率非常高 可以允许32级指针 理论上说也就是 2^32 个元素

Redis10 原理解析 - 图54

Redis10 原理解析 - 图55

Redis10 原理解析 - 图56

Redis10 原理解析 - 图57

1.7 RedisObject

Redis10 原理解析 - 图58

Redis10 原理解析 - 图59

Redis10 原理解析 - 图60

1.8 五种数据结构

1、String

Redis10 原理解析 - 图61

如果小于44字节

Redis10 原理解析 - 图62

小于44字节所采用的编码是 SDS_TYPE_8 RedisObject和SDS 加在一起刚好64字节 redis底层内存分配的算法 jemalloc(https://blog.csdn.net/MOU_IT/article/details/118460045)

会以2^n 内存分配 而64正好是一个内存分片的大小不会产生碎片

相关文章 https://blog.csdn.net/weixin_41231928/article/details/120589257

“一个字节有8位,每一位两种状态1或者0计算机储存数据是以二进制的方式,有一位为符号位,所以最大数为01111111转化为十进制数为127。若无符号,最大数为11111111转化为十进制为255。”

Redis10 原理解析 - 图63

Redis10 原理解析 - 图64

Redis10 原理解析 - 图65

Redis10 原理解析 - 图66

2、 List

Redis10 原理解析 - 图67

Redis10 原理解析 - 图68

3、 Set

Redis10 原理解析 - 图69

Redis10 原理解析 - 图70

Redis10 原理解析 - 图71

4、 ZSet

Redis10 原理解析 - 图72

Redis10 原理解析 - 图73

Redis10 原理解析 - 图74

Redis10 原理解析 - 图75

Redis10 原理解析 - 图76

5、 Hash

Redis10 原理解析 - 图77

Redis10 原理解析 - 图78

Redis10 原理解析 - 图79

Redis10 原理解析 - 图80

Redis10 原理解析 - 图81

Redis10 原理解析 - 图82、、

2. 网络模型

1、用户空间和内核空间

Redis10 原理解析 - 图83

Redis10 原理解析 - 图84

Redis10 原理解析 - 图85

2、阻塞IO

Redis10 原理解析 - 图86

3、 非阻塞IO

Redis10 原理解析 - 图87

4、 IO多路复用

相关链接

Redis10 原理解析 - 图88

Redis10 原理解析 - 图89

select

Redis10 原理解析 - 图90

https://blog.csdn.net/youyou1543724847/article/details/83445226

poll

Redis10 原理解析 - 图91

epoll

Redis10 原理解析 - 图92

Redis10 原理解析 - 图93

5、 多路复用通知机制

Redis10 原理解析 - 图94

6、IO多路复用web服务流程

Redis10 原理解析 - 图95

7、 信号驱动IO 与 异步IO

Redis10 原理解析 - 图96

Redis10 原理解析 - 图97

Redis10 原理解析 - 图98

7、Redis网络模型

Redis10 原理解析 - 图99

Redis10 原理解析 - 图100

Redis10 原理解析 - 图101

Redis10 原理解析 - 图102

Redis10 原理解析 - 图103

Redis10 原理解析 - 图104

Redis10 原理解析 - 图105

Redis10 原理解析 - 图106

Redis10 原理解析 - 图107

Redis10 原理解析 - 图108

3. Redis通信协议

1、 RESP协议

Redis10 原理解析 - 图109

Redis10 原理解析 - 图110

2、 模拟客户端
  1. SocketChannel channel = SocketChannel.open();
  2. ByteBuffer allocate = ByteBuffer.allocate(96);
  3. channel.connect(new InetSocketAddress("localhost",6379));
  4. allocate.put("*3\r\n$3\r\nset\r\n$4\r\nname\r\n$6\r\n张三\r\n".getBytes(StandardCharsets.UTF_8));
  5. allocate.flip();
  6. channel.write(allocate);
  7. allocate.clear();
  8. channel.read(allocate);
  9. allocate.flip();
  10. int limit = allocate.limit();
  11. byte[] bytes = new byte[limit];
  12. for (int i = 0; i < limit; i++) {
  13. bytes[i] = allocate.get();
  14. }
  15. System.out.println(new String(bytes)); // 输出 [+OK]

4、Redis内存策略

1、内存过期

Redis10 原理解析 - 图111

Redis10 原理解析 - 图112

Redis10 原理解析 - 图113

Redis10 原理解析 - 图114

Redis10 原理解析 - 图115

Redis10 原理解析 - 图116

2、内存淘汰

Redis10 原理解析 - 图117

Redis10 原理解析 - 图118

Redis10 原理解析 - 图119

配色与插件

Redis10 原理解析 - 图120

Redis10 原理解析 - 图121

Redis10 原理解析 - 图122

Redis10 原理解析 - 图123

Redis10 原理解析 - 图124

Redis10 原理解析 - 图125