文章主旨
这一组知识点,讲的其实都是:Java 程序怎么去操作 Redis。
它们都服务于同一个目标:让应用更方便、更高效、更安全地使用 Redis,只是各自擅长场景不一样。
所以,这不是简单地记住 4 个名词,而是要理解:它们共同组成了 Java 操作 Redis 的客户端工具体系。
你可以把它们想成一套"分层工具链":
第一层:底层连接与通信( **Lettuce**)
它负责和 Redis 服务器真正通信,偏底层,偏性能,偏生产环境能力。
第二层:Spring 风格的基础操作封装( **RedisTemplate**/ **StringRedisTemplate**)
它们让你在 Spring 项目里更方便地做常规 Redis 操作。
第三层:高级能力封装( **Redisson**)
它不只是操作 Redis 数据,而是把 Redis 封装成了很多可直接使用的分布式组件。
分点讲解客户端
RedisTemplate
- RedisTemplate 是什么?
:::color3
RedisTemplate 是 Spring Data Redis 提供的一个 通用型 Redis 操作模板。
它的核心作用,就是把对 Redis 的各种操作,包装成 Java 里比较统一、比较好调用的 API。
:::
- RedisTemplate 有什么作用?
:::color3
不仅仅是封装了操作命令,还管理连接数
为 SpringBoot操作Redis不仅仅是要封装各种不同的命令,还要管理连接,以及数据序列化等等。
于是 Spring 提供了 RedisTemplate,把这些常见动作封装起来,让你在业务代码里可以更自然地写。
:::
- RedisTemplate 的优点
:::color3
- **通用性强:**它不是只支持字符串,而是支持 Redis 多种数据结构。
- **Spring 兼容性好:**在 Spring Boot 项目里配置后就能直接注入使用。
- **适合操作 Java 对象:**如果你要往 Redis 存一个对象,
RedisTemplate比较自然。
:::
- RedisTemplate 的缺点
:::color3
序列化配置比较麻烦。
因为 Redis 本质上存的是二进制或字符串数据,而 Java 对象要存进去,就得先做序列化。
如果 RedisTemplate 的 key、value、hashKey、hashValue 序列化器没有配置好,就容易出现这些问题:
- Redis 里存出来的 key 像乱码
- value 可读性差
- 不同服务之间序列化不兼容
- 反序列化报错
所以很多人说它"好用,但配置烦",原因就在这里。
:::
- 对比其他客户端
:::color3
**<font style="color:#117CEE;">RedisTemplate</font>**vs**<font style="color:#117CEE;">StringRedisTemplate</font>**
StringRedisTemplate本质上可以看作是 RedisTemplate 的字符串特化版本。**<font style="color:#117CEE;">RedisTemplate</font>**** **vs**<font style="color:#117CEE;">Lettuce</font>**
RedisTemplate是上层模板,底层往往还是依赖 Lettuce 去跟 Redis 通信。**<font style="color:#117CEE;">RedisTemplate</font>**vs**<font style="color:#117CEE;">Redisson</font>**
RedisTemplate适合做基础数据操作;Redisson更擅长高级分布式功能。
:::
StringRedisTemplate
- StringRedisTemplate 是什么?
:::color3
StringRedisTemplate 可以理解为:**RedisTemplate<String, String>**** 的专用版本。**
也就是说,key和value 默认就是拿字符串。
所以它特别适合那种"Redis 中存的就是纯字符串内容"的场景。
:::
- StringRedisTemplate 避免了复杂的序列化配置
:::color3
你可以想一下,实际开发中,有大量 Redis 使用场景其实都是字符串:
- 验证码:
phone:code -> 123456 - token:
token:xxx -> userId - 计数器
- 简单 JSON 字符串缓存
- 分布式锁标记值
如果还用通用 RedisTemplate,就得处理更多序列化细节。
而 StringRedisTemplate 直接帮你把最常见的字符串场景优化好了。
所以它出现的意义就是:
针对最常见的字符串操作场景,降低配置复杂度,提高可读性。
:::
- StringRedisTemplate 适用场景
:::color3
它特别适合:key 是字符串、value 也是字符串、数据希望在 Redis 可直接读懂、不想折腾复杂序列化配置
常见应用包括:缓存 JSON 字符串、存验证码、存 token、存状态标记、做简单计数
:::
- StringRedisTemplate 的局限性
:::color3
它虽然方便,但也有边界:
- 它更适合字符串,不适合直接存复杂 Java 对象
- 如果你频繁操作对象,最后还是要手动转 JSON,或者换成
RedisTemplate
:::
Redisson
- Redisson 是什么?
:::color3
Redisson **是Redis 的增强能力工具箱,**封装了很多 **面向对象的分布式工具,**比如:
- 分布式锁
- 布隆过滤器
- 限流器
- 延迟队列
- 分布式集合
- 分布式对象
- 可重入锁、读写锁、公平锁等
:::
- Redisson 不仅仅为了存个值,更多的是解决分布式问题
:::color3
Redisson 价值在于:把这些复杂的分布式问题封装成现成 API,让你少造轮子。
因为在真实业务里,我们用 Redis,往往不只是为了"存个值"。
很多时候我们真正要解决的是这些问题:
- 多个服务实例同时抢同一份资源,怎么加锁?
- 重复请求怎么控制?
- 大量数据判重,怎么节省空间?
- 分布式环境下,怎么做同步控制?
这些问题如果只用 RedisTemplate 或原生命令,也不是不能做,但会有两个问题:
- 实现复杂
- 容易踩坑
例如分布式锁,表面看上去好像一个 setnx 就够了,实际上还涉及:
- 锁过期
- 误删别人的锁
- 可重入
- 看门狗续期
- 异常场景释放锁
:::
- Redisson 独特优点
:::color3
- 高级能力丰富。:很多分布式场景,直接就有现成实现。
- **封装成熟:**比自己基于 Redis 命令手搓要安全很多。
- **面向对象使用体验好:**你调用的是
RLock、RBloomFilter、RRateLimiter,代码可读性更好。
:::
- Redisson 的局限性
:::color3
**代码更重:**它比简单的 RedisTemplate 要更"厚",引入后你其实是引入了一整套高级抽象。
**学习成本:**你需要理解它封装出来的分布式语义,而不只是 Redis 基本命令。
**杀鸡用牛刀:**如果你只是缓存几个字符串,用 Redisson 就有点"杀鸡用牛刀"。
:::
Lettuce
- Lettuce 是什么?
:::color3
Lettuce 是一个 现代、高性能的 Redis Java 客户端 。(更底层)
它是真正负责和 Redis 服务器建立连接、发送命令、接收结果的那一层工具。
即使你业务代码写的是 RedisTemplate,底层实际用的也可能是 Lettuce。
:::
- Lettuce 的局限性
:::color3
Lettuce 更偏底层,所以直接使用时,你会更接近 Redis 原生命令和连接细节。
这意味着:灵活是灵活,但业务开发层面不一定最省事
:::
SpringBoot 整合客户端
StringRedisTemplate
- 导入 Maven 依赖
xml
<dependencies>
<!-- Spring Boot Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Spring Boot Web,方便演示接口 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
- application.yaml 配置
yaml
spring:
redis:
host: localhost
port: 6379
password:
database: 0
timeout: 5000
- service 业务代码
java
package com.example.redisdemo.service;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class StringRedisService {
private final StringRedisTemplate stringRedisTemplate;
public StringRedisService(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
public void setValueWithExpire(String key, String value, long timeoutSeconds) {
// 设置带过期时间的字符串缓存
stringRedisTemplate.opsForValue().set(key, value, timeoutSeconds, TimeUnit.SECONDS);
// 获取字符串缓存
stringRedisTemplate.opsForValue().get(key);
// 删除缓存
stringRedisTemplate.delete(key);
// 计数器 +1
stringRedisTemplate.opsForValue().increment(key);
}
}
RedisTemplate
- 导入 Maven 依赖
xml
<dependencies>
<!-- Spring Boot Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Spring Boot Web,方便演示接口 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
- application.yaml 配置
yaml
spring:
redis:
host: localhost
port: 6379
password:
database: 0
timeout: 5000
- 创建一个实体类,用于演示
java
package com.example.redisdemo.model;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private Long id;
private String name;
private Integer age;
}
- RedisTemplate 配置类
这里的配置类中序列化知识点,在下一篇文章说明。
java
package com.example.redisdemo.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// key 采用 String 序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// value 采用 JSON 序列化
Jackson2JsonRedisSerializer<Object> jacksonSerializer =
new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.activateDefaultTyping(
objectMapper.getPolymorphicTypeValidator(),
ObjectMapper.DefaultTyping.NON_FINAL
);
jacksonSerializer.setObjectMapper(objectMapper);
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(jacksonSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(jacksonSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
- Service 业务代码
java
package com.example.redisdemo.service;
import com.example.redisdemo.model.User;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class UserRedisService {
private final RedisTemplate<String, Object> redisTemplate;
public UserRedisService(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void saveUser(String key, User user) {
redisTemplate.opsForValue().set(key, user, 10, TimeUnit.MINUTES);
}
public User getUser(String key) {
Object value = redisTemplate.opsForValue().get(key);
if (value instanceof User) {
return (User) value;
}
return null;
}
}
Redisson
- Maven 依赖
xml
<dependencies>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.27.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
- application.yaml 配置
yaml
spring:
redis:
host: localhost
port: 6379
password:
database: 0
- Service 业务代码
java
package com.example.redisdemo.service;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class OrderService {
private final RedissonClient redissonClient;
public OrderService(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
public String createOrder(Long userId) {
String lockKey = "lock:order:" + userId;
RLock lock = redissonClient.getLock(lockKey);
boolean locked = false;
try {
// 尝试加锁,最多等 5 秒,锁自动释放时间 10 秒
locked = lock.tryLock(5, 10, TimeUnit.SECONDS);
if (!locked) {
return "系统繁忙,请稍后重试";
}
// 模拟业务处理
System.out.println("用户 " + userId + " 正在创建订单...");
Thread.sleep(3000);
return "订单创建成功";
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return "线程被中断";
} finally {
// 只有当前线程持有锁时才释放
if (locked && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
Lettuce
- Maven 导入依赖
xml
<dependencies>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>6.3.2.RELEASE</version>
</dependency>
</dependencies>
- 使用示例
java
package com.example.redisdemo.lettuce;
import io.lettuce.core.RedisClient;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
public class LettuceDemo {
public static void main(String[] args) {
// 创建客户端
RedisClient redisClient = RedisClient.create("redis://localhost:6379");
// 建立连接
StatefulRedisConnection<String, String> connection = redisClient.connect();
try {
// 获取同步命令接口
RedisCommands<String, String> commands = connection.sync();
// 设置值
commands.set("name", "lettuce");
// 获取值
String value = commands.get("name");
System.out.println("value = " + value);
} finally {
// 关闭连接和客户端
connection.close();
redisClient.shutdown();
}
}
}
封装 RedisUtils
java
package com.example.demo.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtils {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// ========================= 通用操作 =========================
/**
* 设置过期时间
*/
public boolean expire(String key, long timeout, TimeUnit unit) {
try {
return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, unit));
} catch (Exception e) {
throw new RuntimeException("设置过期时间失败", e);
}
}
/**
* 获取过期时间
*/
public long getExpire(String key, TimeUnit unit) {
try {
Long expire = redisTemplate.getExpire(key, unit);
return expire == null ? -1 : expire;
} catch (Exception e) {
throw new RuntimeException("获取过期时间失败", e);
}
}
/**
* 判断 key 是否存在
*/
public boolean hasKey(String key) {
try {
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
} catch (Exception e) {
throw new RuntimeException("判断 key 是否存在失败", e);
}
}
/**
* 删除单个 key
*/
public boolean delete(String key) {
try {
return Boolean.TRUE.equals(redisTemplate.delete(key));
} catch (Exception e) {
throw new RuntimeException("删除 key 失败", e);
}
}
/**
* 批量删除 key
*/
public long delete(Collection<String> keys) {
try {
Long count = redisTemplate.delete(keys);
return count == null ? 0 : count;
} catch (Exception e) {
throw new RuntimeException("批量删除 key 失败", e);
}
}
// ========================= String =========================
/**
* 普通 set
*/
public void set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
} catch (Exception e) {
throw new RuntimeException("String set 失败", e);
}
}
/**
* 带过期时间 set
*/
public void set(String key, Object value, long timeout, TimeUnit unit) {
try {
redisTemplate.opsForValue().set(key, value, timeout, unit);
} catch (Exception e) {
throw new RuntimeException("String set(带过期时间) 失败", e);
}
}
/**
* get
*/
public Object get(String key) {
try {
return redisTemplate.opsForValue().get(key);
} catch (Exception e) {
throw new RuntimeException("String get 失败", e);
}
}
/**
* 自增
*/
public long increment(String key, long delta) {
try {
Long value = redisTemplate.opsForValue().increment(key, delta);
return value == null ? 0 : value;
} catch (Exception e) {
throw new RuntimeException("String increment 失败", e);
}
}
/**
* 自减
*/
public long decrement(String key, long delta) {
try {
Long value = redisTemplate.opsForValue().increment(key, -delta);
return value == null ? 0 : value;
} catch (Exception e) {
throw new RuntimeException("String decrement 失败", e);
}
}
// ========================= List =========================
/**
* 从左侧插入
*/
public long leftPush(String key, Object value) {
try {
Long count = redisTemplate.opsForList().leftPush(key, value);
return count == null ? 0 : count;
} catch (Exception e) {
throw new RuntimeException("List leftPush 失败", e);
}
}
/**
* 从右侧插入
*/
public long rightPush(String key, Object value) {
try {
Long count = redisTemplate.opsForList().rightPush(key, value);
return count == null ? 0 : count;
} catch (Exception e) {
throw new RuntimeException("List rightPush 失败", e);
}
}
/**
* 获取列表范围
*/
public List<Object> listRange(String key, long start, long end) {
try {
List<Object> list = redisTemplate.opsForList().range(key, start, end);
return list == null ? Collections.emptyList() : list;
} catch (Exception e) {
throw new RuntimeException("List range 失败", e);
}
}
/**
* 获取列表长度
*/
public long listSize(String key) {
try {
Long size = redisTemplate.opsForList().size(key);
return size == null ? 0 : size;
} catch (Exception e) {
throw new RuntimeException("List size 失败", e);
}
}
/**
* 左侧弹出
*/
public Object leftPop(String key) {
try {
return redisTemplate.opsForList().leftPop(key);
} catch (Exception e) {
throw new RuntimeException("List leftPop 失败", e);
}
}
/**
* 右侧弹出
*/
public Object rightPop(String key) {
try {
return redisTemplate.opsForList().rightPop(key);
} catch (Exception e) {
throw new RuntimeException("List rightPop 失败", e);
}
}
// ========================= Hash =========================
/**
* put 一个 field
*/
public void hPut(String key, String hashKey, Object value) {
try {
redisTemplate.opsForHash().put(key, hashKey, value);
} catch (Exception e) {
throw new RuntimeException("Hash put 失败", e);
}
}
/**
* putAll
*/
public void hPutAll(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
} catch (Exception e) {
throw new RuntimeException("Hash putAll 失败", e);
}
}
/**
* get 一个 field
*/
public Object hGet(String key, String hashKey) {
try {
return redisTemplate.opsForHash().get(key, hashKey);
} catch (Exception e) {
throw new RuntimeException("Hash get 失败", e);
}
}
/**
* 获取整个 hash
*/
public Map<Object, Object> hEntries(String key) {
try {
return redisTemplate.opsForHash().entries(key);
} catch (Exception e) {
throw new RuntimeException("Hash entries 失败", e);
}
}
/**
* 删除一个或多个 field
*/
public long hDelete(String key, Object... hashKeys) {
try {
Long count = redisTemplate.opsForHash().delete(key, hashKeys);
return count == null ? 0 : count;
} catch (Exception e) {
throw new RuntimeException("Hash delete 失败", e);
}
}
/**
* 判断 field 是否存在
*/
public boolean hHasKey(String key, String hashKey) {
try {
return Boolean.TRUE.equals(redisTemplate.opsForHash().hasKey(key, hashKey));
} catch (Exception e) {
throw new RuntimeException("Hash hasKey 失败", e);
}
}
// ========================= Set =========================
/**
* 添加元素
*/
public long sAdd(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
return count == null ? 0 : count;
} catch (Exception e) {
throw new RuntimeException("Set add 失败", e);
}
}
/**
* 获取所有元素
*/
public Set<Object> sMembers(String key) {
try {
Set<Object> members = redisTemplate.opsForSet().members(key);
return members == null ? Collections.emptySet() : members;
} catch (Exception e) {
throw new RuntimeException("Set members 失败", e);
}
}
/**
* 判断元素是否存在
*/
public boolean sIsMember(String key, Object value) {
try {
return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, value));
} catch (Exception e) {
throw new RuntimeException("Set isMember 失败", e);
}
}
/**
* 删除元素
*/
public long sRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count == null ? 0 : count;
} catch (Exception e) {
throw new RuntimeException("Set remove 失败", e);
}
}
/**
* 获取集合大小
*/
public long sSize(String key) {
try {
Long size = redisTemplate.opsForSet().size(key);
return size == null ? 0 : size;
} catch (Exception e) {
throw new RuntimeException("Set size 失败", e);
}
}
// ========================= ZSet =========================
/**
* 添加元素和分数
*/
public boolean zAdd(String key, Object value, double score) {
try {
return Boolean.TRUE.equals(redisTemplate.opsForZSet().add(key, value, score));
} catch (Exception e) {
throw new RuntimeException("ZSet add 失败", e);
}
}
/**
* 按区间获取元素(升序)
*/
public Set<Object> zRange(String key, long start, long end) {
try {
Set<Object> set = redisTemplate.opsForZSet().range(key, start, end);
return set == null ? Collections.emptySet() : set;
} catch (Exception e) {
throw new RuntimeException("ZSet range 失败", e);
}
}
/**
* 按分数区间获取元素
*/
public Set<Object> zRangeByScore(String key, double min, double max) {
try {
Set<Object> set = redisTemplate.opsForZSet().rangeByScore(key, min, max);
return set == null ? Collections.emptySet() : set;
} catch (Exception e) {
throw new RuntimeException("ZSet rangeByScore 失败", e);
}
}
/**
* 获取元素分数
*/
public Double zScore(String key, Object value) {
try {
return redisTemplate.opsForZSet().score(key, value);
} catch (Exception e) {
throw new RuntimeException("ZSet score 失败", e);
}
}
/**
* 删除元素
*/
public long zRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForZSet().remove(key, values);
return count == null ? 0 : count;
} catch (Exception e) {
throw new RuntimeException("ZSet remove 失败", e);
}
}
/**
* 给元素增加分数
*/
public Double zIncrementScore(String key, Object value, double delta) {
try {
return redisTemplate.opsForZSet().incrementScore(key, value, delta);
} catch (Exception e) {
throw new RuntimeException("ZSet incrementScore 失败", e);
}
}
/**
* 获取排名(升序,0 开始)
*/
public Long zRank(String key, Object value) {
try {
return redisTemplate.opsForZSet().rank(key, value);
} catch (Exception e) {
throw new RuntimeException("ZSet rank 失败", e);
}
}
/**
* 获取倒序排名(0 开始)
*/
public Long zReverseRank(String key, Object value) {
try {
return redisTemplate.opsForZSet().reverseRank(key, value);
} catch (Exception e) {
throw new RuntimeException("ZSet reverseRank 失败", e);
}
}
}