Redis中的String⼆进制安全机制
⽂章⽬录
参考⽂章:
⼆进制安全
⼆进制安全是与操作字符串的⽅法的相关术语,该⽅法的参数可以包含任何字符,⽅法会公平的对待数据流的每个字符,不特殊处理其中某⼀个字符,包括特殊字符。【特殊情况:该⽅法就是⽤于处理特定字符】
⼆进制安全的例⼦
C语⾔中字符串是以特殊字符“\0”来作为字符串的结束标识。对于字符串str="0123456789\0123456789”来说,在C语⾔⾥⾯str 的长度就是10(strlen(str)=10),所以strlen()函数不是⼆进制安全的。
Redis的⼆进制安全
简单动态字符串(Simple Dynamic Strings,SDS)是Redis的基本数据结构之⼀,主要⽤于存储字符串、整型数字。
Redis 3.2 之前的SDS主要是通过C语⾔中结构体类型确定的,包括已占⽤字节数len、剩余可⽤字节数free、实际保存字符串的字符数组来确定的。
(1)由于有长度的统计变量len的存在,读写字符串时不依赖“\0”终⽌符,保证了⼆进制安全
(2)Redis保存的字符串对外暴露的是数组的长度指针,⽽不是结构体的指针,上层可以像操作普通字符串⼀样操作SDS。
struct sdshdr {
//buf中已占⽤字节数
int len;
//buf剩余可⽤字节数
int free;
/
/实际保存字符串的字符数组
char buf[];
};
柔性数组:C语⾔中结构体的最后⼀个元素可以是⼤⼩未知的数组,也就是所谓的0长度,所以我们可以⽤结构体来创建柔性数组。⽽定义SDS的结构体中的数组就是柔性数组,可以根据需要调整数组长度,也可以通过柔性数组的⾸地址偏移得到结构体⾸地址。
SDS采⽤柔性数组的缺点: 不同SDS字符串占⽤了相同⼤⼩的头部空间(buf、leng长度),浪费空间。
Redis 5.0改进了SDS,将根据字符串的长度,分成了5种类型sdshdr5、sdshdr8、sdshdr16、sdshdr32、sdshdr64。
图中:
1、len表⽰buf中已占⽤字节数。
2、alloc表⽰buf中已分配字节数,记录的是为buf分配的总长度,不同于free。
3、flags标识当前结构体的类型,低3位⽤作标识位,⾼5位预留。
4、buf柔性数组,真正存储字符串的数据空间。
(1)五种类型都多了⼀个flags字段,但sdsdr5没有了头部(len和free )
(2)sdshdr5结构中,flags占1个字符,其低3位表⽰结构体类型,⾼5位表⽰长度,能表⽰的长度区间为0~31,flags后⾯就是字符串的内容。⽽长度⼤于31的字符串,1个字节存不下,那么就要将len和free单独存放,因此redis存放数据时会先检查字符串长度,再根据字符串长度计算好不同类型的头部和初始长度,然后动态分配内存
常见问题
(1)SDS如何兼容C语⾔字符串?如何保证⼆进制安全?
SDS对象中的buf是⼀个柔性数组,上层调⽤时,SDS直接返回了buf。由于buf是直接指向内容的指针,
所以兼容C语⾔函数。⽽当真正读取内容时,SDS会通过len来限制读取长度,⽽⾮“0”,所以保证了⼆进制安全。
redis五种数据结构
(2)sdshdr5的特殊之处是什么?
sdshdr5只负责存储⼩于32字节的字符串。⼀般情况下,⼩字符串的存储更普遍,所以Redis进⼀步压缩了sdshdr5的数据结构,将sdshdr5的类型和长度放⼊了同⼀个属性中,⽤flags的低3位存储类型,⾼5位存储长度。创建空字符串时,sdshdr5会被sdshdr8替代。(3)SDS是如何扩容的?
SDS在涉及字符串修改时会调⽤sdsMakeroomFor函数进⾏检查,会根据空闲长度和新增内容的长度进⾏⽐较判断,然后根据不同情况动态扩容

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。