redis 字符串和列表实现

Redis 虽说由 C 语言实现,但用户直接操作的字符串绝大多数情况下均非 C 语言中以空字符结尾的字符串,而是一种封装了 C 字符串的称作简单动态字符串(simple dynamic string, SDS)的抽象结构,并将其作为 Redis 的默认字符串表示。
SDS 结构的定义如下:

struct sdshdr{
int len; // 记录 buf 数组中已使用的字节数,等于 SDS 所保存的字符串的长度
int free; // 记录 buf 数组中未使用的字节数
char buf[]; // 保存真正的字节数据,长度为:len + free + 1(额外的一字节用于保存末尾的空字符)
};

之所以采用 SDS 结构而非直接使用 C 风格字符串,主要是为了满足 Redis 对字符串在安全性、效率以及功能方面的要求。这主要表现在以下几方面。
1、获取字符串长度的复杂度
因为访问 C 字符串的长度则需要 O(N)(N 为字符串的长度),而根据 SDS 结构的定义可知,要获取其保存的字符串的长度,只需访问 len 属性即可,时间复杂度为 O(1),这避免了在 Redis 中反复执行 STRLEN 命令对系统性能造成的影响。
2、杜绝缓冲区溢出
SDS 的内存分配策略能保证在每次修改 SDS 底层存储的字符串时,buf 数组有足够的空间容纳新的字符串,从而避免了缓冲区溢出的可能。
3、使用了空间预分配和惰性空间释放来优化内存分配
当剩余的未使用空间 free 不能容纳修改后增长的字节长度时,使用空间预分配能减少连续执行字符串增长操作所需的内存重分配次数。具体的预分配策略算法如下:
a)若修改后 SDS 的字符串长度 len 小于 1MB,则分配和 len 同样大小的未使用空间 free,此时 SDS 的属性 len 和 free 的值相同。
b)若修改后 SDS 的字符串长度 len 不小于 1MB,则额外分配 free 为 1MB 的未使用空间。
而使用惰性空间释放则是指:在需要缩短 SDS 保存的字符串时,程序并不立即释放缩短后多出来的空间,而是追加到 free 属性中,以备将来使用。
4、二进制安全
由于 C 字符串是以空字符来区分字符串是否结束,所以这使得其只能保存文本数据,而不能保存数据中可能含有空字符的二进制数据(如图片、音频、视频和压缩文件等)。为了确保 Redis 可以适用于多种场景,SDS 的 API 都是二进制安全的(binary-safe)。这也是将 buf 称为字节数组的原因,因为 Redis 不是用它来保存字符,而是用来保存一系列二进制数据。因此 SDS 是使用 len 属性而非空字符来判断字符串是否结束。
5、兼容部分 C 字符串函数
尽管 SDS 的 API 都是二进制安全的,但它们依然遵循 C 字符串以空字符串结尾的惯例,这主要是为了让那些保存文本数据的 SDS 可以重用一部分 C 中的字符串函数。

Redis 中的列表结构是基于双端链表的,因此它既支持栈操作,也支持队列操作。除列表键值外,Redis 中的发布与订阅、慢查询、监视器等功能也都用到了链表结构。每个链表节点都使用一个 listNode 结构来表示,此外,为了方便操作,Redis 用了一个 list 结构来封装了 listNode 结构组成的链表。这两种结构的定义如下:

typedef struct listNode{
struct listNode *prev; // 前置节点
struct listNode *next; // 后置节点
void *value; // 节点值
}listNode;

typedef struct list{
listNode *head; // 表头节点
listNode *tail; // 表尾节点
unsigned long len; // 列表所包含的节点数量
void *(*dup)(void *ptr); // 节点值复制函数
void (*free)(void *ptr); // 节点值释放函数
int (*match)(void *ptr, void *key); // 节点值对比函数
}list;

为实现多态,以便链表可以用于保存各种不同类型的值,listNode 结构中使用了“void *”空指针来保存节点值,并通过 list 结构中的 dup、free 和 match 三个属性来为节点值设置类型特定函数。

参考:
1、《Redis 设计与实现》第二章——简单动态字符串。
2、《Redis 设计与实现》第三章——链表。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值