Redis实战案例:构建高性能分布式系统
文章结构如下:
- 使用Redis实现会话管理
- Redis实战案例:分布式锁
- 使用Redis实现缓存系统
- Redis在排行榜系统中的应用
- Redis在分布式系统中的应用(服务发现、配置中心等)
一、使用Redis实现会话管理
场景痛点
传统Session存储在单体架构中面临:
水平扩展困难
服务器重启导致会话丢失
跨服务会话共享问题
Redis解决方案
// Spring Boot配置
@Configuration
@EnableRedisHttpSession // 开启Redis Session存储
public class SessionConfig {
@Bean
public RedisConnectionFactory connectionFactory() {
return new LettuceConnectionFactory("redis-server", 6379);
}
}
// 登录控制器
@RestController
public class LoginController {
@PostMapping("/login")
public String login(@RequestParam String username,
HttpSession session) {
// 会话信息存入Redis
session.setAttribute("user", username);
return "登录成功";
}
@GetMapping("/profile")
public String profile(HttpSession session) {
// 从Redis获取会话
String user = (String) session.getAttribute("user");
return "当前用户: " + user;
}
}
核心优势:
会话数据TTL自动过期(默认30分钟)
支持千万级并发会话存储
服务重启不影响用户登录状态
二、Redis实现分布式缓存系统
缓存穿透解决方案
@Service
public class ProductService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 空值缓存+布隆过滤器防穿透
public Product getProduct(Long id) {
String key = "product:" + id;
// 1. 查缓存
Product product = (Product) redisTemplate.opsForValue().get(key);
if (product != null) {
return product;
}
// 2. 布隆过滤器判断是否存在
if (!bloomFilter.mightContain(id)) {
return null; // 不存在直接返回
}
// 3. 查数据库
product = productDao.findById(id);
if (product == null) {
// 缓存空值防止穿透
redisTemplate.opsForValue().set(key, "NULL", 5, TimeUnit.MINUTES);
return null;
}
// 4. 写入缓存
redisTemplate.opsForValue().set(key, product, 1, TimeUnit.HOURS);
return product;
}
}
缓存雪崩预防:
// 差异化过期时间
public void cacheProducts(List<Product> products) {
Random random = new Random();
for (Product p : products) {
// 基础1小时 + 随机30分钟
int expire = 60 + random.nextInt(30);
redisTemplate.opsForValue().set(
"product:" + p.getId(),
p,
expire,
TimeUnit.MINUTES
);
}
}
三、Redis在排行榜系统中的应用
实时游戏积分榜实现
@Service
public class RankingService {
private static final String RANK_KEY = "game:leaderboard";
// 更新玩家分数
public void updateScore(String playerId, int score) {
redisTemplate.opsForZSet().add(RANK_KEY, playerId, score);
}
// 获取TOP10玩家
public List<Player> getTop10() {
Set<ZSetOperations.TypedTuple<Object>> set =
redisTemplate.opsForZSet().reverseRangeWithScores(RANK_KEY, 0, 9);
return set.stream().map(tuple ->
new Player((String) tuple.getValue(), tuple.getScore())
).collect(Collectors.toList());
}
// 获取玩家排名
public Long getPlayerRank(String playerId) {
// ZREVRANK获取从高到低排名
return redisTemplate.opsForZSet().reverseRank(RANK_KEY, playerId);
}
}
// 玩家对象
@Data
@AllArgsConstructor
class Player {
private String playerId;
private Double score;
}
功能扩展:
// 获取分段榜单(第11-20名)
redisTemplate.opsForZSet().reverseRange(RANK_KEY, 10, 19);
// 实时刷新榜单(每5秒)
@Scheduled(fixedRate = 5000)
public void refreshLeaderboard() {
// 自动清理7天前的数据
long cutoff = System.currentTimeMillis() - (7 * 86400000);
redisTemplate.opsForZSet().removeRangeByScore(RANK_KEY, 0, cutoff);
}
四、分布式系统中的应用
1. 分布式锁实现
public class DistributedLock {
private static final String LOCK_PREFIX = "lock:";
public boolean tryLock(String lockKey, long expireSeconds) {
String lockName = LOCK_PREFIX + lockKey;
// SETNX+EXPIRE原子操作
return redisTemplate.execute((RedisCallback<Boolean>) conn -> {
Boolean acquired = conn.set(
lockName.getBytes(),
"locked".getBytes(),
Expiration.seconds(expireSeconds),
RedisStringCommands.SetOption.SET_IF_ABSENT
);
return acquired != null && acquired;
});
}
public void unlock(String lockKey) {
// 使用Lua脚本保证原子删除
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else return 0 end";
redisTemplate.execute(script,
Collections.singletonList(LOCK_PREFIX + lockKey),
"locked"
);
}
}
2. 服务发现与心跳检测
// 服务注册
@Scheduled(fixedRate = 5000)
public void registerService() {
String key = "services:" + serviceName;
// 记录服务节点+时间戳
redisTemplate.opsForZSet().add(
key,
instanceId,
System.currentTimeMillis()
);
}
// 健康检查
@Scheduled(fixedRate = 10000)
public void checkServices() {
String key = "services:" + serviceName;
// 清理30秒未上报的节点
double min = System.currentTimeMillis() - 30000;
redisTemplate.opsForZSet().removeRangeByScore(key, 0, min);
}
五、实时消息系统
发布/订阅模型
@Configuration
public class RedisPubSubConfig {
@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory factory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(factory);
container.addMessageListener(messageListener(), new ChannelTopic("order:created"));
return container;
}
@Bean
public MessageListener messageListener() {
return (message, pattern) -> {
String orderMsg = new String(message.getBody());
System.out.println("收到新订单: " + orderMsg);
// 处理业务逻辑...
};
}
}
// 订单服务发布消息
@Service
public class OrderService {
public void createOrder(Order order) {
orderDao.save(order);
redisTemplate.convertAndSend("order:created", order.toString());
}
}
六、高级实战案例:秒杀系统
@Service
public class SeckillService {
private static final String STOCK_KEY = "seckill:stock:";
private static final String USER_LIMIT_KEY = "seckill:user:";
public boolean trySeckill(Long itemId, String userId) {
// 1. 校验用户购买资格
String userKey = USER_LIMIT_KEY + itemId;
if (redisTemplate.opsForValue().increment(userKey, 1) > 1) {
return false; // 限购1件
}
redisTemplate.expire(userKey, 1, TimeUnit.HOURS);
// 2. 原子扣减库存
String stockKey = STOCK_KEY + itemId;
Long stock = redisTemplate.opsForValue().decrement(stockKey);
if (stock == null || stock < 0) {
redisTemplate.opsForValue().increment(stockKey); // 库存回滚
return false;
}
// 3. 发送MQ创建订单
kafkaTemplate.send("seckill_orders", userId + ":" + itemId);
return true;
}
}
优化点:
库存预热:活动前加载库存到Redis
Lua脚本:合并操作为原子指令
限流策略:Redis-Cell模块实现令牌桶
总结:Redis最佳实践
场景 | 核心数据结构 | 关键优化点 |
---|---|---|
会话管理 | String | 合理设置TTL |
分布式缓存 | String/Hash | 空值缓存+布隆过滤器 |
排行榜 | ZSet | 分段查询+定期清理 |
分布式锁 | String | Lua脚本保证原子性 |
服务发现 | ZSet | 心跳检测+自动清理 |
秒杀系统 | String+Hash | 库存预热+Lua原子操作 |
重要提醒:
生产环境务必启用持久化:
AOF
+RDB
混合模式集群部署至少3主3从,保证高可用
使用
Redis 6.0+
支持多线程IO提升吞吐量