RedisTemplate & StringRedisTemplate 使用指南

前言

由于对于 Redis 始终停留在理论阶段——懂数据结构类型、数据结构底层实现、缓存击穿、缓存穿透、缓存雪崩,还有各种防xxx的方案,但是貌似都停留在学习上、代码阅读上,现在重新补充一下代码实现的细节上。

项目中很多地方使用了 StringRedisTemplate,其实是继承自 RedisTemplate 的。

然后就去搜 RedisTemplate 的 API、搜如何使用,挑选了7-8篇博客结合 ai 整理知识点如下:

从导入依赖开始

为什么是从导入依赖开始呢?主要是这个过程引发了我的疑问:

看到有一篇博客使用如下pom依赖:

1
2
3
4
5
<!--Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

但是我的项目中使用如下pom依赖:

1
2
3
4
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>

为什么引入的是 redisson,但是还能使用StringRedisTemplate 呢?

询问了一遍ai,两者的对比:

场景 推荐依赖 原因
分布式锁 Redisson 提供标准的 RLock 接口,内置看门狗(Watchdog)自动续期机制,健壮性高。
基础缓存/Session Spring Data Redis 使用 RedisTemplate 或 Spring Cache 抽象,更贴合 Spring 生态标准。
分布式限流/信号量 Redisson 提供现成的 RRateLimiterRSemaphore,直接调用即可。
复杂数据结构操作 Redisson 提供 RMap, RSet 等,操作方式与 Java 集合类一致,更符合面向对象习惯。
直接执行 Redis 命令 Spring Data Redis RedisTemplate 可以直接执行如 opsForValue().set(...) 等命令,与原生 Redis 命令映射更直接。

所以想要使用 RedisTemplate 对数据库进行操作,本质上是要导入 spring-boot-starter-data-redis的。(然后导入 Redisson 其实就导入了 Spring Data Redis)

写application.yml

1
2
3
4
5
6
spring:
data:
redis:
port: 6379
host: 127.0.0.1
password:

RedisTemplate 常用 API

Spring Data Redis 提供了 RedisTemplateStringRedisTemplate 两个核心模板类。RedisTemplate 支持泛型(默认 JDK 序列化),StringRedisTemplate 专用于 String 类型的 Key-Value 操作(String 序列化)。

核心操作入口:

  • opsForValue():操作 String(字符串)
  • opsForHash():操作 Hash(哈希)
  • opsForList():操作 List(列表)
  • opsForSet():操作 Set(集合)
  • opsForZSet():操作 ZSet(有序集合)

String 类型

对应的 Redis 结构:String

核心类:ValueOperations<K, V>

方法 (Java) 原始指令 (Redis) 描述
set(key, value) SET key value 设置键值对
set(key, value, timeout, unit) SETEX key seconds value 设置键值对并指定过期时间
setIfAbsent(key, value) SETNX key value 只有键不存在时才设置(分布式锁基础)
get(key) GET key 获取值
increment(key, delta) INCRBY key increment 原子递增
decrement(key, delta) DECRBY key decrement 原子递减

Java 代码示例

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Autowired
private RedisTemplate<String, Object> redisTemplate;

public void stringDemo() {
// 1. SET: 设置值
redisTemplate.opsForValue().set("user:1001", "Jason");

// 2. SETEX: 设置值并设置5分钟过期
redisTemplate.opsForValue().set("code:1234", "8888", 5, TimeUnit.MINUTES);

// 3. SETNX: 如果不存在则设置 (返回 boolean)
Boolean isSet = redisTemplate.opsForValue().setIfAbsent("lock:order:1", "locked");

// 4. INCR: 计数器自增
redisTemplate.opsForValue().increment("article:view:1", 1);

// 5. GET: 获取
Object value = redisTemplate.opsForValue().get("user:1001");
}

Hash 类型

对应的 Redis 结构:Hash

核心类:HashOperations<K, HK, HV>

方法 (Java) 原始指令 (Redis) 描述
put(key, hashKey, value) HSET key field value 设置 Hash 字段值
putAll(key, map) HMSET key field value ... 批量设置 Hash 字段
get(key, hashKey) HGET key field 获取指定字段值
entries(key) HGETALL key 获取 Hash 表中所有字段和值
keys(key) HKEYS key 获取所有字段名
delete(key, hashKey) HDEL key field 删除一个或多个字段

Java 代码示例

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void hashDemo() {
String key = "cart:user:1001";

// 1. HSET: 添加商品到购物车
redisTemplate.opsForHash().put(key, "sku_123", "1");

// 2. HMSET: 批量添加
Map<String, Object> map = new HashMap<>();
map.put("sku_456", "2");
map.put("sku_789", "5");
redisTemplate.opsForHash().putAll(key, map);

// 3. HGET: 获取特定商品数量
Object count = redisTemplate.opsForHash().get(key, "sku_123");

// 4. HGETALL: 获取购物车所有商品
Map<Object, Object> items = redisTemplate.opsForHash().entries(key);

// 5. HDEL: 删除商品
redisTemplate.opsForHash().delete(key, "sku_123");
}

List 类型

对应的 Redis 结构:List (双向链表)

核心类:ListOperations<K, V>

方法 (Java) 原始指令 (Redis) 描述
leftPush(key, value) LPUSH key value 从左侧(头部)推入
rightPush(key, value) RPUSH key value 从右侧(尾部)推入
leftPop(key) LPOP key 从左侧弹出
rightPop(key) RPOP key 从右侧弹出
range(key, start, end) LRANGE key start stop 获取指定范围元素(0, -1 为全部)

Java 代码示例

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
public void listDemo() {
String key = "task:queue";

// 1. LPUSH: 生产者推送消息
redisTemplate.opsForList().leftPush(key, "task_1");
redisTemplate.opsForList().leftPushAll(key, "task_2", "task_3");

// 2. RPOP: 消费者消费消息
Object task = redisTemplate.opsForList().rightPop(key);

// 3. LRANGE: 查看队列所有数据
List<Object> allTasks = redisTemplate.opsForList().range(key, 0, -1);
}

Set 类型

对应的 Redis 结构:Set (无序去重集合)

核心类:SetOperations<K, V>

方法 (Java) 原始指令 (Redis) 描述
add(key, values...) SADD key member ... 添加元素
members(key) SMEMBERS key 获取所有成员
isMember(key, value) SISMEMBER key member 判断是否是成员
size(key) SCARD key 获取集合大小
remove(key, values...) SREM key member ... 移除元素

Java 代码示例

Java

1
2
3
4
5
6
7
8
9
10
11
12
public void setDemo() {
String key = "users:like:article:1";

// 1. SADD: 点赞(去重)
redisTemplate.opsForSet().add(key, "user_1", "user_2", "user_3");

// 2. SISMEMBER: 检查是否点赞过
Boolean hasLiked = redisTemplate.opsForSet().isMember(key, "user_1");

// 3. SMEMBERS: 获取所有点赞用户
Set<Object> users = redisTemplate.opsForSet().members(key);
}

ZSet 类型

对应的 Redis 结构:Sorted Set (有序集合)

核心类:ZSetOperations<K, V>

方法 (Java) 原始指令 (Redis) 描述
add(key, value, score) ZADD key score member 添加元素并指定分数
range(key, start, end) ZRANGE key start stop 正序获取范围元素
reverseRange(key, start, end) ZREVRANGE key start stop 倒序获取范围元素(常用于排行榜)
incrementScore(key, v, delta) ZINCRBY key increment member 增加元素分数

Java 代码示例

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
public void zSetDemo() {
String key = "game:rank";

// 1. ZADD: 更新玩家分数
redisTemplate.opsForZSet().add(key, "PlayerA", 100);
redisTemplate.opsForZSet().add(key, "PlayerB", 95);

// 2. ZINCRBY: 分数增加
redisTemplate.opsForZSet().incrementScore(key, "PlayerA", 10); // 变为 110

// 3. ZREVRANGE: 获取 Top 10 排行榜
Set<Object> top10 = redisTemplate.opsForZSet().reverseRange(key, 0, 9);
}

通用 Key 操作

这些操作直接在 RedisTemplate 实例上调用,不经过 opsForX

方法 (Java) 原始指令 (Redis) 描述
delete(key) DEL key 删除 Key
hasKey(key) EXISTS key 检查 Key 是否存在
expire(key, time, unit) EXPIRE key seconds 设置过期时间
getExpire(key) TTL key 获取剩余存活时间
keys(pattern) KEYS pattern 查找匹配的 Key(生产环境慎用)

Java 代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void commonDemo() {
String key = "temp:data";

// 1. EXPIRE: 设置过期时间
redisTemplate.expire(key, 10, TimeUnit.SECONDS);

// 2. TTL: 获取剩余时间
Long expire = redisTemplate.getExpire(key);

// 3. DEL: 删除
redisTemplate.delete(key);

// 4. EXISTS: 检查存在
Boolean exists = redisTemplate.hasKey(key);
}

StringRedisTemplate 常用 API

StringRedisTemplateRedisTemplate<String, String> 的子类,其 Key 和 Value 的序列化器默认都配置为 StringRedisSerializer。这意味着它存入 Redis 的数据是纯字符串,不会包含任何 Java 类信息(class info),在 Redis 客户端(如 redis-cli)中查看时完全可读。

核心特点:

  • Key:必须是 String。
  • Value:必须是 String。
  • 适用场景:缓存简单的字符串、计数器、或者手动将对象转为 JSON 字符串后存储(这是最常用的方式,能避免序列化反序列化的类兼容性问题)。

无需额外配置,Spring Boot 自动装配,直接注入即可:

1
2
@Autowired
private StringRedisTemplate stringRedisTemplate;

String 类型

场景:缓存 JSON 字符串、验证码、计数器。

1
2
3
4
5
6
7
8
9
10
11
12
13
public void stringDemo() {
// 1. SET: 存入普通字符串
stringRedisTemplate.opsForValue().set("city", "Beijing");

// 2. SETEX: 存入验证码,60秒过期
stringRedisTemplate.opsForValue().set("sms:code:13800000000", "123456", 60, TimeUnit.SECONDS);

// 3. INCR: 计数器 (虽然是字符串,但如果是数字格式,Redis 允许自增)
stringRedisTemplate.opsForValue().increment("page:view", 1);

// 4. GET: 取出
String value = stringRedisTemplate.opsForValue().get("city");
}

Hash 类型

注意:Hash 的 Field(项)和 Value(值)也都必须是 String。

1
2
3
4
5
6
7
8
9
10
11
12
13
public void hashDemo() {
String key = "user:1001:profile";

// 1. HSET: 所有的值都必须转为 String
stringRedisTemplate.opsForHash().put(key, "name", "Jason");
stringRedisTemplate.opsForHash().put(key, "age", "18"); // int 要转 string

// 2. HGET: 获取出来也是 String
String name = (String) stringRedisTemplate.opsForHash().get(key, "name");

// 3. HMGET: 获取多个字段
List<Object> values = stringRedisTemplate.opsForHash().multiGet(key, Arrays.asList("name", "age"));
}

List 类型

1
2
3
4
5
6
7
8
9
public void listDemo() {
String key = "msg:queue";

// LPUSH
stringRedisTemplate.opsForList().leftPush(key, "message_1");

// LRANGE
List<String> list = stringRedisTemplate.opsForList().range(key, 0, -1);
}

Set 类型

1
2
3
4
5
6
7
8
9
public void setDemo() {
String key = "tags:java";

// SADD
stringRedisTemplate.opsForSet().add(key, "spring", "redis", "mybatis");

// SMEMBERS
Set<String> members = stringRedisTemplate.opsForSet().members(key);
}

ZSet 类型

1
2
3
4
5
6
7
8
9
public void zsetDemo() {
String key = "ranking";

// ZADD
stringRedisTemplate.opsForZSet().add(key, "user_a", 100);

// ZRANGE: 获取 String 类型的成员
Set<String> topUsers = stringRedisTemplate.opsForZSet().range(key, 0, 10);
}

如何存储对象?

在企业开发中,StringRedisTemplate 最常见的用法是手动序列化

为什么要手动序列化?

如果使用 RedisTemplate<String, Object> 并配置 Jackson 序列化器,Redis 中会存储额外的 @class 属性(全限定类名)。如果将来包名变了,或者不同项目共用 Redis 但包结构不同,反序列化就会报错。

使用 StringRedisTemplate + JSON工具 可以完全掌控数据格式,通用性最强。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Autowired
private StringRedisTemplate stringRedisTemplate;
// 假设使用 Hutool 的 JSONUtil 或 Jackson 的 ObjectMapper
// import cn.hutool.json.JSONUtil;

public void objectDemo() {
User user = new User(1, "Jason", "admin");
String key = "user:1";

// 【存】:手动转为 JSON 字符串
String jsonStr = JSONUtil.toJsonStr(user);
stringRedisTemplate.opsForValue().set(key, jsonStr);

// 【取】:取出字符串,手动转回对象
String resultJson = stringRedisTemplate.opsForValue().get(key);
if (resultJson != null) {
User resultUser = JSONUtil.toBean(resultJson, User.class);
System.out.println(resultUser.getName());
}
}

推荐-StringRedisTemplate

模板类 泛型 序列化方式 适用场景
StringRedisTemplate <String, String> String 序列化 首选。存取普通字符串,或手动转换 JSON 对象。数据通用性最强,无类名耦合。
RedisTemplate <String, Object> 需手动配置 (JSON/JDK) 适合仅仅在 Java 内部使用,且不希望手动编写 JSON 转换代码的场景。但需注意类名兼容性。