看完这个还不了解redis的SDS,半夜你来扒我家墙头

本文深入解析Redis如何通过简单动态字符串(SDS)确保二进制安全存储。介绍了SDS的结构、内存分配策略及其如何避免内存溢出等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述:

我们都知道redis底层是用C语言实现的,而C的字符串是以‘\0’来判断结束的,是一个对特殊字符存储不安全的。那redis是怎么做到存储二进制安全的呢?

重头戏-redis中的字符串

redis中还是有用到C中字符串的地方,比如一些不需要修改的字面量一些reids要输出的日志信息等。而需要修改的字符串redis抽象成了我们的SDS对象:简单动态字符串(simple dynamic string,SDS)。

SDS的定义

每个 sds.h/sdshdr 结构表示一个 SDS 值,结构如下:

struct sdshdr {

    // 记录 buf 数组中已使用字节的数量
    // 等于 SDS 所保存字符串的长度
    int len;
    // 记录 buf 数组中未使用字节的数量
    int free;
    // 字节数组,用于保存字符串,
    char buf[];

};

我们的buf中存放的就是我们真正的按照\0结束的字符串内容(在3.2版本之后此处改成了一个指针会根据不同长度指向不同的结构),len是我们的字符串内容的长度。还可以看到有一个free的子段,这个子段的作用先卖个关子下边说

SDS中free的作用

在c语言中使用不当经常会有内存溢出的问题,导致我们的字符串被覆盖,而redis是怎么避免这种问题的出现的呢?

首先我们在redis中插入一个key:

set demo hello;
这个时候的SDS对象是:

struct sdshdr {
    int len; = 5
    int free; = 0
    char buf[]; = 'hello'

};

set demo ‘hello world’;
这个时候的SDS对象是:

struct sdshdr {
    int len; = 11
    int free; = 11
    char buf[]; = 'hello world'

};
> set demo 'hello world wang';
struct sdshdr {
    int len; = 16
    int free; = 6
    char buf[]; = 'hello world wang'

};

我们在set hello world之后 redis会根据我们的free子段去判断需不需要向操作系统申请内存,如果free够用则不申请。这个时候就涉及到redis的内存分配策略了,为了避免多次向操作系统申请内存的开销,redis在每次申请都会按照当前len的两倍去申请(当len于1M时候就只会多申请1M),也就可以解释我们的free为什么是11了,这就是redis的空间预分配。
我们可以看下redis-5源码涉及到内存分配的策略,扩容是这个方法XDM可以去看看 sds sdsMakeRoomFor(sds s, size_t addlen)


还有一个惰性空间释放的概念会涉及到我们的free子段,当我们将redis的字符串内容减少的时候,redis并不会立马释放内存而是仅仅修改free子段,以便下次使用。

总结

  • 因为SDS有存储字符串长度len,所以我们获取redis字符串长度是一个O(1)的操作。
  • len的存在避免了内存溢出的问题
  • 通过内存分配策略,减少了字符串修改时候的内存分配次数

[2021最新完整面试题及答案(都整理成文档),有很多干货,包含mysql,netty,spring,线程,spring cloud、JVM、源码、算法,详细的学习规划图等资料,需要这些内容的朋友请扫码加vx获取哦~
写作不易!请点赞、关注、评论给作者一个鼓励吧~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值