Redis数据结构二之SDS和双向链表
本文首发于公众号:Hunter后端
原文链接:Redis数据结构二之SDS和双向链表
这一篇笔记介绍一下 SDS(simple dynamic string)和双向链表。
以下是本篇笔记目录:
- SDS
- 常数复杂度获取字符串长度
- 杜绝缓冲区溢出
- 减少修改字符串带来的内存重分配次数
- 二进制安全
- 兼容C字符串函数
- 双向链表
1、 SDS
SDS,simple dynamic string,即简单动态字符串
SDS 在 Redis 2.9 版本中数据结构如下:
struct sdshdr {
int len;
int free;
char buf[];
};
在这个结构中,len 表示 buf 数组中已使用字节的数量,free 表示 buf 数组中未使用字节的数量,buf 则表示是一个 char 类型的数组。
Redis 没有复用 C字符串,有以下几个方面的考虑和优点。
1. 常数复杂度获取字符串长度
C字符串并不记录自身的长度信息,如果要获取C字符串的长度,必须遍历整个字符串然后计数。
SDS 结构中有 len 属性记录 SDS 本身的长度,可以直接获取。
2. 杜绝缓冲区溢出
因为 C字符串并不记录自身的长度信息,在执行某些操作,比如拼接字符串的时候,并不会自动查询是否拥有足够内存,那么这个操作可能就会造成缓冲区溢出的问题
而 SDS 执行相应的字符串修改时,其 API 会先检查 SDS 的空间是否需求,不满足则会进行扩展,这个空间分配策略也就是下面要讲的
3. 减少修改字符串带来的内存重分配次数
C字符串每次进行字符串修改时,程序都需要手动进行内存重分配的操作,而 SDS 通过空间预分配和惰性空间释放两种策略对此进行了优化
空间预分配
当 SDS API 对一个 SDS 进行修改并需要对 SDS 进行空间扩展时,程序不仅会为 SDS 分配修改所需要的空间,还会为其分配额外的未使用空间
如果修改之后,SDS 的长度,也就是结构中的 len 属性小于 1MB,那么程序会额外分配同样大小的未使用空间,这个时候,len 属性和 free 属性将相同
如果修改之后,SDS 的长度,也就是结构中的 len 属性大于等于 1MB,那么程序会额外分配 1MB 的未使用空间
惰性空间释放
当需要对SDS保存的字符串进行缩短时,程序并不会重新分配内存来回收多出来的字节,而是会使用 free 属性将这些字节记录下来,以备后面使用
4. 二进制安全
C字符串保存的字符结尾都是以空字符结尾,所以字符串中间不能包含空字符,否则程序读入空字符的时候就会被认为是字符串结尾,因此C字符串只能保存文本数据,不能保存图片、音频等这样的二进制数据
而 SDS 的 API 都是以处理二进制的方式来处理 SDS 中存放在 buf 里的数据,程序不会对数据做任何限制、过滤,所以 SDS 的 API 都是二进制安全的
SDS 使用 len 属性值而不是空字符串来判断字符串是否结束
5. 兼容C字符串函数
虽然SDS的API都是二进制安全的,但是仍然遵循C字符串以空字符结尾的惯例,而且在为 buf 数组分配空间的时候总是会多分配一个字节来容纳这个空字符,所以保存文本数据的 SDS 可以重用一部分C中的函数
以下是 SDS 与 C字符串区别的总结:
| C字符串 | SDS |
|---|---|
| 获取字符串长度复杂度为 O(N) | 获取字符串长度复杂度为O(1) |
| API是不安全的,可能会造成缓冲区溢出 | API是安全的,不会造成缓冲区溢出 |
| 修改字符串长度N次必须执行N次内存重分配 | 修改长度N次最多需要执行N次内存重分配 |
| 只能保存文本数据 | 可以保存文本或者二进制数据 |
| 可以使用<string.h>库中函数 | 可以使用部分 |
在之后的的 Redis 版本对 SDS 的结构有过更新,将 free 属性换成了 alloc,这个属性表示的意思是分配的空间长度。和之前的 free 属性比较,其关系是 alloc = free + len
2、 双向链表
C 语言没有链表这个结构,所以 Redis 自己设计了一个链表数据结构。
在 Redis 中,链表节点的结构拥有指向前置节点和后置节点的属性。
链表结构则包含链表表头节点、表尾节点、节点长度等属性,便于快速获取链表相关信息。
双向链表是列表对象的底层实现之一,什么情况下使用双向链表作为列表对象的底层实现我们之后再介绍。
以下是链表节点的结构:
typedef struct listNode{
// 前置节点
struct listNode *prev;
// 后置节点
struct listNode *next;
// 节点值
struct *value;
}listNode;
在链表节点中,拥有前置节点和后置节点的指针构成双向的链表。
以下是链表的结构:
typedef struct list{
// 表头节点
listNode *head;
// 表尾节点
listNode *tail;
// 链表包含的节点数量
unsigned long len;
...
}list;
在链表结构中,有表头节点和表尾节点可快速定位到链表的头部和尾部,以及用有 len 属性表示链表包含的节点数量。
如果想获取更多后端相关文章,可扫码关注阅读:

Redis数据结构二之SDS和双向链表的更多相关文章
- 京东云开发者| Redis数据结构(二)-List、Hash、Set及Sorted Set的结构实现
1 引言 之前介绍了Redis的数据存储及String类型的实现,接下来再来看下List.Hash.Set及Sorted Set的数据结构的实现. 2 List List类型通常被用作异步消息队列.文 ...
- Redis数据结构之字符串-SDS
C语言中,传统的字符串表示是以空字符结尾的字符数组,Redis的字符串没有直接使用该表示,而是选择构建了一种名为简单动态字符串(simple dynamic string, SDS)的抽象类型. 在R ...
- 分布式缓存技术redis学习系列(二)——详细讲解redis数据结构(内存模型)以及常用命令
Redis数据类型 与Memcached仅支持简单的key-value结构的数据记录不同,Redis支持的数据类型要丰富得多,常用的数据类型主要有五种:String.List.Hash.Set和Sor ...
- 分布式缓存技术redis学习(二)——详细讲解redis数据结构(内存模型)以及常用命令
Redis数据类型 与Memcached仅支持简单的key-value结构的数据记录不同,Redis支持的数据类型要丰富得多,常用的数据类型主要有五种:String.List.Hash.Set和Sor ...
- Redis数据结构之sds基本操作函数
本文及后续文章,Redis版本均是v3.2.8 本篇文章讲解sds基本操作函数,我们从源码角度来进一步理解. 一.sds创建函数和销毁 sds创建函数 /* Create a new sds stri ...
- Redis数据结构之简单动态字符串SDS
Redis的底层数据结构非常多,其中包括SDS.ZipList.SkipList.LinkedList.HashTable.Intset等.如果你对Redis的理解还只停留在get.set的水平的话, ...
- redis学习(二) redis数据结构介绍以及常用命令
redis数据结构介绍 我们已经知道redis是一个基于key-value数据存储的数据结构数据库,这里的key指的是string类型,而对应的value则可以是多样的数据结构.其中包括下面五种类型: ...
- Redis 数据结构-双向链表
Redis 数据结构-双向链表 最是人间留不住,朱颜辞镜花辞树. 1.简介 Redis 之所以快主要得益于它的数据结构.操作内存数据库.单线程和多路 I/O 复用模型,进一步窥探下它常见的五种基本数据 ...
- redis数据结构存储SDS设计细节(redis的设计与实现笔记)
redis虽说是用C语言开发的,但是redis考虑了性能.安全性.效率性.功能等要,redis底层存储字符串实现,自己实现了名为简单动态字符串(Simple dynamic string)简称SDS的 ...
- Redis系列(二):Redis的数据类型及命令操作
原文链接(转载请注明出处):Redis系列(二):Redis的数据类型及命令操作 Redis 中常用命令 Redis 官方的文档是英文版的,当然网上也有大量的中文翻译版,例如:Redis 命令参考.这 ...
随机推荐
- 7. 基础增删改 - 使用Portal Webapi进行会员信息的增删改
我们可以通过使用Portal Web API在Portal页面中跨所有Microsoft Dataverse表执行创建.更新和删除操作,下面我们就一起来看一下如何通过使用AJAX函数来进行操作. AJ ...
- koa中间件的实现原理
koa中间件的实现原理如何?先来看一个例子. koa的执行顺序是这样的: const middleware = async function (ctx, next) { console.log(1) ...
- nginx的location与proxy_pass配置超详细讲解及其有无斜杠( / )结尾的区别
本文所使用的环境信息如下: windows11 (主机系统) virtual-box-7.0环境下的ubuntu-18.04 nginx-1.22.1 (linux) 斜杠结尾之争 实践中,nginx ...
- day12-SpringBoot数据库操作
SpringBoot数据库操作 1.JDBC+HikariDataSource 在SpringBoot 2.x项目中,默认使用Hikari连接池管理数据源.相比于传统的 C3P0 .DBCP.Tomc ...
- VUE中的$set与$delete的原理
我们上文说了,Vue 是通过 Object.defineProperty 和重写数组的原型方法来达到监控数据的目的.但是,在某些情况下,上面两种方案无法做到监控数据的变化,例如: (1):当我们给对象 ...
- 西瓜视频的li绑定容器 踏坑之旅
一定要绑定key,不然会出现一个li里面渲染出两个video标签
- Chrome浏览器插件 Undo Close Tab (恢复关掉的标签页)
背景 如果您经常使用Chrome浏览器,也许有时候会意外关闭一个标签页,从而丢失您正在查看的内容.这时您可能会感到非常烦恼,并希望能够迅速找回这个标签页.当然,您可以通过点击浏览器历史记录中的条目来找 ...
- Kubernetes集群调度增强之超容量扩容
作者:京东科技 徐宪章 1 什么是超容量扩容 超容量扩容功能,是指预先调度一定数量的工作节点,当业务高峰期或者集群整体负载较高时,可以使应用不必等待集群工作节点扩容,从而迅速完成应用横向扩容.通常情况 ...
- 苞米豆的多数据源 → dynamic-datasource-spring-boot-starter,挺香的!
开心一刻 2023年元旦,我妈又开始了对我的念叨 妈:你到底想多少岁结婚 我:60 妈:60,你想找个多大的 我:找个55的啊,她55我60,结婚都有退休金,不用上班不用生孩子,不用买车买房,成天就是 ...
- docker未授权攻击利用复现
环境配置 受害机:CentOS 攻击者:kali 配置docker配置文件,使得测试机存在未授权访问 vim /usr/lib/systemd/system/docker.service 原本[Ser ...