📦 String的定义
字符串是最基础的数据类型,value 可以是:
- 字符串(简单的字符串、复杂的字符串,例如 JSON、XML)
- 数字(整数、浮点数)
- 二进制数据(图片、音频、视频),但最大不能超过 512MB
📋 String的常见命令
🔧 设置键值对
SET KEY_NAME VALUE:设置给定 key 的值。如果 key 已经存储其他值,SET 就覆写旧值,且无视类型GETSET KEY_NAME VALUE:设置指定 key 的值,并返回 key 的旧值SETEX KEY_NAME TIMEOUT VALUE:为指定的 key 设置值及其过期时间。如果 key 已经存在,SETEX 命令将会替换旧值MSET key1 value1 key2 value2 .. keyN valueN:同时设置一个或多个 key-value 对
🔍 根据键查找值
GET KEY_NAME:返回 key 的值。如果 key 不存在时,返回 nil;如果 key 不是字符串类型,则返回一个错误
📏 获取字符串长度
STRLEN KEY_NAME:获取指定 key 所储存的字符串值的长度
🧱 String的底层实现
Redis 中的 String 类型底层实现主要基于 SDS(Simple Dynamic String,简单动态字符串)结构,并结合 int、embstr、raw 等不同的编码方式进行优化存储。
🏗️ SDS的结构
SDS 结构包含以下四个成员:
- len:记录了字符串长度。获取字符串长度时,只需要返回这个成员变量值即可,时间复杂度为 $O(1)$
- alloc:分配给字符数组的空间长度。通过
alloc - len可以计算出剩余空间大小,判断是否满足修改需求。如果不满足,SDS 会自动扩容至执行修改所需的大小,因此不需要手动修改空间大小,也不会出现缓冲区溢出问题 - flags:用来表示不同类型的 SDS。一共设计了 5 种类型,分别是 sdshdr5、sdshdr8、sdshdr16、sdshdr32 和 sdshdr64
- buf[]:字符数组,用来保存实际数据。不仅可以保存字符串,也可以保存二进制数据
💡 相比于C语言字符串的优势
- 二进制安全:SDS 不仅可以保存文本数据,还可以保存二进制数据。因为 SDS 使用
len属性的值而不是空字符来判断字符串是否结束,并且 SDS 的所有 API 都会以处理二进制的方式来处理存放在buf[]数组里的数据。所以 SDS 能保存图片、音频、视频、压缩文件这样的二进制数据 - 常数时间复杂度:SDS 获取字符串长度的时间复杂度是 $O(1)$。因为 C 语言的字符串并不记录自身长度,所以获取长度的复杂度为 $O(n)$;而 SDS 结构里用
len属性记录了字符串长度,所以复杂度为 $O(1)$ - 空间自动扩容:Redis 的 SDS API 是安全的,拼接字符串不会造成缓冲区溢出。因为 SDS 在拼接字符串之前会检查空间是否满足要求,如果空间不够会自动扩容,所以不会导致缓冲区溢出的问题
⚖️ 编码方式
Redis String 类型会根据字符串的长度和内容选择不同的编码方式:
- int 编码:如果一个字符串对象保存的是整数值,并且这个整数值可以用
long类型来表示,那么字符串对象会将整数值保存在字符串对象结构的ptr属性里面(将 void 转换成 long),并将编码设置为int - embstr 编码:如果字符串对象保存的是一个短字符串,那么字符串对象将使用 SDS 来保存这个字符串,并将编码设置为
embstr。embstr是专门用于保存短字符串的一种优化编码方式 - raw 编码:如果字符串对象保存的是一个长字符串,那么字符串对象将使用 SDS 来保存这个字符串,并将编码设置为
raw