Python微信订餐小程序课程视频
https://blog.csdn.net/m0_56069948/article/details/122285951
Python实战量化交易理财系统
https://blog.csdn.net/m0_56069948/article/details/122285941
准备将之前攒下的书先看一遍,主要是有个大概的了解,以后用的时候也知道在哪里找。所以准备开几篇共读的帖子,激励自己多看一些书。
Redis 基于 简单动态字符串
(SDS)、双端链表
、字典
、压缩列表
、整数集合
等基础的数据结构,创建了一个对象系统,这个对象系统包含:字符串对象
(String)、列表对象
(List)、集合对象
(Set)、有序集合对象
(Zset)、哈希对象
(Hash) 5种数据对象类型。但是这5种对象类型,其内部的基础的存储结构 并不是 一对一的一种,而是每一种包含了至少两种数据结构。
我们这篇主要用来说一下其基础的存储结构
前提条件
redis 底层是使用C语言编写的,所以很多函数直接使用的C库。
一、SDS(简单动态字符串)
我们知道C语言中字符串 是以字符数组char[]
进行存储的,字符串的结束是以 空字符‘/0’
来进行标识的,也就是字符串的实际长度比我们看见的字符串都会多1 byte(字节)
。
如果我们想要查看一下字符串的长度,那么就需要遍历一下字符数组,时间复杂度为O(n)。
![image.png](https://img-blog.csdnimg.cn/img_convert/5b72b6cb8f3bf497d4470c9204e57680.png#clientId=u45edc488-58e8-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=268&id=u4707854a&margin=[object Object]&name=image.png&originHeight=268&originWidth=932&originalType=binary&ratio=1&rotation=0&showTitle=true&size=52829&status=done&style=none&taskId=u13de9252-8aca-44a5-a16c-2560ebf0af0&title=SDS结构图&width=932 “SDS结构图”)
1.1 结构说明:
- redis中使用结构体
SDS
用来存储字符串类型,同样的使用字符数组
进行存储 也自带空字符‘/0’
,从而可以使用C语言中字符串相关的特性/函数。 - len:数组已用长度记录,就是说字符串的真实长度(不算‘/0’)
- free:数组中剩余可用长度,也就是数组中还有多少长度使用的。
1.2 内存预分配
我们从SDS结构图可以知道SDS中字符数组的长度是和字符串长度不一样的,那么这个长度是如何分配的?
- 首先如果是创建/扩展:
- 小于1M,分配的 未使用内存 是 使用内存的
2倍
- 大于1M,那么 每次扩展未使用内存为
1M
- 小于1M,分配的 未使用内存 是 使用内存的
- 如果是收缩:
并不会立即真正释放
,会留下未使用的内存,可以通过Api来进行释放,从而避免内存泄漏
。
1.3 二进制
由于C语言中字符串以 ‘/0’标识结尾,所以C语言中字符串不能存储 图片、音视频的二进制数据,但是redis 中字符串以len来做为结尾的判断,所以可以使用字符串来存储二进制的数据。
当然对于 文本类型的 本身结束就是‘/0’结尾的,所以我们可以直接使用C的字符串特性。
1.4 特性(总结):
- 自带空格,从而可以使用C语言字符串相关特性
存储
使用空间
和未使用空间
这样长度可以快速得出(时间复杂度O(1)
),不用遍历数组(时间复杂度O(n)
)- 由2我们可以杜绝 C语言中
缓存溢出
的问题 - 节省了避免缓存溢出而带来 内存重分配的系统开销
- 空间预分配
- 扩展:小于1M 预分配未使用空间为 使用空间的2倍,大于1M,预分配未使用空间为1M;
- 收缩:惰性空间释放
- 可以存储图片和音视频二进制数据。
关于 C语言缓存溢出:
我们知道数组是一块内存挨着的存储空间,C语言中,如果我们直接对字符串增加,会有如下这种情况的发生:
![image.png](https://img-blog.csdnimg.cn/img_convert/d4d3b3a872442267c5484c6b7c182f79.png#clientId=u45edc488-58e8-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=298&id=u43dc17ec&margin=[object Object]&name=image.png&originHeight=298&originWidth=1832&originalType=binary&ratio=1&rotation=0&showTitle=true&size=49201&status=done&style=none&taskId=ub6e75eb1-2133-4bf1-9bf9-f7fa24fb256&title=字符串“hello”未添加 字段之前内存快照&width=1832 “字符串“hello”未添加 字段之前内存快照”)
现在给hello 尾部添加 “-wi” 字符串
“字符串“hello”添加 “-wi” 字符串之后内存快照”所以C语言中我们为了防止这种情况,每次扩展的时候都会进行
内存重分配
,使得空余的字符数组可以容得下我们新加的字符串。但是内存重分配
会导致系统调用,对于redis这种频繁增加删除的数据库来说,这种肯定要尽可能的减少系统性能的浪费。
二、链表
其实就是一个结构体
持有双向链表
Copytypedef struct list{
//表头节点
listNode *head;
//表尾节点
listNode *tail;
//链表所包含的节点数量
unsigned long len;
//节点值复制函数
void *(*dup)(void *ptr);
//节点值释放函数
void *(*free)(void *ptr);
//节点值对比函数
int (*match)(void *ptr,void *key);
}list;
![image.png](https://img-blog.csdnimg.cn/img_convert/bfa4ed8ce68f2b31199fc8d0b8d3be7a.png#clientId=u45edc488-58e8-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=122&id=uaf0d2abe&margin=[object Object]&name=image.png&originHeight=122&originWidth=528&originalType=binary&ratio=1&rotation=0&showTitle=true&size=22779&status=done&style=none&taskId=u62ebd04a-a911-4b2f-aa0f-b081eab36cb&title=redis链表结构图&width=528 “redis链表结构图”)
特性
- 双向连表,这样查找前(或者后)一个节点,复杂度为O(1)
- 有头尾指针,查找第一个节点、最后一个节点复杂度为O(1)
- 带链表长度计数器,返回长度复杂度为O(1)
- 无环(⚠️)
void*
存储节点的值,可以使用dup\free\match 等特定函数。
三、字典
C语言本身没有 字典
类型,但是对于key-vale 这种映射的关系 在redis是常用的,所以redis 自己构建了一个结构体,本身使用的是 hash 结构
Copytypedef struct dict {
dictType *type; //dictType也是一种数据结构,dictType结构中包含了一些函数,这些函数用来计算key的哈希值,进而用这个哈希值计算key在dictEntry型table数组中的下标
void *privdata; //私有数据,保存着dictType结构中函数的参数
dictht ht[2]; //两张哈希表:一张用来正常存储节点,一张用来在rehash时临时存储节点
long rehashidx; //rehash的标记:默认-1,当table数组中已有元素个数增加/减少到一定量时,整个字典结构将进行rehash给每个table元素重新分配位置,rehashidx代表rehash过程的进度,rehashidx==-1代表字典没有在进行rehash,rehashidx>-1代表该字典结构正在对进行rehash
} dict;
3.1 字典结构体
- dictType:也是一种数据结构,dictType结构中包含了一些函数(dup\free等),这些函数用来计算key的哈希值,进而用这个哈希值计算key在dictEntry型table数组中的下标。
说白了,也就是redis 的字典为每种基础类型都创建了一个dictType,使得可以使用类型特定的函数
- privdata:私有数据,存储dictType构造参数,不同的类型传不同 的参数
- ht[]:哈希表,真正存储数据的地方。其中
ht[0]
是使用的表,ht[1]
是没有分配内存空间
,只有在rehash
的时候会分配内存,用到。 - rehashidx:在
rehash
的时候才会使用。
3.1.1 redis 哈希表结构体:
Copytypedef struct dictht { //哈希表
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigne