在高并发、高流量后端系统中,数据库往往是性能瓶颈 ------ 频繁的数据库查询会导致响应延迟、数据库压力过大。Redis 作为高性能键值对缓存数据库,支持内存存储、持久化、分布式部署,可通过缓存热点数据、实现分布式锁等方式,大幅提升系统响应速度、降低数据库压力,同时解决分布式环境下的并发安全问题,是企业级系统性能优化的核心工具。
本文聚焦 SpringBoot 与 Redis 的实战落地,从环境搭建、缓存注解使用、手动缓存操作,到分布式锁实现、缓存问题解决方案,全程嵌入 Java 代码教学,帮你快速掌握 Redis 核心应用,打造高性能、高并发的后端系统。
一、核心认知:Redis 核心价值与适用场景
1. 核心优势
- 极致性能:基于内存操作,读写速度可达每秒百万级,响应延迟毫秒级,远超传统数据库;
- 数据结构丰富:支持 String、Hash、List、Set、Sorted Set 等多种数据结构,适配复杂业务场景;
- 分布式兼容:支持主从复制、哨兵模式、集群部署,适配分布式系统架构;
- 功能强大:可实现缓存、分布式锁、消息队列、计数器、限流等多种功能;
- 持久化机制:支持 RDB、AOF 两种持久化方式,确保数据不丢失(兼顾性能与可靠性)。
2. 核心适用场景
- 热点数据缓存:用户信息、商品详情、首页数据等高频查询数据,减轻数据库压力;
- 分布式锁:解决分布式系统中并发修改、资源竞争问题(如库存扣减、订单创建);
- 会话存储:分布式环境下的用户会话共享(替代 Session 本地存储);
- 异步通信:基于 List 实现简单消息队列,处理异步任务(如日志收集、通知推送);
- 计数器与限流:接口访问次数统计、接口限流(防止恶意请求)。
3. Redis 核心概念
- 键(Key):唯一标识,支持字符串、哈希值等格式,建议设计统一命名规范(如
user:info:1001); - 值(Value):支持多种数据结构,存储业务数据,需合理设置过期时间避免内存溢出;
- 过期策略:支持定期删除、惰性删除,可设置键的过期时间,自动释放内存;
- 分布式锁:基于
SETNX(SET if Not Exists)命令,确保同一时间只有一个线程操作资源。
二、核心实战一:环境搭建(Docker 快速部署)
1. Docker 部署 Redis(单节点,开发测试场景)
bash
运行
# 1. 拉取 Redis 镜像(最新稳定版)
docker pull redis:latest
# 2. 启动 Redis 容器(配置密码、持久化、端口映射)
docker run -d --name redis -p 6379:6379 \
-v redis-data:/data \ # 挂载数据卷,持久化数据
-e REDIS_PASSWORD=redis123 \ # 设置访问密码
redis:latest \
redis-server --requirepass redis123 \ # 开启密码验证
--appendonly yes # 开启 AOF 持久化(数据更可靠)
- 连接测试:使用 Redis 客户端(如 Redis Desktop Manager)连接
localhost:6379,密码redis123,验证服务可用性; - 核心配置说明:AOF 持久化会记录所有写操作,重启后可恢复数据,适合对数据可靠性要求高的场景。
三、核心实战二:SpringBoot 集成 Redis 基础配置
1. 引入依赖(Maven)
xml
<!-- Spring Data Redis 依赖(简化 Redis 操作) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 连接池依赖(提升 Redis 连接效率) -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- Web 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2. 配置文件(application.yml)
yaml
# Redis 配置
spring:
redis:
host: localhost # Redis 服务地址
port: 6379 # 端口
password: redis123 # 访问密码
database: 0 # 数据库索引(默认 0,Redis 支持 16 个数据库)
timeout: 10000 # 连接超时时间(毫秒)
lettuce: # 连接池配置(Lettuce 是 SpringBoot 2.x 默认 Redis 客户端)
pool:
max-active: 16 # 最大连接数
max-idle: 8 # 最大空闲连接数
min-idle: 4 # 最小空闲连接数
max-wait: -1 # 最大等待时间(-1 表示无限制)
# 服务端口
server:
port: 8085
3. Redis 配置类(自定义序列化方式)
默认序列化方式会导致 Redis 中存储的数据可读性差,需自定义序列化(JSON 格式),同时配置缓存管理器。
java
运行
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
public class RedisConfig {
// 自定义 RedisTemplate(JSON 序列化,支持复杂对象存储)
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
// 字符串序列化器(Key 用字符串序列化)
StringRedisSerializer stringSerializer = new StringRedisSerializer();
// JSON 序列化器(Value 用 JSON 序列化,保留对象结构)
GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
// 配置 Key、Value 序列化方式
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(jsonSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);
redisTemplate.setHashValueSerializer(jsonSerializer);
// 初始化 RedisTemplate
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
// 配置缓存管理器(支持缓存注解)
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
// 缓存配置(设置默认过期时间 30 分钟,JSON 序列化)
RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30)) // 默认缓存过期时间
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.disableCachingNullValues(); // 禁止缓存 null 值(避免缓存穿透)
// 创建缓存管理器
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(cacheConfig)
.build();
}
}
四、核心实战三:Redis 缓存操作(注解式 + 手动式)
1. 注解式缓存(简化开发,适用于简单场景)
通过 @Cacheable、@CachePut、@CacheEvict 等注解,无需手动编写缓存逻辑,自动实现数据缓存与更新。
(1)Service 层使用示例
java
运行
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.example.redis.entity.User;
import com.example.redis.mapper.UserMapper;
import javax.annotation.Resource;
@Service
public class UserService {
@Resource
private UserMapper userMapper;
// ✅ 缓存查询结果:key 为 "user:info:" + id,缓存过期时间 30 分钟
@Cacheable(value = "user", key = "'info:' + #id", unless = "#result == null")
public User getUserById(Long id) {
// 缓存不存在时,查询数据库(仅执行一次)
System.out.println("查询数据库,用户ID:" + id);
return userMapper.selectById(id);
}
// ✅ 更新缓存:更新数据库后,同步更新缓存(避免缓存与数据库不一致)
@CachePut(value = "user", key = "'info:' + #user.id", unless = "#user == null")
public User updateUser(User user) {
userMapper.updateById(user);
return user;
}
// ✅ 清除缓存:删除数据库数据后,删除对应缓存
@CacheEvict(value = "user", key = "'info:' + #id")
public void deleteUser(Long id) {
userMapper.deleteById(id);
}
// ✅ 清除所有缓存(如批量更新时)
@CacheEvict(value = "user", allEntries = true)
public void clearUserCache() {
// 仅清除缓存,无业务逻辑
}
}
(2)Controller 层接口
java
运行
import org.springframework.web.bind.annotation.*;
import com.example.redis.entity.User;
import com.example.redis.result.Result;
import com.example.redis.service.UserService;
import javax.annotation.Resource;
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@GetMapping("/{id}")
public Result<User> getUserById(@PathVariable Long id) {
User user = userService.getUserById(id);
return Result.success(user);
}
@PutMapping
public Result<User> updateUser(@RequestBody User user) {
User updatedUser = userService.updateUser(user);
return Result.success(updatedUser);
}
@DeleteMapping("/{id}")
public Result<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return Result.success();
}
}
2. 手动式缓存(灵活控制,适用于复杂场景)
通过 RedisTemplate 手动操作 Redis,支持多种数据结构,适配复杂业务场景(如 Hash、List 操作)。
(1)Redis 工具类封装
java
运行
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtils {
@Resource
private RedisTemplate<String, Object> redisTemplate;
// ✅ 存储 String 类型数据(设置过期时间)
public void setString(String key, Object value, Long expireTime, TimeUnit timeUnit) {
redisTemplate.opsForValue().set(key, value, expireTime, timeUnit);
}
// ✅ 获取 String 类型数据
public Object getString(String key) {
return redisTemplate.opsForValue().get(key);
}
// ✅ 删除数据
public Boolean delete(String key) {
return redisTemplate.delete(key);
}
// ✅ 判断键是否存在
public Boolean hasKey(String key) {
return redisTemplate.hasKey(key);
}
// ✅ 设置键过期时间
public Boolean expire(String key, Long expireTime, TimeUnit timeUnit) {
return redisTemplate.expire(key, expireTime, timeUnit);
}
// ✅ 存储 Hash 类型数据(示例:存储用户详情,字段拆分)
public void setHash(String key, String hashKey, Object value) {
redisTemplate.opsForHash().put(key, hashKey, value);
}
// ✅ 获取 Hash 类型数据
public Object getHash(String key, String hashKey) {
return redisTemplate.opsForHash().get(key, hashKey);
}
}
(2)工具类使用示例(热点商品缓存)
java
运行
import org.springframework.stereotype.Service;
import com.example.redis.entity.Goods;
import com.example.redis.mapper.GoodsMapper;
import com.example.redis.utils.RedisUtils;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Service
public class GoodsService {
@Resource
private GoodsMapper goodsMapper;
@Resource
private RedisUtils redisUtils;
// 热点商品缓存(手动控制缓存逻辑)
public Goods getHotGoodsById(Long id) {
String key = "goods:hot:" + id;
// 1. 先查缓存
Goods goods = (Goods) redisUtils.getString(key);
if (goods != null) {
return goods;
}
// 2. 缓存不存在,查数据库
goods = goodsMapper.selectById(id);
if (goods != null) {
// 3. 存入缓存,设置过期时间 10 分钟(热点数据可缩短过期时间)
redisUtils.setString(key, goods, 10L, TimeUnit.MINUTES);
}
return goods;
}
}
五、核心实战四:分布式锁实现(解决并发安全问题)
分布式环境下,多个服务实例同时操作同一资源(如库存扣减)会导致数据不一致,通过 Redis 分布式锁可确保同一时间只有一个线程操作资源。
1. 分布式锁工具类
java
运行
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Component
public class RedisDistributedLock {
@Resource
private RedisTemplate<String, Object> redisTemplate;
// 锁前缀(避免键冲突)
private static final String LOCK_PREFIX = "lock:";
// 锁过期时间(避免死锁,默认 30 秒)
private static final Long LOCK_EXPIRE = 30L;
/**
* 获取分布式锁
* @param lockKey 锁标识(如 "stock:1001")
* @param requestId 唯一标识(如 UUID,确保释放锁时是自己的锁)
* @return 是否获取成功
*/
public Boolean tryLock(String lockKey, String requestId) {
String key = LOCK_PREFIX + lockKey;
// SETNX 命令:当键不存在时设置值,原子操作(确保并发安全)
Boolean locked = redisTemplate.opsForValue().setIfAbsent(key, requestId, LOCK_EXPIRE, TimeUnit.SECONDS);
return locked != null && locked;
}
/**
* 释放分布式锁
* @param lockKey 锁标识
* @param requestId 唯一标识
*/
public void unlock(String lockKey, String requestId) {
String key = LOCK_PREFIX + lockKey;
try {
String value = (String) redisTemplate.opsForValue().get(key);
// 仅释放自己的锁(避免释放其他线程的锁)
if (requestId.equals(value)) {
redisTemplate.delete(key);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
2. 分布式锁使用示例(库存扣减)
java
运行
import org.springframework.stereotype.Service;
import com.example.redis.mapper.StockMapper;
import com.example.redis.utils.RedisDistributedLock;
import javax.annotation.Resource;
import java.util.UUID;
@Service
public class StockService {
@Resource
private StockMapper stockMapper;
@Resource
private RedisDistributedLock distributedLock;
// 库存扣减(分布式环境下安全操作)
public Boolean deductStock(Long goodsId, Integer num) {
String lockKey = "stock:" + goodsId;
String requestId = UUID.randomUUID().toString();
try {
// 1. 获取分布式锁(最多等待 5 秒,每隔 1 秒重试)
Boolean locked = false;
int count = 0;
while (!locked && count < 5) {
locked = distributedLock.tryLock(lockKey, requestId);
if (!locked) {
Thread.sleep(1000);
count++;
}
}
if (!locked) {
// 获取锁失败(限流)
return false;
}
// 2. 扣减库存(业务逻辑)
int row = stockMapper.deductStock(goodsId, num);
return row > 0;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
// 3. 释放锁(确保无论是否异常,都释放锁)
distributedLock.unlock(lockKey, requestId);
}
}
}
六、进阶:缓存常见问题解决方案
1. 缓存穿透(查询不存在的数据,缓存无效,直击数据库)
- 问题表现:恶意请求不存在的 ID,缓存无法命中,频繁查询数据库,导致数据库压力过大;
- 解决方案:缓存 null 值(设置较短过期时间),或使用布隆过滤器过滤不存在的 ID。
java
运行
// 缓存 null 值解决方案(修改查询方法)
public Goods getGoodsById(Long id) {
String key = "goods:info:" + id;
Goods goods = (Goods) redisUtils.getString(key);
if (goods != null) {
return goods;
}
// 数据库查询也不存在,缓存 null 值(过期时间 5 分钟)
goods = goodsMapper.selectById(id);
if (goods == null) {
redisUtils.setString(key, null, 5L, TimeUnit.MINUTES);
return null;
}
redisUtils.setString(key, goods, 30L, TimeUnit.MINUTES);
return goods;
}
2. 缓存击穿(热点数据缓存过期,大量请求同时直击数据库)
- 问题表现:热点数据缓存过期瞬间,大量并发请求同时查询数据库,导致数据库压力骤增;
- 解决方案:互斥锁(同一时间只有一个线程查询数据库并更新缓存),或热点数据永不过期。
3. 缓存雪崩(大量缓存同时过期,或 Redis 服务宕机,所有请求直击数据库)
- 问题表现:缓存服务器宕机,或大量缓存设置同一过期时间,导致所有请求直接访问数据库,数据库崩溃;
- 解决方案:Redis 集群部署(高可用),缓存过期时间加随机值(避免同时过期),服务降级(熔断保护数据库)。
七、避坑指南
坑点 1:缓存与数据库数据不一致
表现:更新数据库后,缓存未同步更新,导致查询到旧数据;✅ 解决方案:采用「更新数据库 + 同步更新缓存」或「更新数据库 + 删除缓存」策略,避免单独操作数据库或缓存。
坑点 2:Redis 序列化后数据可读性差
表现:Redis 中存储的数据为乱码,无法直接查看;✅ 解决方案:自定义序列化方式(如 JSON 序列化),避免使用默认的 JdkSerializationRedisSerializer。
坑点 3:分布式锁死锁
表现:获取锁后线程异常退出,未释放锁,导致其他线程无法获取锁;✅ 解决方案:设置锁过期时间,确保锁自动释放;使用 try-finally 确保锁一定会被释放。
坑点 4:Redis 连接池耗尽
表现:系统报错「Could not get a resource from the pool」,无法获取 Redis 连接;✅ 解决方案:合理配置连接池参数(max-active、max-idle),避免频繁创建销毁连接,排查是否有连接未释放的情况。
八、终极总结:Redis 实战的核心是「缓存高效 + 并发安全」
Redis 实战的核心价值,是通过缓存热点数据提升系统响应速度,通过分布式锁解决分布式并发问题,同时兼顾数据可靠性与系统稳定性。企业级开发中,需根据业务场景选择合适的缓存策略与数据结构,规避常见缓存问题,平衡性能与一致性。
核心原则总结:
- 缓存策略适配业务:简单场景用注解式缓存,复杂场景用手动式缓存,热点数据缩短过期时间;
- 并发安全优先:分布式环境下操作共享资源,必须使用分布式锁,避免数据不一致;
- 高可用不可少:生产环境必用 Redis 集群(主从 + 哨兵),避免单点故障导致缓存雪崩;
- 问题提前预防:针对缓存穿透、击穿、雪崩,提前部署解决方案,而非事后补救。