Redis 缓存实战指南(含分布式锁代码教学):从基础使用到缓存高可用,解决生产环境核心问题
Redis 作为高性能的内存数据库,凭借「读写速度快、支持多数据结构、可持久化」的优势,已成为后端开发的必备技术 —— 主要用于缓存优化、分布式锁、计数器、排行榜等场景,能大幅提升系统吞吐量,降低数据库压力。但很多开发者在使用 Redis 时,容易陷入「缓存一致性混乱、缓存穿透 / 击穿 / 雪崩、分布式锁死锁」等坑,导致线上故障。
Redis 实战的核心是「按需选型数据结构、设计合理缓存策略、保障高可用与安全性」。本文结合 Java(SpringBoot)与原生 Redis 命令,从基础使用、缓存实战、分布式锁实现到高可用配置,全程嵌入代码教学,帮你彻底掌握 Redis 实战技巧,适配生产环境需求。
一、核心认知:Redis 到底能解决什么问题?
1. Redis 核心价值
- 缓存优化:将热点数据存入内存,查询时优先命中缓存,减少数据库 IO,提升接口响应速度(从毫秒级降至微秒级);
- 异步通信:配合消息队列实现分布式通知(如缓存更新通知);
- 分布式协调:基于 Redis 实现分布式锁、分布式计数器、分布式限流;
- 特殊场景:支持 List/Hash/Sorted Set 等数据结构,适配排行榜、购物车、会话存储等场景。
2. Redis 与数据库的协同原则
核心是「缓存为辅,数据库为主」,遵循「先查缓存,缓存未命中查数据库,查询结果回写缓存」的基础流程,同时需解决「缓存与数据库一致性」问题,避免数据脏读。
二、核心实战一:Redis 基础使用(代码教学,Java + 原生命令)
✅ 原生 Redis 命令(入门必备,直接在 Redis 客户端执行)
1. 核心数据结构使用
bash
运行
# 1. String(字符串,适配缓存单个值、计数器)
SET user:1:name "张三" # 存储键值对,过期时间1小时(3600秒)
SET user:1:name "张三" EX 3600
GET user:1:name # 获取值
INCR counter:login # 计数器自增(统计登录次数)
# 2. Hash(哈希,适配缓存对象,如用户信息、商品详情)
HSET user:1 id 1 username "张三" age 20 # 存储对象属性
HGET user:1 username # 获取单个属性
HMGET user:1 id username # 获取多个属性
HGETALL user:1 # 获取整个对象
# 3. List(列表,适配队列、栈、消息通知)
LPUSH notify:queue "订单创建成功" # 左进(队列用LPUSH+RPOP,栈用LPUSH+LPOP)
RPOP notify:queue # 右出
LLEN notify:queue # 获取列表长度
# 4. Sorted Set(有序集合,适配排行榜、优先级队列)
ZADD rank:score 90 "张三" 85 "李四" # 存储分数与成员
ZRANGE rank:score 0 -1 WITHSORES # 按分数升序查询所有成员
ZREVRANGE rank:score 0 2 # 按分数降序查询前3名
2. 缓存核心命令
bash
运行
EXPIRE user:1 3600 # 给键设置过期时间(秒)
TTL user:1 # 查看键剩余过期时间(-1永久有效,-2已过期)
DEL user:1 # 删除键(缓存失效时使用)
FLUSHDB # 清空当前数据库缓存(测试环境用,生产禁用)
✅ Java(SpringBoot)集成 Redis 完整代码(实战必用)
步骤 1:引入依赖(Maven)
xml
<!-- SpringBoot Redis starter -->
<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>
步骤 2:配置文件(application.yml)
yaml
spring:
redis:
host: localhost # Redis 服务地址
port: 6379 # 端口
password: "" # 密码(生产环境必须设置)
database: 0 # 数据库索引(0-15)
lettuce: # 连接池配置(lettuce 性能优于 jedis)
pool:
max-active: 8 # 最大连接数
max-idle: 8 # 最大空闲连接
min-idle: 2 # 最小空闲连接
max-wait: 100ms # 连接等待时间
步骤 3:核心代码实现(Service 层,缓存用户信息示例)
java
运行
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Service
public class UserRedisService {
@Resource
private StringRedisTemplate stringRedisTemplate; // Spring 封装的 Redis 操作模板
private static final String USER_KEY_PREFIX = "user:";
private static final long CACHE_EXPIRE = 1L; // 缓存过期时间(小时)
// ✅ 1. 缓存用户信息(新增/更新)
public void cacheUser(Long userId, String username, Integer age) {
String key = USER_KEY_PREFIX + userId;
// Hash 结构存储用户对象,便于单独更新属性
stringRedisTemplate.opsForHash().put(key, "username", username);
stringRedisTemplate.opsForHash().put(key, "age", age.toString());
// 设置过期时间,避免缓存雪崩
stringRedisTemplate.expire(key, CACHE_EXPIRE, TimeUnit.HOURS);
}
// ✅ 2. 查询缓存中的用户信息
public String getUsernameFromCache(Long userId) {
String key = USER_KEY_PREFIX + userId;
// 先查缓存,缓存命中直接返回
return (String) stringRedisTemplate.opsForHash().get(key, "username");
}
// ✅ 3. 删除缓存(用户信息更新/删除时同步删除)
public void deleteUserCache(Long userId) {
String key = USER_KEY_PREFIX + userId;
stringRedisTemplate.delete(key);
}
}
三、核心实战二:Redis 缓存三大问题解决方案(代码教学,生产必备)
缓存穿透、击穿、雪崩是 Redis 缓存的高频故障点,需针对性设计防护策略,以下代码可直接落地。
✅ 问题 1:缓存穿透(查询不存在的数据,穿透缓存冲击数据库)
解决方案:缓存空值 + 布隆过滤器(选其一或组合)
java
运行
// 方案1:缓存空值(简单易实现,适合数据不存在场景较少的情况)
public String getUsername(Long userId) {
String key = USER_KEY_PREFIX + userId;
// 1. 查缓存
String username = (String) stringRedisTemplate.opsForHash().get(key, "username");
if (username != null) {
return username;
}
// 2. 缓存未命中,查数据库
User user = userMapper.selectById(userId);
if (user == null) {
// 缓存空值,设置较短过期时间(如5分钟),避免缓存被空值占满
stringRedisTemplate.opsForHash().put(key, "username", "");
stringRedisTemplate.expire(key, 5, TimeUnit.MINUTES);
return null;
}
// 3. 数据库查询成功,回写缓存
cacheUser(userId, user.getUsername(), user.getAge());
return user.getUsername();
}
// 方案2:布隆过滤器(适合数据量大、不存在数据查询频繁的场景,需引入依赖)
// 1. 引入布隆过滤器依赖
// <dependency>
// <groupId>com.google.guava</groupId>
// <artifactId>guava</artifactId>
// <version>32.1.1-jre</version>
// </dependency>
// 2. 初始化布隆过滤器(项目启动时加载所有用户ID)
private BloomFilter<Long> userBloomFilter;
@PostConstruct // 项目启动后执行
public void initBloomFilter() {
List<User> allUser = userMapper.selectList(null);
userBloomFilter = BloomFilter.create(Funnels.longFunnel(), allUser.size(), 0.01); // 误判率0.01
for (User user : allUser) {
userBloomFilter.put(user.getId());
}
}
// 3. 接口层拦截
public String getUsernameWithBloomFilter(Long userId) {
// 布隆过滤器判断ID不存在,直接返回null,不查缓存和数据库
if (!userBloomFilter.mightContain(userId)) {
return null;
}
// 后续流程同“查缓存→查数据库→回写缓存”
return getUsername(userId);
}
✅ 问题 2:缓存击穿(热点 Key 过期,大量请求同时冲击数据库)
解决方案:互斥锁 + 热点 Key 永不过期(选其一)
java
运行
// 方案1:互斥锁(用Redis实现,确保同一时间只有一个请求查数据库)
public String getHotUser(Long userId) {
String key = USER_KEY_PREFIX + userId;
String lockKey = "lock:user:" + userId;
// 1. 查缓存
String username = (String) stringRedisTemplate.opsForHash().get(key, "username");
if (username != null) {
return username;
}
// 2. 缓存未命中,获取互斥锁
boolean locked = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if (locked) {
try {
// 3. 获得锁,查数据库并回写缓存
User user = userMapper.selectById(userId);
if (user != null) {
cacheUser(userId, user.getUsername(), user.getAge());
return user.getUsername();
}
} finally {
// 4. 释放锁
stringRedisTemplate.delete(lockKey);
}
}
// 5. 未获得锁,休眠后重试(或返回默认值)
try {
Thread.sleep(50);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return getHotUser(userId);
}
✅ 问题 3:缓存雪崩(大量 Key 同时过期,数据库被压垮)
解决方案:Key 过期时间加随机值 + Redis 集群高可用
java
运行
// 方案1:过期时间加随机值(避免Key批量过期)
public void cacheUserWithRandomExpire(Long userId, String username, Integer age) {
String key = USER_KEY_PREFIX + userId;
stringRedisTemplate.opsForHash().put(key, "username", username);
stringRedisTemplate.opsForHash().put(key, "age", age.toString());
// 过期时间 = 基础时间(1小时)+ 随机时间(0-300秒)
long randomExpire = CACHE_EXPIRE * 3600 + new Random().nextInt(300);
stringRedisTemplate.expire(key, randomExpire, TimeUnit.SECONDS);
}
// 方案2:Redis集群(主从+哨兵),避免单点故障,保障缓存服务可用(运维配置,代码无需改动)
四、核心实战三:Redis 分布式锁(Redisson 实现,代码教学)
分布式锁用于解决分布式系统中资源竞争问题(如并发下单、库存扣减),Redisson 封装了 Redis 分布式锁的实现,支持自动续期、可重入,比手动实现更稳定。
1. 集成 Redisson
xml
<!-- 引入依赖 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.23.3</version>
</dependency>
2. 分布式锁实战代码(库存扣减场景)
java
运行
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Service
public class StockService {
@Resource
private RedissonClient redissonClient;
@Resource
private StockMapper stockMapper;
private static final String STOCK_LOCK_KEY = "lock:stock:";
// 并发扣减库存
public boolean deductStock(Long productId, Integer num) {
String lockKey = STOCK_LOCK_KEY + productId;
RLock lock = redissonClient.getLock(lockKey);
try {
// 1. 获取锁(等待3秒,自动释放10秒,支持自动续期)
boolean locked = lock.tryLock(3, 10, TimeUnit.SECONDS);
if (!locked) {
return false; // 获取锁失败,返回失败
}
// 2. 扣减库存(先查库存,再扣减,保证原子性)
Stock stock = stockMapper.selectByProductId(productId);
if (stock == null || stock.getNum() < num) {
return false;
}
stock.setNum(stock.getNum() - num);
stockMapper.updateById(stock);
return true;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
} finally {
// 3. 释放锁(只有持有锁的线程才能释放)
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
五、Redis 实战避坑指南
坑点 1:缓存与数据库一致性问题
表现:更新数据库后未同步更新缓存,导致缓存与数据库数据不一致;✅ 解决方案:采用「先更数据库,再删缓存」(推荐),或「缓存更新加锁」,避免并发场景下的数据脏读。
坑点 2:Redis 持久化配置不当,数据丢失
表现:Redis 宕机后,内存数据丢失,缓存雪崩;✅ 解决方案:开启 RDB+AOF 混合持久化,RDB 用于快速恢复,AOF 用于保证数据不丢失。
坑点 3:分布式锁未释放,导致死锁
表现:获取锁后线程异常退出,未释放锁,其他线程无法获取;✅ 解决方案:使用 Redisson 自动释放锁,或手动设置锁过期时间,避免死锁。
六、终极总结:Redis 实战的核心是「平衡性能与可靠性」
Redis 是提升系统性能的「利器」,但绝非「银弹」。实战中需牢记:缓存的核心是「减少数据库压力」,分布式锁的核心是「解决资源竞争」,所有配置与编码都要围绕「高可用、高一致、高性能」展开。
核心原则总结:
- 缓存策略按需选型:根据数据特性选择缓存结构,结合业务场景防护三大缓存问题;
- 高可用优先于性能:Redis 集群 + 持久化是生产环境必备,避免单点故障;
- 分布式锁优先复用成熟组件:Redisson 比手动实现更稳定,减少重复造轮子。
更多推荐




所有评论(0)