Redis设计与实现笔记 2.简单动态字符串

Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组),而是自己构建Simple Dynamic String(SDS),名为简单动态字符串的抽象类型,并将SDS用作Redis的默认字符串表示。

2.1 SDS的定义

struct sdshdr {
    //记录buf数组中已使用字节的数量
    //等于SDS所保存字符串的长度
    int len;

    //记录buf数组中为使用字节的数量
    int free;
    
    //字节数组,用于保存字符串
    char buf[];
};

1.一个例子

digraph {    label = "\n å¾ 2-2    带ææªä½¿ç¨ç©ºé´ç SDS 示ä¾";    rankdir = LR;    node [shape = record];    //    sdshdr [label = "sdshdr | free \n 5 | len \n 5 | <buf> buf"];    buf [label = "{ 'R' | 'e' | 'd' | 'i' | 's' | '\\0' | | | | | }"];    //    sdshdr:buf -> buf;}

 

     free = 5;

     len = 5;//不包含空字符

2.SDS遵循C字符串以空字符结尾的惯例,保存空字符的1字节空间不计算在SDS的len属性里。(自动管理,对上层透明)

     好处:可以直接重用一部分C字符传函数库里的函数

2.2 SDS与C字符串的区别

  1. 常数复杂度获取字符串长度
    strlen函数:c字符串并不记录自身的长度信息,strlen所作的仅仅是一个计数器的工作,它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符'\0'为止,然后返回计数器值(长度不包含'\0')。其时间复杂度为O(n)。
    SDS:保存了len属性,其时间复杂度为O(1)。STRLEN命令。
  2. 杜绝缓冲区溢出
    C字符串不记录自身长度带来的另一个问题是容易造成缓冲区溢出,比如strcat,strcpy函数。
    SDS的空间分配策略完全杜绝了发生缓冲区溢出的可能性。
  3. 减少修改字符串时带来的内存重分配次数
    3.1  C字符串:并不记录自身的长度,其底层实现总是一个N+1个字符长的数组(额外的一个字符空间用于保存空字符)。因为字符传的长度和底层数组的长度之间存在着这种关联性,所以每次增长或缩短一个C字符串,程序都总要对保存这个C字符串的数组进行一次内存重分配操作。因为内存重分配涉及复杂的算法,并且可能需要执行系统调用,所以它通常是一个比较耗时的操作(对于Redis这种速度要求严苛,数据频繁被修改的数据库,不可接受)。
    3.2  SDS通过未使用空间解除了字符串长度和底层数组长度之间的关联。通过未使用空间,SDS实现了空间育分配和惰性空间释放两种优化策略。
        A.空间预分配:1.如果对SDS进行修改后,len将<1MB,那么程序分配和len属性同样大小的未使用空间。(len+free+1)
                                 2.如果对SDS进行修改后,len将>=1MB,那么程序分配1MB的未使用空间。(len+1MB+1)
        B.惰性空间释放:用于优化SDS的字符缩短操作。(并不马上收回空间,可以自定义释放)
  4. 二进制安全

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

    SDS的字符串都是二进制安全的(binary-safe),所有SDS API都会以处理二进制的方式来处理SDS存放在buf数组里的数据,程序不会对其中的数据做任何限制,过滤或着假设,数据在写入时是什么样的,它被读取时就是什么样。(这也是将SDS的buf属性成为字节数组的原因----Redis不是用这个数组来保存字符,而是用它来保存一系列二进制数据)。
  5. 兼容部分C字符串函数
    避免了不必要的代码重复。
  6. C字符串与SDS之间的区别
    C字符串SDS
    获取字符串长度的复杂度为O(N)获取字符串长度的复杂度为O(1)
    API是不安全的,可能会造成缓冲区溢出API是安全的,不会造成缓冲区溢出
    修改字符串长度N次必然需要执行N次内存重分配修改字符串长度N次最多需要执行N次内存重分配
    只能保存文本数据可以保存文本或二进制数据
    可以使用所有<string.h>库中的函数可以使用一部分<string.h>库中的函数

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值