美文网首页
Redis 设计与实现 -- 阅读笔记

Redis 设计与实现 -- 阅读笔记

作者: guoweikuang | 来源:发表于2018-11-04 12:19 被阅读11次
    image

    一、简单动态字符串(SDS)

    简单动态字符串(simple dynamic string, SDS) 是 Redis 实现的一个用于保存字符串的数据结构,Redis 没有使用C 语言传统的字符串表示。

    比如:

    redis> set msg "hello, world"
    创建一个键值对
    键值对的 键 是一个字符串对象,对象的底层实现是一个保存着字符串"msg"的SDS
    键值对的 值 是一个字符串对象,对象的底层实现是一个保存着字符串"hello, world" 的 SDS
    

    SDS 的定义

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

    比如保存一个 "Redis" 字符串:

     ------
    |sdshdr|
     ------
    | free |
    |  0   |
     ------
    |  len |
    |   5  |
     ------      
    | buf  |   ----->   | 'R' | 'e' | 'd' | 'i' | 's' | '\0' |
     ------ 
    
    
    free 值为0,表示这个 SDS 没有分配任未使用空间,如果free > 0, 这个 SDS 为 buf 数组分配对应值的未使用空间,比如 free = 5, 会在 buf 数组的 "\0" 后分配5字节未使用的空间
    len  值为5,表示这个 SDS 保存了一个5字节长的字符串
    

    SDS 和 C 字符串的区别

    SDS 比 C 字符串更适用于 Redis 的原因?

    1、C 字符串不记录自身的长度,导致获取长度需要遍历字符串, 复杂度为O(N)。而 SDS 结构的 len 属性记录了 SDS 本身的长度,复杂度为O(1)
    2、当使用 char *strcat(chart *dest, const chart *src) 函数时,如果 dest 没有分配足够的内存容纳 src, 将产生缓冲区溢出的问题,导致溢出到相邻的字符的空间中,修改了相邻字符的内容
    3、SDS 空间分配策略会检查 SDS 的空间是否满足所需的需求,然后通过 SDS API 自动将空间扩展到所需的大小,就不会出现缓冲区溢出问题
    4、减少修改字符串时带来的内存重分配次数,如果是增长字符串的操作,执行操作之前会通过内存重分配来扩展底层数组的空间大小,如果是缩短字符串的操作,执行操作之后通过内存重分配来释放字符串不再使用的部分空间。内存重分配涉及复杂的算法,是一个耗时操作
    

    C 字符串 与 SDS 区别

    C 字符串 SDS
    获取字符串长度复杂度O(N) 获取字符串长度复杂度O(1)
    API 不安全,容易缓冲区溢出 API 安全,缓冲区无溢出
    修改字符串长度N次需要执行 N 次内存重分配 修改字符串长度N次<= N次内存重分配
    只能保持文本数据 保持文本及二进制数据
    使用所有库函数 只能使用部分库函数

    SDS 空间预分配和惰性空间释放策略


    1、空间预分配

    空间预分配用于优化 SDS 的字符串增长操作,其实就是当需要对 SDS 空间扩展时,除了分配所需空间外,还会为 SDS 分配额外的未使用空间。额外分配的空间有两种情况:

    1、修改 SDS 后, SDS 的长度(len属性) < 1MB, 将分配和 len 大小一样的未使用空间,len 的值和 free 值相同
    (如修改后 SDS = 13 字节,程序分配了 13 字节未使用空间,SDS 的 buf 数组长度 = 13 + 13 + 1 = 27 字节)
    2、修改 SDS 后,SDS 的长度 > 1MB, 将分配 1MB 的未使用空间
    (如修改后 SDS = 20MB,SDS 的 buf 数组长度 = 20MB + 1MB + 1byte(保存空字符))
    

    2、惰性空间释放

    惰性空间释放用于优化 SDS 的字符串缩短操作,当 SDS API 需要缩短 SDS 保存的字符串长度时,不立即使用内存重分配回收,而是使用 free 记录这些字节的数量。不释放多出来的字节空间,如果这时候对 SDS 进行增长操作,这些未使用空间就可以使用。

    总结

    Redis 使用 C 字符串作为字面量,在大多数情况下,使用 SDS (简单动态字符串) 作为字符串表示,还有对比了 C 字符串与 SDS 的区别,并指出 SDS 的优势等

    相关文章

      网友评论

          本文标题:Redis 设计与实现 -- 阅读笔记

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