Redis和C和golang语言底层String结构分析

C语言的字符串类型

//c语言声明字符串
char *s = "hello"
//本质是字符数组: {'h','e','l','l','o','\0'}

我们都知道,Redis的底层是用C语言写的,那么Redis为什么不直接使用C语言的字符串结构呢?

原因是因为C语言字符串存在很多问题:

  • 获取字符串长度的需要通过运算 
  • 非二进制安全
  • 不可修改

1.因为c语言字符串本质是字符数组,所以获取长度时需要遍历到 \0结束字符,才能获取字符串长度

2.且因为结束字符是\0,所以C语言不允许字符串中出现\0字符,所以是二进制不安全的

3.因为string通常指向字符串字面量,而字符串字面量存储位置是只读段,而不是堆或栈上,所以才有了string不可修改的约定。

Go语言string底层

Go语言的string有三个特点:

  • string是8bit字节的集合,通常是但并不一定非得是UTF-8编码的文本。
  • string可以为空(长度为0),但不会是nil;
  • string对象不可以修改。

源码包src/runtime/string.go:stringStruct定义了string的数据结构:

type stringStruct struct {
    str unsafe.Pointer        //stringStruct.str:字符串的首地址;
    len int                   //stringStruct.len:字符串的长度;
}
  • stringStruct.str:字符串的首地址;
  • stringStruct.len:字符串的长度;

所以字符串的结构和切片非常相像,只是缺少了切片的cap字段。事实上string和切片,准确的说是byte切片经常发生转换。因为本文的重点是讲述string底层结构,所以不展开[]byte和string的转换关系

Redis底层字符串结构SDS

Redis构建了一种新的字符串结构,称为简单动态字符串(Simple Dynamic String),简称SDS

Redis底层SDS源码(64位):

struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; /* used */
    uint64_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
  • len:buf已保存的字符串字节数,不包含结束标志
  • alloc:buf申请的总字节数,不包含结束标识
  • flags:不同SDS的头类型,用来控制SDS的头大小
  • buf:数据存储区,为了兼容C语言,尾部会有\0结束标志。但并不以其结束,而是以len长度结束,所以是二进制安全的

SDS的头类型大小

#define SDS_TYPE_5  0
#define SDS_TYPE_8  1
#define SDS_TYPE_16 2
#define SDS_TYPE_32 3
#define SDS_TYPE_64 4

如上代码所示,redis的__attribute__ 结构体,其实分为5bit,8bit.....64bit位的多种类型。一般采用8bit位的1字节大小来存储头信息(长度,申请数,8bit就是255,若不够就用更大的类型)。

示例

我们假设在redis中操作

set name "虎哥"

底层就会自动创建 “name” 和 “虎哥”这两个字符串变量。所以说redis最常见的数据结构就是String

SDS的动态扩容

例如一个内容为“hi”的SDS:

假如我们要给SDS追加一段字符串“,Amy”,这里首先会申请新内存空间:

  • 如果新字符串小于1M,则新空间为扩展后字符串长度的两倍+1;
  • 如果新字符串大于1M,则新空间为扩展后字符串长度+1M+1。称为内存预分配

 注意的是,这里alloc扩容到12,但因为是不包含结束符的,所以实际的空间里有13

总结

  • 获取字符串长度的时间复杂度为O(1)
  • 支持动态扩容
  • 减少内存分配次数
  • 二进制安全

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值