• Redis5设计与源码分析读后感(二)简单动态字符串SDS


    一、引言

      学习之前先了解几个概念:  

      SDS定义:简单动态字符串,Redis的基本数据结构之一,用于储存字符串和整型数据。

      二进制安全:C语言中用""表示字符串结束,如果字符串本身就有这个字符,那么此字符串会被阶段,此时为非二进制安全;若通过某种机制保证读写字符串时不损害其内容,则称为二进制安全。

      字节对齐:字节按照一定规则在空间上排列。(不按规则排列有些架构CPU进行访问时会引起错误或者影响读取效率)。

    PS:如4字节对齐的意思是4字节为一个对齐单位,1字节对齐的意思就是连续存放。

      柔性数组:伸缩性数组,大小待定的数组,结构体和地址连续,查找内存更快,不用额外的指针

    二、新旧版本间的字符串结构对比

    5.0之前版本的SDS结构

    5.0版本的SDS结构

    sdshdr5

     

    • flags:低3位表示类型,高5位表示长度
    • buf:柔性数组,存放实际内容

    sdshdr16

      由于sdshdr8、sdshdr16、sdshdr32、sdshdr64结构类似,这里以sdshdr16为例子解析:

    • len:表示buf中已占用字节数
    • alloc:表示buf中已分配的字节数,不同于free,记录的是为buf分配的总长度
    • flags:标识当前结构体的类型,低3位表示类型,高5位闲置预留
    • buf:柔性数组,真正储存字符串的数据空间,存放实际内容

    三、字符串结构特点

    5.0之前版本的SDS结构

    • 有单独的统计变量len和free,可以很方便地得到字符串长度。
    • 内容存放在柔性数组中,SDS对上层暴露的是直接指向柔性数组buf的指针,兼容C语言处理字符串的各种函数。
    • 有长度统计变量len,读写字符串不依赖“”终止符,保证了二进制安全

    5.0版本的SDS结构

    • 字符串结构类型:短字符串【sdshdr5、sdshdr8】、长字符串【sdshdr16、sdshdr32】、更长字符串【sdshdr64】。
    • 包含字段:len【长度】、alloc【已分配的总长度】、flags【字符串结构类型,注意sdshdr5和其它的区别】、buf【柔性数组,储存字符串内容】。
    • _attribute_((_packed_)):结构体按1字节对齐节省空间,且地址连续,操作效率飞起

    小结

    • SDS暴露给上层的是指向柔性数组buf的指针。
    • 读操作的时间复杂度多为O(1),直接读取成员变量;涉及修改的写操作,则可能会触发扩容。

    四、常用的操作字符串函数

    • sdsnewlen :创建字符串
    • sdsfree :释放字符串【同时释放内存】
    • sdsclear :重置统计值,但buf并没有真正清除【不释放内存,减少申请内存时的开销】
    • sdscatsds :拼接字符串

    创建字符串

      创建字符串一般包含以下两点:

    • 计算好不同类型的头部和初始长度
    • 动态分配内存

      需要注意的点:

    • 创建空字符串时,SDS_TYPE_5 被强制转换为SDS_TYPE_8【应该是为了以后实例化的时候不用再去扩容而重新申请内存】
    • 长度计算时有"+1"操作,是为了算上结束符""
    • 返回值是指向 sds 结构中的 buf 字段的指针

    释放字符串

      释放字符串的方法一般有2种:

    • sdsfree释放字符串【同时释放内存】
    • sdsclear重置统计值,但buf并没有真正清除【不释放内存,减少申请内存时的开销】

    拼接字符串

      拼接字符串比较好理解,主要需要注意的是拼接过程中的扩容策略,扩容策略一般有以下几种:

    • 若sds中剩余的空闲长度avail大于新增内容的长度addlen,直接在柔性数组buf末尾追加即可,无需扩容
    • 若sds中剩余的空闲长度avail小于或等于新增内容的长度addlen:
      • 新增后总长度len+addlen<1MB :按照新长度的2倍扩容
      • 新增后总长度len+addlen>1MB :按照新长度+1MB扩容
    • 最后根据新长度重新选取储存类型【判断是否修改flags】,并分配空间:
      • 若无需更改,则只扩大柔性数组buf即可
      • 若需要更改,则重新开辟内存,并将原来的buf柔性数组移动到新位置

    扩容流程示例:

    五、小问答

    Q:SDS如何兼容C语言字符串?

    A:SDS对象的buf是柔性数组,且直接返回上层,而且是直接指向内容的指针,所以兼容C语言函数。

     

    Q:如何保证二进制安全?

    A:读取内容时,SDS会通过len来限制读取长度,不依赖于""终止符,所以保证了二进制安全。

     

    Q:sdshdr5的特殊之处?

    A:

    • 只负责储存小于32字节的字符串
    • 类型和长度放到同一个属性中,节省内存
    • 创建空字符串时,sdshdr5会被sdshdr8替代

     

    Q:SDS是如何扩容的?

    A:SDS在涉及字符串修改处会调用 sdsMakeRoomFor 函数进行检查,根据不同情况动态扩容,此操作对于上层调用方透明,即无感知。

     

  • 相关阅读:
    PHP开发APP接口(三)
    PHP开发APP接口(二)
    PHP开发APP接口(一)
    解密PHP模糊查询技术
    流程的问题
    德邦项目《表》
    微信公众号开发1
    在world2013中插入GB_2312
    HTML5笔记(一)
    蓝色文字显示
  • 原文地址:https://www.cnblogs.com/riches/p/13489543.html
Copyright © 2020-2023  润新知