Redis实战案例

发布于:2025-07-12 ⋅ 阅读:(23) ⋅ 点赞:(0)

Redis实战案例:构建高性能分布式系统

文章结构如下:

  1. 使用Redis实现会话管理
  2. Redis实战案例:分布式锁
  3. 使用Redis实现缓存系统
  4. Redis在排行榜系统中的应用
  5. 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提升吞吐量