有道无术,术尚可求,有术无道,止于术。
本系列Redis 版本 7.2.5
源码地址:https://gitee.com/pearl-organization/study-redis-demo
文章目录
- 1. 前言
- 2. 常用命令
- 2.1 SET
- 2.2 GET
- 2.3 MSET
- 2.4 MGET
- 2.5 GETSET
- 2.6 STRLEN
- 2.7 SETEX
- 2.8 SETNX
- 2.9 INCR
- 2.10 DECR
- 3. SDS(简单动态字符串)
- 3.1 二进制安全
- 3.2 查询字符串长度效率高
- 3.3 缓冲区溢出保护
- 3.4 空间预分配
- 3.5 惰性空间释放
- 4. 应用场景
- 4.1 存储常规数据
- 4.2 计数器
- 4.3 分布式锁
1. 前言
在官方文档中,可以看到 Redis 支持很多种数据类型,以解决不同场景下的各种问题。接下来,会详细介绍 Redis 中的各种数据类型, 主要是介绍一些常用的,包括:
StringListHashSetSorted set(ZSet)BitmapHyperLogLogGeospatialStream
String字符串是编程语言里最常用的数据类型,使用双引号表示。
比如JAVA:
String str="1234";
比如C语言:
char str[] = "1234";
在 Redis 中, String 类型是最基本的数据类型,是二进制安全的,可以存储任意类型的数据,文本、数字、图片或者序列化的对象 。

注意:Key的类型只能为字符串,Value的类型为字符串时,默认情况下的最大值是 512M 。
2. 常用命令
String 相关的所有命令:
| 命名 | 描述 |
|---|---|
| APPEND | 将 value 追加到 key 原来的值的末尾 |
| DECR | 将 key 中储存的数字值减一 |
| DECRBY | 将 key 所储存的值减去给定的减量值 ( decrement ) |
| GET | 设置指定 key 的值 |
| GETDEL | 获取 key 的值并删除该 key |
| GETEX | 获取 key 的值,并可选择设置其过期时间 |
| GETRANGE | 返回 key 中字符串值的子字符 |
| GETSET | 将给定 key 的值设为 value ,并返回 key 的旧值 |
| INCR | 将 key 中储存的数字值增一 |
| INCRBY | 将 key 所储存的值加上给定的增量值 ( increment ) |
| INCRBYFLOAT | 将 key 所储存的值加上给定的浮点增量值 ( increment ) |
| LCS | 实现了最长公共子序列算法,可用于评估字符串的相似程度 |
| MGET | 获取所有(一个或多个)给定 key 的值 |
| MSET | 同时设置一个或多个 key-value 对 |
| MSETNX | 同时设置一个或多个 key-value 对 |
| PSETEX | 以毫秒为单位设置 key 的生存时间 |
| SET | 设置指定 key 的值 |
| SETEX | 设置 key 的值为 value 同时将过期时间设为 seconds |
| SETNX | 只有在 key 不存在时设置 key 的值 |
| SETRANGE | 从偏移量 offset 开始用 value 覆写给定 key 所储存的字符串值 |
| STRLEN | 返回 key 所储存的字符串值的长度 |
| SUBSTR | 返回字符串值的子字符串,由偏移量开始和结束(两者都包含在内)决定 |
2.1 SET
用于给指定的 Key 设置字符串值,如果 Key 已经保存了一个值,那么这个操作会直接覆盖原来的值,并且忽略原始类型。当 SET 命令执行成功后,之前设置的过期时间都将失效。
示例:
127.0.0.1:6379> SET mykey "Hello World"
OK
127.0.0.1:6379> SET mykey "Hello World" EX 60
OK
2.2 GET
用于获取指定 Key 的字符串值,如果 Key 不存在, 那么返回特殊值 nil, 如果键 Key的值不是字符串类型, 返回错误, 因为 GET 命令只能用于字符串值。
示例:
127.0.0.1:6379> GET mykey
"Hello World"
127.0.0.1:6379> GET mykey_nil
(nil)
2.3 MSET
和 SET 命令一样,会用新值替换旧值, MSET 是原子操作,所有 Key 的值同时设置,客户端不会看到有些 Key 值被修改,而另一些 Key 值没变。总是返回 OK ,因为 MSET 不会失败。
基本语法:
MSET key1 value1 key2 value2 .. keyN valueN
示例:
127.0.0.1:6379> MSET key1 "Hello" key2 "World"
OK
127.0.0.1:6379> GET key1
"Hello"
127.0.0.1:6379> GET key2
"World"
2.4 MGET
用于获取所有给定 Key 的值,返回一个列表,值的类型是字符串,如果某个 Key 不存在或者值不是字符串,那么这个 Key 返回特殊值 nil 。
基本语法:
MGET KEY1 KEY2 .. KEYN
示例:
127.0.0.1:6379> MGET key1 key2 nonexisting
1) "Hello"
2) "World"
3) (nil)
2.5 GETSET
用于给指定的 Key 设置字符串值, 并返回设置之前的旧值。如果 Key 没有旧值返回 nil,如果 Key 对应的值存在但不是字符串类型时,返回一个错误。
示例:
127.0.0.1:6379> SET mykey "Hello"
OK
127.0.0.1:6379> GETSET mykey "World"
"Hello"
127.0.0.1:6379> GET mykey
"World"
2.6 STRLEN
用于获取指定 Key 所储存的字符串值的长度,当储存的不是字符串类型时,返回错误。
基本语法:
STRLEN KEY_NAME
示例:
127.0.0.1:6379> SET mykey "Hello world"
OK
127.0.0.1:6379> STRLEN mykey
(integer) 11
127.0.0.1:6379> STRLEN mykey_nil
(integer) 0
2.7 SETEX
用于给指定的 Key 设置字符串值,并将 Key 的生存时间设置为多少秒。如果键 Key 已经存在,将覆盖已有的值。
基本语法:
SETEX key seconds value
示例:
127.0.0.1:6379> SETEX mykey 60 "Hello"
OK
127.0.0.1:6379> TTL mykey
(integer) 52
127.0.0.1:6379> GET mykey
"Hello"
2.8 SETNX
SETNX 是 SET if Not eXists 的缩写,在指定的 Key 不存在时,设置指定的值,这种情况下等同 SET 命令,当 Key 存在时,什么也不做。
返回值为整数:
1:Key存在0:Key不存在
SETNX 命令是原子性的,常用于实现分布式锁,添加成功表示获取到锁,添加失败表示未获取到锁(后续详解介绍)。
示例:
redis> SETNX mykey "Hello"
(integer) 1
redis> SETNX mykey "World"
(integer) 0
redis> GET mykey
"Hello"
2.9 INCR
用于将指定的 Key 储存的数字值增一,如果 Key 不存在,那么值会先被初始化为 0 ,然后再执行 INCR 操作,返回值为执行操作之后 Key 的值。如果值包含错误的类型,或字符串类型的值不能表示为数字,将会返回一个错误 ERR ERR hash value is not an integer。
操作的值限制在 64 位(bit)有符号数字表示之内。本质上这是一个字符串操作,因为 Redis 没有专门的整数类型,储在 Key 中的字符串被转换为十进制有符号整数,在此基础上加 1 。常用于计数器、限流(后续详解介绍)。
基本语法:
INCR KEY_NAME
示例:
127.0.0.1:6379> SET mykey "10"
"OK"
127.0.0.1:6379> INCR mykey
(integer) 11
127.0.0.1:6379> GET mykey
"11"
2.10 DECR
用于将指定的 Key 储存的数字值减去一,如果 Key 不存在,那么值会先被初始化为 0 ,然后再执行 DECR 操作。如果值包含错误的类型,或字符串类型的值不能表示为数字,将会返回一个错误。操作的值限制在 64 位(bit)有符号数字表示之内,返回值为执行操作之后 Key 的值
基本语法:
DECR KEY_NAME
示例:
127.0.0.1:6379> DECR mykey
(integer) 9
127.0.0.1:6379> SET mykey "234293482390480948029348230948"
"OK"
127.0.0.1:6379>DECR mykey
ERR ERR value is not an integer or out of range
3. SDS(简单动态字符串)
SDS 是 Simple Dynamic String 的首字母缩写,翻译为简单的动态字符串,是 Redis 自定义的一种表示字符串的特殊数据结构。相较于传统的C语言字符串,SDS 具有更多的功能和更高的性能,当前也会占用更多的内存。
Redis 3.2 之前的版本:
struct sdshdr {
int len; // 记录buf数组中已使用字节的数量,即字符串的实际长度
int free; // 存储剩余(空闲)的空间
char buf[]; // 存储实际数据
};
Redis 3.2 之后的版本,SDS 变成了 5 种数据结构,不同类型的存储空间大小不一样:
/* Note: sdshdr5 is never used, we just access the flags byte directly.
* However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; // 已用空间的长度,占 4 个字节,不包括 ‘\0’;
uint8_t alloc; // 实际分配长度,占 4 个字节,不包括 ‘\0’;
unsigned char flags; // 标记当前 buf[] 类型( sdshdr8/16/32/64)
char buf[]; // 存储实际数据
};
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
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[];
};
相比于C语言字符串,SDS 有以下优点:
- 二进制安全
- 查询字符串长度效率高
- 缓冲区溢出保护
- 空间预分配
- 惰性空间释放
3.1 二进制安全
在 C 语言中,用字符 \0 表示字符串的结束,如果字符串中本身就有 \0 字符,字符串就会被截断,即非二进制安全,例如 hello \0 world 会被截断解析为 hello。
SDS 不受 \0 字符影响,能够准确存储任意二进制数据,因为它基于 len 属性而非遇到 \0 来判断字符串的结束位置。
3.2 查询字符串长度效率高
在 C 语言中,获取一个字符串长度时,需对整个字符串进行遍历,直至遇到结束符号。在高并发场景下频繁遍历字符串,势必会造成性能瓶颈。
SDS 中于 len 属性记录了字符串的长度,可以直接通过 STRLEN 命令获取长度即可。
3.3 缓冲区溢出保护
在 C 语言中,字符串本身不记录可用空间大小,在进行字符串拼接等操作时,如果没有检查就可能会导致缓冲区溢出。
SDS 中于 free 属性记录了额外未使用的空间大小,每次进行字符串操作前都会检查剩余空间是否足够,不足时会自动扩容,有效避免了缓冲区溢出问题。
3.4 空间预分配
在 C 语言中,每次修改字符串长度(增加或减少)时,通常需要重新分配内存,这可能导致频繁的内存操作。
SDS 优化了字符串增长操作,当修改字符串并需对 SDS 的空间进行扩展时,不仅会为 SDS分配修改所必要的空间,还会分配额外的未使用空间(free属性),下次再修改就先检查未使用空间free是否满足,满足则不用在扩展空间。有效的减少字符串连续增长操作,减少所产生的内存重分配次数。
3.5 惰性空间释放
惰性空间释放策略则用于优化 SDS 字符串缩短操作,当缩短 SDS 字符串后,并不会立即执行内存重分配来回收多余的空间,而是用 free 属性将这些空间记录下来,如果后续有增长操作,则可直接使用。
4. 应用场景
4.1 存储常规数据
String 可以存储任意类型的数据,文本、数字、图片或者序列化的对象 。
常用实际场景:
- 图形、短信验证码
- 令牌
例如,存储短信验证码并设置过期时间为 60 秒:
localhost:0>SET 13688889999 80906 EX 60
"OK"
localhost:0>GET 13688889999
"80906"
4.2 计数器
INCR 用于将指定的 Key 储存的数字值增一, DECR 用于将指定的 Key 储存的数字值减去一。
常用实际场景:
- 阅读数
- 点赞
- 分布式
ID - 网站访问量
- 记录接口访问次数,实现防止重复提交、简单限流
例如,使用 INCR 记录网站每日访问量:
localhost:0>SET web_views:20240617 0
"OK"
localhost:0>INCR web_views:20240617
"1"
localhost:0>GET web_views:20240617
"1"
4.3 分布式锁
SETNX 命令是原子性的,常用于实现分布式锁,添加成功表示获取到锁,添加失败表示未获取到锁(后续详解介绍)。
例如,执行 SETNX 添加锁,返回 1 表示成功获取到锁:
localhost:0>SETNX my_lock yes
"1"
其他线程执行SETNX,返回 0 表示获取锁失败:
localhost:0>SETNX my_lock yes
"0"
执行完成后,删除锁,其他线程再次获取则成功:
localhost:0>DEL my_lock
"1"
localhost:0>SETNX my_lock yes
"1"



















