SDS(simple dynamic string)

SDS定义: 简单动态字符串

Redis3.x 版本

SDS结构:header部分+buf部分
header部分为:len+free字段
buf:为char[]

 //在c里面的结构体名称在java里面相当于类名,可以用sdshdr声明变量Aobing
struct  sdshdr{    //记录buf数组中已使用字节的数量//等于SDS所保存字符串的长度unsigned int len;//记录buf数组中未使用字节的数量unsigned int free;//char数组,用于保存字符串char buf[];
}

为什么字符串末尾加个\0字符?

buf 尾部自动追加一个’\0’字符并不会计算在 SDS 的len中,这是为了遵循 C 字符串以空字符串结尾的惯例,使得 SDS 可以直接使用一部分string.h库中的函数,如strlen
Redis3.x 版本中不同长度的字符串占用的头部是相同的 , 如果某一字符串很短但是头部却占用了更多的空间,这未免太浪费了 ,我们可以在 SDS 中新增一个 type 字段来标识类型,但是没必要使用一个 4 字节的int类型去做!可以使用 1 字节的char类型,通过位运算(3位即可标识2^3种类型)来获取类型。

Reids6.0版本

将 SDS 分为三种级别的字符串:

  • 短字符串(长度小于32),len和free的长度用1字节即可;
  • 长字符串,用2字节或者4字节;
  • 超长字符串,用8字节。
    共有五种类型的SDS(长度小于1字节、1字节、2字节、4字节、8字节)
 *// 注意:sdshdr5从未被使用,Redis中只是访问flags。*
struct __attribute__ ((__packed__)) sdshdr5 {unsigned char flags; */\* 低3位存储类型, 高5位存储长度 \*/*char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {uint8_t len; */\* 已使用 \*/*uint8_t alloc; */\* 总长度,用1字节存储 \*/*unsigned char flags; */\* 低3位存储类型, 高5位预留 \*/*char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {uint16_t len; */\* 已使用 \*/*uint16_t alloc; */\* 总长度,用2字节存储 \*/*unsigned char flags; */\* 低3位存储类型, 高5位预留 \*/*char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {uint32_t len; */\* 已使用 \*/*uint32_t alloc; */\* 总长度,用4字节存储 \*/*unsigned char flags; */\* 低3位存储类型, 高5位预留 \*/*char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {uint64_t len; */\* 已使用 \*/*uint64_t alloc; */\* 总长度,用8字节存储 \*/*unsigned char flags; */\* 低3位存储类型, 高5位预留 \*/*char buf[];
};
uint8_t / uint16_t / uint32_t /uint64_t 在c语言里面是别名

Redis为什么不对齐呢?

综上所述我们知道了对齐填充可以提高 CPU 的数据读取效率,作为 IO 频繁的 Redis 为什么选择不对齐呢?

我们再次回顾 Redis6.x 中的 SDS 结构

有个细节各位需要知道,即 SDS 的指针并不是指向 SDS 的起始位置(len位置),而是直接指向buf[],使得 SDS 可以直接使用 C 语言string.h库中的某些函数,做到了兼容,十分nice~。

如果不进行对齐填充,那么在获取当前 SDS 的类型时则只需要后退一步即可flagsPointer = ((unsigned char*)s)-1;相反,若进行对齐填充,由于 Padding 的存在,我们在不同的系统中不知道退多少才能获得flags,并且我们也不能将 sds 的指针指向flags,这样就无法兼容 C 语言的函数了,也不知道前进多少才能得到 buf[]。

为什么使用SDS?

1.相比C语言字符串,使获取字符串长度时间复杂度降为O(1)

C语言字符串不记录自身长度,如果想获取自身长度必须遍历整个字符串,对每个字符进行计数,这个操作时间复杂度是O(n)。相比较而言,Redis程序只要访问SDS的len属性就可以直接获取到字符串长度,时间复杂度为O(1),确保获取字符串长度不会成为Redis性能瓶颈,比如对字符串键反复执行strlen命令。如:获取“Redis”字符串长度时程序会直接访问len属性即可,该字符串长度为5。

2. 杜绝缓冲区溢出
假设程序里有两个在内存中紧邻的字符串s1s2,s1的值为Redis,底层数组的值为[‘R’,‘e’,‘d’,‘i’,‘s’,’\0’],s2的值为Memcache,底层数组的值为[‘M’,‘e’,‘m’,‘c’,‘a’,‘c’,‘h’,‘e’,’\0’],在C语言中如果要执行strcat(s1, " cluster")把s1修改为Redis cluster,如果忘记在执行strcat命令之前为s1重新分配空间,那么在执行完strcat命令之后,s1底层数组的值变为[‘R’,‘e’,‘d’,‘i’,‘s’,’ ‘,‘c’,‘l’,‘u’,‘s’,‘t’,‘e’,‘r’,’\0’],s2底层数组的值变为[‘c’,‘l’,‘u’,‘s’,‘t’,‘e’,‘r’,’\0’,’\0’],s1的数据溢出到s2所在的内存空间,s2的值被意外修改。与C语言不同,当SDS API需要对SDS进行修改时,API会先检查SDS当前剩余空间是否满足修改之后所需的空间,如果不满足的话API会自动将SDS的空间扩展至修改之后所需空间大小,然后再执行实际的修改操作,所以SDS不会出现缓冲区溢出问题。(缓冲区扩充并非是直接扩充到所需要的空间大小,它和SDS空间分配策略有关,参见下一小节)


3. 减少修改字符串时带来的内存重分配次数

C语言字符串底层是使用一个n+1个字符长度的char类型数据实现的,所以每次增长或缩短一个C语言字符串,程序都要对这个字符串数组进行一次内存重分配操作:

  • ① 如果程序执行的是增长字符串操作,比如strcat操作,在执行这个操作之前需要通过内存重分配扩展底层数组,如果忘记了则会造成缓冲区溢出。
  • ② 如果程序执行的是缩短字符串操作,比如trim操作,在执行这个操作之前需要通过内存重分配释放字符串不再使用的内存空间,如果忘记了则会造成内存泄漏。
    因为内存重分配涉及复杂的算法,并且可能需要执行系统调用,所以它通常是一个比较耗时的操作。Redis经常被用于速度要求严苛、数据被频繁修改的场合,如果每次修改字符串都需要执行一次内存重分配的话,那么对于性能会造成很大影响。

在SDS中通过未使用空间解除了字符串长度和底层数组长度之间的关联,在SDS中,buf数组长度不一定是字符串长度加1,数组中可能包含未使用的字节,这些字节的数量就是由SDS的free属性记录。通过未使用空间,SDS实现了空间预分配和惰性空间释放两种优化策略。

3.1 空间预分配

用于字符串增长操作,当字符串增长时,程序会先检查需不需要对SDS空间进行扩展,如果需要扩展,程序不仅会为SDS分配修改所必要的空间,还会为SDS分配额外的未使用空间,额外分配的未使用空间公式如下:

  • ① 如果对SDS修改之后,SDS的长度(修改之后len属性的值)小于1MB,那么则分配和len属性同样大小的未使用空间,这时SDS的len属性和free属性的值相同。如:如果修改之后SDS的len将变为10字节,那么程序也会分配10字节的未使用空间,SDS的buf数组实际长度变为10 + 10 + 1 = 21(额外一个字节用于保存结束符\n)
  • ② 如果对SDS修改之后,SDS的长度大于等于1MB,那么程序会分配1MB的未使用空间。如:修改之后的len将变为10MB,那么程序会分配1MB的未使用空间,SDS的bug数组长度为10MB + 1MB + 1byte

3.2 惰性空间释放

用于优化SDS的字符串收缩操作,当字符串收缩时,程序不会立即执行内存重分配来回收收缩后内存多出来的空间,而是使用free属性记录下来,以备将来使用。


二进制安全
什么是二进制安全?

通俗地讲,C语言中,用’0’表示字符串的结束,如果字符串本身就有’0’字符,字符串就会被截断,即非二进制安全;若通过某种机制,保证读写字符串时不损害其内容,则是二进制安全。
C字符串中的字符除了末尾字符为’\0’外其他字符不能为空字符,否则会被认为是字符串结尾(即使实际上不是)。

这限制了C字符串只能保存文本数据,而不能保存二进制数据。而SDS使用len属性的值判断字符串是否结束,所以不会受’\0’的影响。

Redis——SDS相关推荐

  1. Redis sds packed对齐理解

    Redis sds 的源码中,使用了 __attribute__ ((__packed__)) ,一般情况下,结构体会按其所有变量大小的最小公倍数做字节对齐,而用packed修饰以后,结构体则变为按1 ...

  2. redis sds的申请扩容源码

    sds是redis中简单的字符串实现 sdsnewlen()方法可以用来申请sds. sds sdsnewlen(const void *init, size_t initlen); 第一个参数是sd ...

  3. Redis源码分析(sds)

    源码版本:redis-4.0.1 源码位置:https://github.com/antirez/sds 一.SDS简介 sds (Simple Dynamic String),Simple的意思是简 ...

  4. c语言实现string sds,redis学习 - sds字符串

    redis学习 - sds字符串 Redis 设计与实现:如果想要知道redis底层,这本书可以给予不少的帮助,非常推荐每一位学习redis的同学去翻一翻. sds字符串建议多看看源代码的实现,这篇文 ...

  5. 一、redis原理之string底层数据结构SDS

    一.前言 我们说Redis 是用 C 语言写的,,但是对于Redis的字符串,却不是 C 语言中的字符串(即以空字符'\0'结尾的字符数组),它是自定义的数据结构SDS(simple dynamic ...

  6. TechTarget数据库Redis

    2019独角兽企业重金招聘Python工程师标准>>> http://www.searchdatabase.com.cn/topic/redis.htm Redis NoSQL标准缺 ...

  7. Redis 帝国的神秘使者,竟然想改造 C 语言!

    你好,我是悟空. 迎接使者大人 "吁····" 这声音从一辆豪华马车中传出,拉车的两匹马儿听到后,立马停在了路边. "先生,可有什么不对劲?"车夫谨慎地问道. ...

  8. 一文搞懂 Redis

    一 什么是NoSQL? Nosql = not only sql(不仅仅是SQL) 关系型数据库:列+行,同一个表下数据的结构是一样的. 非关系型数据库:数据存储没有固定的格式,并且可以进行横向扩展. ...

  9. 《Redis设计与实现》学习笔记

    Redis 本文会有一些Redis和Java容器对象的对比,一个是分布式数据库,一个是JVM内部数据容器,应用场景不同,仅仅是为了加深对Redis"数据库"的认识,加深对Redis ...

  10. 面渣逆袭:Redis连环五十二问!三万字+八十图详解!

    基础 1.说说什么是Redis? Redis图标 Redis是一种基于键值对(key-value)的NoSQL数据库. 比一般键值对数据库强大的地方,Redis中的value支持string(字符串) ...

最新文章

  1. 花里胡哨,如何在Linux终端输出带有颜色的字体,将带颜色的字体赋值给变量...
  2. Linux下查看文件内容
  3. 微信小程序 获取授权信息详解
  4. 如何在 SAP 电商云里使用 Backoffice 和 Smart Edit 创建新的 Content Page
  5. Yii2 的快速配置 api 服务 yii2-fast-api
  6. java集合的某项相加_java8实现list集合中按照某一个值相加求和,平均值等操作代码...
  7. python 可视化 词云图
  8. 【软技能】完全写作指南--演讲幻灯片
  9. [FPGA基础应用]基于CPLD+ARM架构模拟PC104总线时序
  10. 新高考教师增值评价方式的再研究
  11. 数字化商业模式三结构:价值创造、价值交付、价值捕获
  12. 显示计算机101代码,steam错误代码-101 解决steam错误代码-101的方法
  13. Word怎么删除页眉页尾
  14. device-mapper: multipath: Failing path recovery
  15. 你会copying了吗?(Effective C++ 12 复制对象时勿忘其每一个成分)
  16. 为什么事件的最早发生时间是源点到顶点的最长路径长度?(关键路径详解)
  17. web课程设计网页规划与设计:HTML+CSS题材——我的家乡-沧州 6页 带报告
  18. 燕窝等物品不得携带入境 具有相关检疫证书和疫苗接种证书的犬、猫等宠物
  19. 【Unity Shaders】Reflecting Your World —— Unity3D中的法线贴图和反射
  20. Listary - 文件浏览与搜索增强的超级神器

热门文章

  1. 基于TCP的STM32 IAP bootloader初步设计
  2. 一梦江湖获取服务器信息一直获取不出来,一梦江湖手游4月10日更新汇总-一梦江湖手游4月10日更新内容有哪些_牛游戏网...
  3. 很多人在睡觉的时候半梦半醒时,感…
  4. 日志追踪-类加载器-自定义类加载器
  5. Android 垃圾分类APP(三)垃圾分类之语音输入
  6. 企业私有云资源规划及设计
  7. 在ARM开发板上安装OpenCV4.5.1
  8. PMP知识点(十一、干系人管理)
  9. 厦门大学计算机专业录取分数线2019,厦门大学2019年各专业录取分数分析
  10. php sqlserver 日期转字符串,sqlserver  时间(datetime)转换成字符串