Java操作Redis字符串:从入门到实战的完整案例解析
目录导读

Redis字符串基础与Java集成环境搭建
1 Redis字符串的本质
Redis的字符串(String)是最基础的数据类型,但功能远不止存储文本,它可以存储普通字符串、JSON序列化数据、数字(支持原子加减)、二进制数据(如图片Base64编码)等,其最大存储容量为512MB,是构建缓存、计数器、分布式锁的核心工具。
2 Java连接Redis的3种主流方式
- Jedis:轻量级客户端,API直接对应Redis命令,适合小规模项目
- Lettuce:基于Netty的异步客户端,支持连接池和集群模式,Spring Boot默认集成
- Redisson:提供分布式对象和锁的高级封装,学习曲线稍陡
环境配置示例(以Lettuce + Spring Boot为例):
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
# application.yml
spring:
redis:
host: localhost
port: 6379
password: your_password
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
Java操作Redis字符串的5个核心方法
1 基础存取(SET/GET)
@Autowired
private StringRedisTemplate stringRedisTemplate;
public void basicExample() {
// 存储字符串
stringRedisTemplate.opsForValue().set("user:name", "张三");
// 读取字符串
String name = stringRedisTemplate.opsForValue().get("user:name");
System.out.println(name); // 输出:张三
// 带过期时间的存储(单位:秒)
stringRedisTemplate.opsForValue().set("tmp:code", "123456", 60);
}
注意:StringRedisTemplate与RedisTemplate<String, String>区别在于前者已设置序列化器为String方式,后者需自行配置。
2 批量操作(MSET/MGET)
public void batchExample() {
Map<String, String> userMap = new HashMap<>();
userMap.put("user:1:name", "李四");
userMap.put("user:1:age", "25");
userMap.put("user:1:city", "北京");
// 批量写入
stringRedisTemplate.opsForValue().multiSet(userMap);
// 批量读取
List<String> keys = Arrays.asList("user:1:name", "user:1:age");
List<String> values = stringRedisTemplate.opsForValue().multiGet(keys);
// values = [李四, 25]
}
3 数字操作(INCR/DECR)
public void numericExample() {
String key = "counter:pageviews";
// 原子自增(返回自增后的值)
Long visitCount = stringRedisTemplate.opsForValue().increment(key);
// 自增指定步长
stringRedisTemplate.opsForValue().increment(key, 10);
// 自减
stringRedisTemplate.opsForValue().decrement(key);
// 注意:如果key不存在,初始值为0再操作
}
应用场景:文章阅读量、商品秒杀库存、接口调用次数统计。
4 字符串长度与追加(STRLEN/APPEND)
public void stringManipulation() {
stringRedisTemplate.opsForValue().set("msg", "Hello");
// 获取字符串长度
Long length = stringRedisTemplate.opsForValue().size("msg"); // 5
// 追加字符串(返回追加后总长度)
Integer newLength = stringRedisTemplate.opsForValue().append("msg", " World");
// msg变为"Hello World"
}
5 设置值并返回旧值(GETSET)
public String getSetExample() {
String oldValue = stringRedisTemplate.opsForValue().getAndSet("user:token", "new_token_abc");
// 第一次调用返回null(如果key不存在),第二次返回旧token
return oldValue;
}
典型用途:定时刷新token时保留上一个值用于校验。
实战案例:缓存用户会话信息
1 需求描述
用户登录后,将用户的基本信息(userId、昵称、角色、权限)缓存到Redis,设置过期时间为30分钟,并在每次请求中校验会话有效性。
2 代码实现
@Component
public class SessionManager {
@Autowired
private StringRedisTemplate redisTemplate;
private static final String SESSION_PREFIX = "session:";
private static final long EXPIRE_SECONDS = 1800;
// 缓存会话
public void cacheSession(String token, UserSession session) {
String json = JSONObject.toJSONString(session);
redisTemplate.opsForValue().set(SESSION_PREFIX + token, json, EXPIRE_SECONDS, TimeUnit.SECONDS);
}
// 获取会话(自动延期)
public UserSession getSession(String token) {
String key = SESSION_PREFIX + token;
String json = redisTemplate.opsForValue().get(key);
if (json == null) {
return null;
}
// 每次访问重置过期时间(滑动窗口)
redisTemplate.expire(key, EXPIRE_SECONDS, TimeUnit.SECONDS);
return JSONObject.parseObject(json, UserSession.class);
}
// 删除会话(登出)
public void removeSession(String token) {
redisTemplate.delete(SESSION_PREFIX + token);
}
}
3 性能优化建议
- 连接池配置:
max-active根据并发量设置,通常为CPU核心数的2倍 - 序列化选择:使用Jackson或FastJSON时,
@JsonFormat注解避免日期序列化问题 - 批量处理:当需要同时存储多个会话时,使用
multiSet替代循环set
常见踩坑与优化策略
1 踩坑1:序列化混乱导致的数据乱码
现象:用RedisTemplate存储中文,控制台看到\xac\xed\x00\x05t\x00\x04乱码。
原因:默认使用JdkSerializationRedisSerializer,存储时带了类结构信息。
解决:配置自定义序列化器:
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, String> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new StringRedisSerializer());
return template;
}
2 踩坑2:大Key引发的性能问题
风险:单个字符串超过10MB时,GET命令可能导致Redis阻塞其他操作。
优化方案:
- 使用
RedisBloom布隆过滤器预检数据是否存在 - 将大JSON拆分为多个小key,例如
user:100:base_info和user:100:ext_info - 启用Redis的
--bigkeys参数定期扫描
3 踩坑3:字段命名不规范导致查找困难
最佳实践:
- 使用冒号分层:
业务:对象:属性,如order:202310:status - 避免无意义的key前缀,如
a123这种 - 设置合理的TTL,防止内存泄漏
问答环节:高频疑难解答
Q1:StringRedisTemplate和RedisTemplate有什么区别?
A:StringRedisTemplate继承自RedisTemplate,但默认使用String序列化器,因此只支持字符串类型的key和value,RedisTemplate默认使用JDK序列化,可存储对象,但需要自行配置序列化器,日常缓存字符串推荐直接使用StringRedisTemplate。
Q2:Redis字符串如何实现分布式锁?
A:利用SET key value NX EX seconds命令的原子性。
Boolean locked = stringRedisTemplate.opsForValue()
.setIfAbsent("lock:order", "1");
if (locked) {
try {
// 执行业务
} finally {
stringRedisTemplate.delete("lock:order");
}
}
注意:生产环境建议使用Redisson,避免锁释放异常。
Q3:为什么我存JSON到Redis,取出来解析时报错?
A:常见原因有3个:
- 序列化时使用了
Jackson的@JsonFormat,但未在Redis中设置时区 - 存储的JSON字符串末尾包含不可见字符(如换行符)
- 使用了不同客户端(如Jedis和Lettuce)序列化方式不一致
排查:先打印原字符串查看完整内容,再手动用在线JSON工具校验。
Q4:如何批量删除以某个前缀开头的key?
A:Redis原生不支持通配符删除,需组合使用SCAN和DEL:
public void deleteByPrefix(String prefix) {
Set<String> keys = new HashSet<>();
ScanOptions options = ScanOptions.scanOptions().match(prefix + "*").count(1000).build();
Cursor<String> cursor = redisTemplate.scan(options);
while (cursor.hasNext()) {
keys.add(cursor.next());
}
if (!keys.isEmpty()) {
redisTemplate.delete(keys);
}
}
Q5:Redis字符串最大能存多大?超过怎么办?
A:官方限制512MB,如果数据超过,建议方案:
- 分片存储:
user_data_1,user_data_2 - 使用Hash类型替代(适合结构化数据)
- 二级缓存(Redis+本地缓存)
通过以上案例和技巧,您已经掌握了Java操作Redis字符串的核心技能,建议在实际项目中先将简单的缓存需求用字符串类型实现,再逐步探索Hash、List等更复杂的数据结构。合理的key命名和TTL设计是Redis性能优化的第一步。