【Redis】动态字符串SDS

简介: 【Redis】动态字符串SDS

前言

首先,Redis中的key使用的是字符串,而value则有各种类型,不过多数为字符串。

因此字符串是Redis中最常用的一种数据结构。

Redis虽然使用了C语言类实现,但是并没有直接使用C语言的字符串,原因有如下几点:

  • 本质为字符数组,计算长度麻烦
  • 通过特定标识作为字符串结尾,若value中有该标识则可能出现字符串保存错误问题
  • 通过指针指向数组,不方便修改,只能使得指针指向另一字符串

综上,如果直接使用C语言中的字符串,那么将会带来一些不必要的麻烦。

因此Redis采用了类似于Java中字符串实现方式的方式来实现。

但是C语言并没有类,因此C语言使用的是结构体来完成这一结构。

该结构称为简单动态字符串(Simple Dynamic String)SDS

我们插入一个以字符串作为键值对的数据的时候,Redis就会在底层创建两个SDS

其中一个为key,另一个为value。

下面通过源码来讲解这一结构。

源码

如果没有学过C语言的,可以将结构体类比于Java中的类。

这里 struct 就类似于class

这里我们以sdshdr8为例(sdshdr5由于一些问题基本已经废弃)

uint8_t 代表的是unsigned int,并且这个int类型的长度为8bit。也就是最大255。

len:当前buf中已经保存的字符的长度

alloc:为当前buf申请的字节数大小,长度不一定等于len

flags:当前SDS的头类型,由于len的长度受限于数据类型,因此如果只有255可能保存不下,需要有更大的长度,而这个flags就用于标识当前无符号int数据类型的长度。

可选8,16,32,64(单位为bit)

buf:SDS中具体的数据

数据安全

例如一个字符串name,其SDS结构如下:

C语言结构体将会开辟一段连续的空间,该空间的大小为结构体内所有属性所需空间的和。

首先name的长度为4,并且第一次开辟的内存长度一般于与len一样,因此alloc也为4,由于使用的是sdshdr8,因此flags为1。之后为buf中的数据,其中保存的是具体的SDS所要保存的数据,这里为name\0,其中\0代表的是结束符。

同时由于SDS头中已经设定了len为4,因此Redis必须读取4个字符之后才会结束,因此即使当前buf中有结束标识\0也不会被当作真的结束标识,Redis依旧会继续向后读,这就解决了数据安全问题,如果是传统C语言,那么如果字符串中有结束标识\0,之后的数据就会直接被抛弃,会造成数据安全问题。而Redis的这种设计就解决了这一问题。

动态

SDS之所以被叫做动态字符串,是因为其具备动态扩容的能力。依旧以上面的SDS为例,假设我们需要向其追加一段内容“dog”,那么这里首先需要先申请内存空间,而在申请内存空间的时候就会设计以下情况:

  • 如果追加后生成的新字符串小于1M,则新空间为扩展后字符串长度的2倍+1
  • 如果追加后生成的新字符串大于1M,则新空间为扩展后字符串长度+1M+1,我们成为内存预分配。

这里之所以需要预分配是因为当我们申请内存的时候,需要进行用户态和内核态的切换。应用程序默认运行的环境为用户态,当应用程序需要进行一些特殊操作(例如读取文件,申请空间,杀死其他进程等)时,由于用户态默认是不安全的,因此需要进行用户态到内核态的切换,然后进入内核态之后在进行内存空间的分配。而申请内存这一过程极度消耗资源,因此需要尽量减少多次申请内存空间。而内存的预分配能尽可能的减少这一情况。

这是扩展后sdshdr8中的内容。

总结

Redis实现的SDS有如下优点:

  • 获取字符串长度的时间复杂度为O(1)
  • 支持动态扩容
  • 内存预分配,减少内存分配次数
  • 二进制安全,不会由于遇到结束符而提前结束读取


相关文章
|
存储 缓存 NoSQL
redis数据结构-字符串
redis数据结构-字符串
253 1
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
435 32
|
NoSQL Redis
Redis 字符串(String)
10月更文挑战第16天
270 4
|
存储 缓存 NoSQL
3)深度解密 Redis 的字符串
3)深度解密 Redis 的字符串
268 1
|
存储 NoSQL Redis
Redis常见面试题:ZSet底层数据结构,SDS、压缩列表ZipList、跳表SkipList
String类型底层数据结构,List类型全面解析,ZSet底层数据结构;简单动态字符串SDS、压缩列表ZipList、哈希表、跳表SkipList、整数数组IntSet
|
C# 开发者 UED
WPF开发者必备秘籍:深度解析文件对话框使用技巧,打开与保存文件原来如此简单!
【8月更文挑战第31天】在WPF应用开发中,文件操作是常见需求。本文详细介绍了如何利用`Microsoft.Win32`命名空间下的`OpenFileDialog`和`SaveFileDialog`类来正确实现文件打开与保存功能。通过示例代码展示了如何设置文件过滤器、初始目录等属性,并使用对话框进行文件读写操作。正确使用文件对话框能显著提升用户体验,使应用更友好易用。
838 0
|
NoSQL 安全 Shell
Redis源码学习——基础数据结构之SDS
###Redis数据结构-SDS Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。 首先介绍下Redis的基础数据结构 —— SDS Redis没有使用传统C语言的字符串(字符数组)表示。而是自己构建了一种名为sds(Simple Dymamic String)的抽象类型,作为redis的默认字符类型。 SDS用于保存数据库中的
4231 0
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
缓存 NoSQL Java
Redis+Caffeine构建高性能二级缓存
大家好,我是摘星。今天为大家带来的是Redis+Caffeine构建高性能二级缓存,废话不多说直接开始~
1860 0
|
11月前
|
缓存 负载均衡 监控
135_负载均衡:Redis缓存 - 提高缓存命中率的配置与最佳实践
在现代大型语言模型(LLM)部署架构中,缓存系统扮演着至关重要的角色。随着LLM应用规模的不断扩大和用户需求的持续增长,如何构建高效、可靠的缓存架构成为系统性能优化的核心挑战。Redis作为业界领先的内存数据库,因其高性能、丰富的数据结构和灵活的配置选项,已成为LLM部署中首选的缓存解决方案。
995 25