Redis中的String二进制安全机制


参考文章: 深度分析Redis的二进制安全

二进制安全

二进制安全是与操作字符串的方法的相关术语,该方法的参数可以包含任何字符,方法会公平的对待数据流的每个字符,不特殊处理其中某一个字符,包括特殊字符。【特殊情况:该方法就是用于处理特定字符】

二进制安全的例子

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”,所以保证了二进制安全。
(2)sdshdr5的特殊之处是什么?
sdshdr5只负责存储小于32字节的字符串。一般情况下,小字符串的存储更普遍,所以Redis进一步压缩了sdshdr5的数据结构,将sdshdr5的类型和长度放入了同一个属性中,用flags的低3位存储类型,高5位存储长度。创建空字符串时,sdshdr5会被sdshdr8替代。
(3)SDS是如何扩容的?
SDS在涉及字符串修改时会调用sdsMakeroomFor函数进行检查,会根据空闲长度和新增内容的长度进行比较判断,然后根据不同情况动态扩容

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值