Redis基础知识
概念:
Redis(Remote Dictionary Server)是一个在内存中运行的数据结构的存储服务器(an in-memory data structure store)。Redis支持各种抽象数据结构,例如字符串,列表,映射,集合,排序集合,HyperLogLogs,位图,流和空间索引。
用途:
常被用于分布式、键值对数据库、高速缓存、消息代理等应用。
redis的优点
- 内存操作,高性能
- 单线程执行,天然支持并发
Redis的底层数据结构
简单动态字符串:SDS
描述
Redis由C语言编写。它将SDS(simple dynamic string)用作默认的字符串表示方式。而C字符串仅用作字符串字面量(string literal),即无须对字符串值进行修改的地方。Redis的字符串类型的键值都是由SDS实现的。此外,SDS还可以被用作缓冲区(buffer),例如AOF缓冲区、输入缓冲区等。
定义
SDS遵循C字符串以空字符结尾的惯例,保存空字符的1字节空间不计算在SDS的len属性里。该空字符的添加操作由SDS函数自动完成。这一操作的好处是,可以直接复用C语言的一些字符串操作。
每个sds.h/sdshdr表示一个SDS值,SDS的定义如下:
struct sdshdr {
int len; // 记录buf数组中已使用的字节数量,等于SDS所保存字符串的长度
int free; // 记录buf数组中未使用的字节数量
char buf[]; // 字节数组,用于保存字符串
}
特性
前置知识点:
内存的重新分配操作可能出现的问题
- 缓冲区溢出:增长字符串时,程序需要通过内存分配来来确定是否需要对底层数组的空间进行扩充,以便存放增长后的字符串的值。如果忘了这一步,那么保存字符串的数组就会发生越界行为,占用未分配给它的内存区域,从而导致其他数据被意外篡改,这就是缓冲区溢出。
- 内存泄漏:缩短字符串时,同样需要重新分配内存释放掉不需要的那部分空间。如果忘了这一步,那么剩下那一部分空间将会一直处于未使用状态且无法分配给其他程序来使用,这就是内存泄漏。
- 内存重分配设计辅助的算法,并且可能需要执行系统调用,所以它通常是一个比较耗时的操作。
1. 空间预分配
描述:空间预分配操作用于优化SDS字符串的增长操作。
实现:
- 当修改后的SDS长度小于1MB时,那么程序将分配和len相同大小的未使用空间。这时len的值和free的值相同。
- SDS的长度大于等于1MB时,那么会分配1MB的未使用空间。
作用:通过内存重分配和空间预分配的策略,Redis可以减少连续执行字符串增长操作所需的内存重分配次数,从而提高性能
2. 惰性空间释放
描述:用于优化SDS字符串缩短操作。
实现:
- 缩短字符串时,程序并不立即使用内存重分配来回收缩短后多出的字节,而是使用free属性将这些字节的数量记录起来,并等待将来使用。
作用:SDS避免了缩短字符串时所需的内存重分配操作。并为将来可能出现的增长操作提供了优化;不过有可能造成内存空间的浪费。
3. 二进制安全
描述:SDS的API都是二进制安全的。所有SDS API都会以处理二进制的方式来处理SDS存放在buf数组里的数据。
4. 兼容部分C字符串函数
描述:SDS末尾保存的空字串,使得其可以重用一部分<string.h>库定义的函数,从而避免了不必要的代码重复实现。
与C字符串相比的区别
- 长度获取简单。SDS用len属性记录了SDS字符串的长度,因此只需常熟复杂度就可以获取到字符串长度,而C字符串并不记录长度,它需要遍历整个字符串才能得出字符串长度。
- len属性解决了扩充时C字符串的缓冲区溢出(buffer overflow)问题;
- free属性减少修改字符串时带来的内存重分配次数。
链表
描述
- 链表提供了高效的节点重排能力,以及顺序性的节点访问方式。
- 链表在redis中的应用非常广泛,列表键的底层实现之一就是链表(当一个列表键包含了数量比较多的元素,又或者列表中包含的元素都是比较长的字符串时)。
- 此外,发布与订阅、慢查询、监视器等功能也用到了链表,Redis本身还是用链表保存多个客户端的状态信息。
定义
adlist.h/list表示一个链表,而每个链表节点使用一个adlist.h/listNode结构表示。
typedef struct list {
listNode *head;
listNode *tail;
unsigned long len; // 节点数量
} list;
typedef struct listNode {
s

最低0.47元/天 解锁文章
2251

被折叠的 条评论
为什么被折叠?



