4.1 string
4.1.1 概述
- SDS
redis中没有使用C语言的字符串表示,而是自定义一个数据结构叫SDS(simple dynamic string)即简单动态字符串。 - 源码
打开下载的redis源码包,找到src下的sds.h文件查看sds源码:


Redis 5.0.14 的 SDS 有 5 种结构体,核心区别是 len/alloc 的字节大小;
目的是 "按字符串长度选最小结构体",节省内存,日常用最多的是 sdshdr8(短字符串)和 sdshdr16(中等长度字符串)。
|-------|----------------|
| 字段 | 简单含义 |
| len | 字符串实际有多长 |
| alloc | 字符串最多能存多长 |
| flags | 标记这是哪种 SDS 结构体 |
| buf | 真正存字符串内容的地方 |
- C语言和Redis存储字符串的区别
C 语言字符串存储
- 基于字符数组实现,以 '\0' 作为字符串结束标识;
Redis 字符串存储
- 底层通过 SDS 结构的 char buf[] 存储二进制数据(任何类型数据均可转换为二进制存储);
- 具备二进制安全特性:存入的原始数据(含 '\0' 等特殊字符)不会被修改或截断,取出的数据与存入完全一致;
核心差异
- C 语言字符串遇 '\0' 即判定结束,而 Redis 的 SDS 不依赖 '\0' 识别结束,也不会处理存储的二进制数据,保证数据完整性。
4.1.2 命令
-
赋值与取值
#set支持插入/更新
set key value
get key

当键不存在时返回空结果。

只有 key 不存在时才赋值
setnx key value

2.向尾部追加值
append key value
append的作用是向键值的末尾追加value。
如果键不存在则将该键的值设置为value,即相当于 set key value。返回值是追加后字符串的总长度。

-
获取字符串长度
strlen key
strlen命令返回键值的长度,如果键不存在则返回0。

-
同时设置/获取多个键值
mset key value [key value ...]
mget key [key ...]

5.获取value指定位置的内容
getrange key start end
- key:要操作的 Redis 键;
- start:截取的起始下标(从 0 开始);
- end:截取的结束下标(包含该下标);
- 核心:按下标截取 value 的子字符串,下标支持负数(-1 表示最后一个字符,-2 表示倒数第二个,以此类推)。

-
删除key
删单个 key(核心命令)
del key
删多个 key(空格分隔)
del key1 key2 key3

4.1.3 方法
- String 类型核心操作
|---------|-----------------------------------------|-------------|---------------------|
| 功能 | StringRedisTemplate 方法 | 对应 Redis 命令 | 代码中的作用 |
| 单个赋值 | opsForValue().set(key, value) | SET | 给指定 key 设置字符串值 |
| 单个取值 | opsForValue().get(key) | GET | 获取指定 key 的字符串值 |
| 尾部追加 | opsForValue().append(key, value) | APPEND | 向 key 的值尾部追加字符串 |
| 不存在时赋值 | opsForValue().setIfAbsent(key, value) | SETNX | 仅当 key 不存在时赋值,返回布尔值 |
| 带过期时间赋值 | opsForValue().set(key, value, 时长, 时间单位) | SETEX | 赋值 + 设置过期时间(自动删除) |
| 获取并覆盖 | opsForValue().getAndSet(key, newValue) | GETSET | 获取旧值并立刻设置新值 |
| 截取子串 | opsForValue().get(key, start, end) | GETRANGE | 按下标截取 key 对应值的子字符串 |
| 批量赋值 | opsForValue().multiSet(Map) | MSET | 一次性设置多个 key-value |
| 批量取值 | opsForValue().multiGet(List) | MGET | 一次性获取多个 key 的值 |
- Key 通用操作
|-------------|---------------------------------------------------|------------------|-----------------------|
| 功能 | StringRedisTemplate 方法 | 对应 Redis 命令 | 代码中的作用 |
| 删除单个 key | delete(key) | DEL | 删除指定 key |
| 删除多个 key | delete(List) | DEL key1 key2... | 一次性删除多个 key |
| 判断 key 是否存在 | hasKey(key) | EXISTS | 检查 key 是否存在,返回布尔值 |
| 清空当前数据库 | getConnectionFactory().getConnection().flushDb() | FLUSHDB | 清空当前 Redis 库的所有 key |
| 回显测试 | getConnectionFactory().getConnection().echo(字节数组) | ECHO | 调试 Redis 连接(返回传入的字符串) |
- 结论
- String 类型操作的核心套路:stringRedisTemplate.opsForValue().xxx();
- Key 通用操作直接调用 stringRedisTemplate.xxx()(如 delete/hasKey);
- 底层原生命令(如 FLUSHDB/ECHO)需通过 getConnection() 获取连接后执行;
-
代码
package com.qcby.springbootredis;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@SpringBootTest
public class StringTest {
// 注入 SpringBoot 提供的 StringRedisTemplate(替代 Jedis)
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**-
赋值、取值、追加、批量操作
*/
@Test
public void testHello() {
try {
// 1. 赋值(对应 set 命令)
stringRedisTemplate.opsForValue().set("name", "minxr");
// 取值(对应 get 命令)
String ss = stringRedisTemplate.opsForValue().get("name");
System.out.println(ss); // 输出 minxr// 2. 向尾部追加(对应 append 命令) stringRedisTemplate.opsForValue().append("name", "jintao"); ss = stringRedisTemplate.opsForValue().get("name"); System.out.println(ss); // 输出 minxrjintao // 3. 覆盖数据 stringRedisTemplate.opsForValue().set("name", "jintao"); System.out.println(stringRedisTemplate.opsForValue().get("name")); // 输出 jintao // 4. 删除key(对应 del 命令) stringRedisTemplate.delete("name"); System.out.println(stringRedisTemplate.opsForValue().get("name")); // 输出 null // 5. 批量设置/获取(对应 mset、mget 命令) // 替换 Map.of() 适配Java 8 Map<String, String> multiSetMap = new HashMap<>(); multiSetMap.put("name", "minxr"); multiSetMap.put("jarorwar", "aaa"); stringRedisTemplate.opsForValue().multiSet(multiSetMap); // 替换 List.of() 适配Java 8 List<String> multiGetList = new ArrayList<>(); multiGetList.add("name"); multiGetList.add("jarorwar"); System.out.println(stringRedisTemplate.opsForValue().multiGet(multiGetList)); // 输出 [minxr, aaa]} catch (Exception e) {
e.printStackTrace();
}
}
/**
-
key相关操作
*/
@Test
public void testKey() {
System.out.println("=============key==========================");
// 清空当前库(对应 flushdb 命令)
//先获取 Redis 底层连接(相当于拿到 redis-cli 的连接),在执行命令
stringRedisTemplate.getConnectionFactory().getConnection().flushDb();
// 回显(对应 echo 命令)
System.out.println(new String(stringRedisTemplate.getConnectionFactory().getConnection().echo("foo".getBytes())));// 判断key是否存在(对应 exists 命令)
System.out.println(stringRedisTemplate.hasKey("foo")); // 输出 false
stringRedisTemplate.opsForValue().set("key", "values");
System.out.println(stringRedisTemplate.hasKey("key")); // 输出 true
}
/**
-
String 类型完整操作
*/
@Test
public void testString() {
System.out.println("==String==");
try {
System.out.println("=============String==========================");
// 清空当前库
stringRedisTemplate.getConnectionFactory().getConnection().flushDb();// 1. 存储数据 stringRedisTemplate.opsForValue().set("foo", "bar"); System.out.println(stringRedisTemplate.opsForValue().get("foo")); // 输出 bar // 2. 若key不存在则存储(对应 setnx 命令) Boolean setNx = stringRedisTemplate.opsForValue().setIfAbsent("foo", "foo not exits"); System.out.println(setNx); // 输出 false(因为 foo 已存在) System.out.println(stringRedisTemplate.opsForValue().get("foo")); // 输出 bar // 3. 覆盖数据 stringRedisTemplate.opsForValue().set("foo", "foo update"); System.out.println(stringRedisTemplate.opsForValue().get("foo")); // 输出 foo update // 4. 追加数据 stringRedisTemplate.opsForValue().append("foo", " hello, world"); System.out.println(stringRedisTemplate.opsForValue().get("foo")); // 输出 foo update hello, world // 5. 设置有效期并存储(对应 setex 命令) stringRedisTemplate.opsForValue().set("foo", "foo not exits", 2, TimeUnit.SECONDS); //立刻查foo:此时还没到2秒,能拿到值 → 输出 foo not exits System.out.println(stringRedisTemplate.opsForValue().get("foo")); // 输出 foo not exits //让当前执行代码的线程"暂停3秒" Thread.sleep(3000); //3秒后再查foo:2秒过期时间已到,Redis已删了foo → 输出 null System.out.println(stringRedisTemplate.opsForValue().get("foo")); // 输出 null // 6. 获取并更改数据(对应 getset 命令) stringRedisTemplate.opsForValue().set("foo", "foo update"); System.out.println(stringRedisTemplate.opsForValue().getAndSet("foo", "foo modify")); // 输出 foo update // 7. 截取value(对应 getrange 命令) System.out.println(stringRedisTemplate.opsForValue().get("foo", 1, 3)); // 输出 oo空格 // 8. 批量设置/获取 Map<String, String> msetMap = new HashMap<>(); msetMap.put("mset1", "mvalue1"); msetMap.put("mset2", "mvalue2"); msetMap.put("mset3", "mvalue3"); msetMap.put("mset4", "mvalue4"); stringRedisTemplate.opsForValue().multiSet(msetMap); List<String> mgetList = new ArrayList<>(); mgetList.add("mset1"); mgetList.add("mset2"); mgetList.add("mset3"); mgetList.add("mset4"); System.out.println(stringRedisTemplate.opsForValue().multiGet(mgetList)); // 输出 [mvalue1, mvalue2, mvalue3, mvalue4] // 9. 删除多个key List<String> delList = new ArrayList<>(); delList.add("foo"); delList.add("foo1"); delList.add("foo3"); stringRedisTemplate.delete(delList);} catch (Exception e) {
e.printStackTrace();
}
}
}
-