Redis数据类型String详解

一、概述
String是Redis最基本的类型,你可以理解成与Memcached一模一样的类型。
String的数据结构为简单动态字符串(SimpleDynamicString,缩写SDS),是可以修改的字符串,内部结构实现上类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。

二、String底层结构
1.SDS结构
Redis 使用 SDS 简单动态字符串(Simple Dynamic String,SDS)来表示字符串,SDS 的主要结构如下:

struct sdshdr {
    // buf数组中已使用字节的数量
    int len;
    // buf数组中未使用字节的数量
    int free;
    // 字节数组,用于保存字符串
    char buf[];
};

1.“Free”:表示 Buf 数组中未使用字节的数量,也就是 Buf 数组的剩余空间。这样可以在增加字符串长度时,避免频繁的内存重新分配。
2.“Len” :表示 Buf 数组中已使用字节的数量,也就是字符串的长度。这样可以在 O(1) 的时间复杂度内获取字符串长度,而不需要像 C 语言字符串那样遍历整个字符串。
3.“Buf[]” :表示字节数组,用于保存字符串。这个数组的末尾总是包含一个空字符(‘\0’),这样 SDS 就可以兼容 C 语言的字符串函数。

2.SDS优点
(1)获取长度的时间复杂度为 O(1):SDS 内部维护了一个 len 属性,这个属性记录了字符串的长度,因此获取字符串长度的时间复杂度为 O(1),而 C 字符串需要遍历整个字符串才能获取到长度,时间复杂度为 O(n);
(2)内存效率:SDS 通过维护一个 free 属性,记录了 buf 数组中未使用的字节数量,这样可以在需要扩展字符串时,直接使用这些未使用的空间,而不需要重新分配内存,提高了内存的使用效率;
(3)避免缓冲区溢出:SDS在进行字符串修改操作时,会先检查缓冲区是否满足条件,如果不满足,会自动扩展缓冲区,因此可以避免缓冲区溢出的问题。而C字符串则需要程序员自己保证不会发生缓冲区溢出;
(4)减少内存重新分配的次数:当给sds的值追加一个字符串,而当前的剩余空间不够时,就会触发sds的扩容机制。扩容采用了空间预分配的优化策略,即分配空间的时候:如果sds 值大小< 1M ,则增加一倍;反之如果>1M , 则当前空间加1M作为新的空间;
当sds的字符串缩短了,sds的buf内会多出来一些空间,这个空间并不会马上被回收,而是暂时留着以防再用的时候进行多余的内存分配。这个是惰性空间释放的策略,避免了频繁的内存申请。
(5)二进制安全:C 字符串是以空字符 ‘\0’ 作为结束标志,因此不能正确存储包含 ‘\0’ 的字符串。而 SDS 的每一个字符都可以是 ‘\0’,因此 SDS 可以存储任何二进制数据;

三、String的应用场景
Redis中的String 数据类型作为最基本的数据类型,它的应用场景非常广泛,如下:
1.缓存:由于Redis的高性能读写特性,String类型常常被用作缓存,可以将数据库查询结果、网页内容、会话信息等缓存在Redis中,提高系统的读取速度;
2.分布式锁:我们可以使用Redis作分布式锁,通过设置key、value(只有当 key 不存在时才设置 value),可以实现分布式锁,以此保证系统安全。
3.计数器:Redis中的String类型可以将值解析为整数,并提供了自增和自减操作,可以作为各种计数器使用,比如下载量等。
4.限流:通过 INCR 命令和 EXPIRE 命令,可以实现 API 的限流功能,防止系统被过度访问。
5.消息队列:Redis中的String类型可以作消息队列,比如一些应用中,需要将消息发送给多个消费者,如邮件系统等。

四、总结
1.redis的string底层数据结构使用的是sds,但是sds有两种存储方式,一种是embstr,一种是raw。
2.embstr的优势在于和对象头一起分配到连续空间,只需要调用函数malloc一次就行。raw需要两次,一次是对象头,一次是sds。释放也一样,embstr释放一次,raw释放两次。
3.SDS 是Redis自己构建的一种简单动态字符串的抽象类型,并将 SDS 作为 Redis 的默认字符串表示。