Redis中Srting类型的底层实现-SDS

作者: 老男孩_Misaya | 来源:发表于2020-09-02 21:04 被阅读0次

推荐阅读:

1、什么是SDS?

sds是c中的一个数据结构,如下:

struct sdshdr{
    //buf数组中已使用的字节数,即字符串长度
    int len;
    //buf数组未使用的字节数
    int free;
    //存储字节的数组
    int[] buf;
}

redis中用这种数据结构来保存string类型

2、为什么redis要用SDS来存储字符串?

c/c++ 中 char 数组也可以存储字符串,而且c就是这么干的, C 语言使用长度为 N+1 的字符数组来表示长度为 N 的字符串, 并且字符数组的最后一个元素总是空字符 '\0' 。

我们分析一下以下几种情况char数组和sds的区别:

2.1. 长度的获取。

c中字符串长度获取需要遍历char数组,时间复杂度是O(n)

sds直接记录了字符串的长度,所以长度获取的时间复杂度是O(1)

2.2. 缓冲区溢出(buffer overflow)

除了获取字符串长度的复杂度高之外, C 字符串不记录自身长度带来的另一个问题是容易造成缓冲区溢出(buffer overflow)。 <string.h>/strcat 函数可以将 src 字符串中的内容拼接到 dest 字符串的末尾,当dest拼接的src的长度超过自身分配的地址大小时,就会修改到别的地址,造成缓冲区溢出。

SDS 的空间分配策略完全杜绝了发生缓冲区溢出的可能性: 当 SDS API 需要对 SDS 进行修改时, API 会先检查 SDS 的空间是否满足修改所需的要求, 如果不满足的话, API 会自动将 SDS 的空间扩展至执行修改所需的大小, 然后才执行实际的修改操作, 所以使用 SDS 既不需要手动修改 SDS 的空间大小, 也不会出现前面所说的缓冲区溢出问题。

2.3. 空间预分配

空间预分配用于优化 SDS 的字符串增长操作: 当 SDS 的 API 对一个 SDS 进行修改, 并且需要对 SDS 进行空间扩展的时候, 程序不仅会为 SDS 分配修改所必须要的空间, 还会为 SDS 分配额外的未使用空间。

其中, 额外分配的未使用空间数量由以下公式决定:

如果对 SDS 进行修改之后, SDS 的长度(也即是 len 属性的值)将小于 1 MB , 那么程序分配和 len 属性同样大小的未使用空间, 这时 SDS len 属性的值将和 free 属性的值相同;

如果对 SDS 进行修改之后, SDS 的长度将大于等于 1 MB , 那么程序会分配 1 MB 的未使用空间。

举个例子: 如果进行修改之后, SDS 的 len 将变成 13 字节, 那么程序也会分配 13字节的未使用空间, SDS 的 buf 数组的实际长度将变成: 13 + 13 + 1 = 27 字节(额外的一字节用于保存空字符)。

如果SDS 的 len 将变成 30 MB , 那么程序会分配 1 MB 的未使用空间, SDS 的 buf 数组的实际长度将为 30 MB + 1 MB + 1 byte 。 通过空间预分配策略, Redis 可以减少连续执行字符串增长操作所需的内存重分配次数。

2.4. 惰性空间释放

简单的说,sds在使用sds api减少字符串长度的时候,并不会立马释放缩短后多出来的字节,万一下次又用到了呢,是吧。

2.5. 二进制安全

C 字符串中的字符必须符合某种编码(比如 ASCII), 并且除了字符串的末尾之外, 字符串里面不能包含空字符, 否则最先被程序读入的空字符将被误认为是字符串结尾 —— 这些限制使得 C 字符串只能保存文本数据, 而不能保存像图片、音频、视频、压缩文件这样的二进制数据。

虽然数据库一般用于保存文本数据, 但使用数据库来保存二进制数据的场景也不少见, 因此, 为了确保 Redis 可以适用于各种不同的使用场景, SDS 的 API 都是二进制安全的(binary-safe): 所有 SDS API 都会以处理二进制的方式来处理 SDS 存放在 buf 数组里的数据, 程序不会对其中的数据做任何限制、过滤、或者假设 —— 数据在写入时是什么样的, 它被读取时就是什么样。

这也是我们将 SDS 的 buf 属性称为字节数组的原因 —— Redis 不是用这个数组来保存字符, 而是用它来保存一系列二进制数据。

3、SDS的启发。

不要局限于现有的数据结构,多思考一下,结合实际应用场景选择符合自己的最佳结构。

空间换时间真是个不错的选择。

相关文章

  • Redis中Srting类型的底层实现-SDS

    推荐阅读: 这套Github上40K+star学习笔记,可以帮你搞定95%以上的Java面试 毫不夸张的说,这份S...

  • Redis 源码分析(一) :sds

    Redis 源码分析(一) :sds 什么是sds 字符串是Redis中最为常见的数据存储类型,其底层实现是简单动...

  • Redis源码学习基本数据结构之动态字符串sds

    一、sds是什么 字符串是Redis中最为常见的数据存储类型,其底层实现是简单动态字符串sds(simple dy...

  • 简单动态字符串(SDS)

    Redis中,包含字符串值的键值对在底层都是由SDS实现的 redis > SET msg "hello worl...

  • Redis底层数据结构

    Redis底层数据结构类型 简单动态字符串(simple dynamic string)SDS Redis 没有直...

  • Redis 动态字符串

    SDS是Redis底层使用的字符串的表示形式。 SDS用途 SDS主要用两方面作用: 1.实现字符串对像 Redi...

  • redis

    redis Redis 数据结构和底层实现string:简单动态字符串SDS,Redis 的字符串是动态字符串,是...

  • redis常用功能表述

    基础类型应用: String: String类型是Redis中最常使用的类型,内部的实现是通过SDS(Simple...

  • Redis对象(一) - 类型和编码

    对象 前边学习了Redis底层实现的各种数据结构, 包括SDS, list, skiplist, dict, in...

  • t_string.c

    Redis的t_string.c是对于string数据结构的实现。底层是基于 sds 及 dict 实现的。 首先...

网友评论

    本文标题:Redis中Srting类型的底层实现-SDS

    本文链接:https://www.haomeiwen.com/subject/umdvsktx.html