Redis 不只是缓存!Java 打工人必知的 10 个真实工作场景,第 5 个太香了

大家好,我是大华! 在很多Java项目里,Redis就是个@Cacheable,缓存个用户、商品,然后就没了。 但!Redis的本事远不止这点。 我在大厂搬砖的那段时间,用 Spring Boot+Redis 解决过一堆要命的问题。今天我就来分享10个真实又常用的Redis使用场景

1. 限流防崩:别让接口被刷爆

场景 公司搞促销,优惠券的接口被黄牛用脚本疯狂刷,每秒几千的请求,服务器差点炸了。正常用户根本抢不到,页面卡成PPT。

问题 系统扛不住,资源被耗尽,用户体验非常差。

解决方案 用Redis记录每个用户的请求次数,每分钟最多10次,超过直接拒绝。

  • 用户每请求一次,Redis里的计数器+1。
  • 如果是第一次请求,就给这个计数器设置一个60秒的过期时间。
  • 如果计数超过10,就直接返回"请求太频繁"。

Java代码

java 复制代码
public boolean isAllowed(String userId) {
    String key = "rate_limit:" + userId;
    // incr:自增1,如果key不存在,自动创建并设为1
    Long count = redisTemplate.opsForValue().increment(key);
    
    // 如果是第一次请求,设置60秒后自动过期
    if (count == 1) {
        redisTemplate.expire(key, 60, TimeUnit.SECONDS);
    }
    
    // 返回:是否允许(10次以内)
    return count <= 10;
}

黄牛刷不动,系统稳了,用户能正常抢券。

2. 分布式锁:别让订单被重复创建

场景 有时候用户手一抖,点了两下支付按钮,结果生成了两条订单!用户投诉、财务对不上账。

问题 多个服务实例同时处理同一个订单,数据冲突。

解决方案 用Redis加个锁,确保同一时间只有一个请求能处理这个订单。

  • 每次处理订单前,先尝试在Redis里创建一个key,比如 lock:order_123
  • 如果创建成功(说明没人锁),就继续处理。
  • 如果创建失败(说明别人锁了),就提示操作太频繁。

Java代码

java 复制代码
public boolean tryLock(String orderId) {
    String key = "lock:order:" + orderId;
    // setIfAbsent:只有key不存在时才设置,相当于"抢锁"
    Boolean success = redisTemplate.opsForValue()
        .setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
    return Boolean.TRUE.equals(success);
}

注意

  • 这个锁10秒后自动过期,防止死锁。
  • 生产环境建议用Redisson,功能更全,比如可重入锁、看门狗自动续期。

3. 延时任务:订单超时自动关闭

场景 用户下单不付款,占着库存不放,别人想买也买不了。

传统做法 后台定时任务每分钟扫一遍数据库,查哪些订单超时了,再关闭。又慢又耗数据库资源。

Redis做法 用**ZSET(有序集合)**存订单,按超时时间戳排序。

  • 下单时,把订单ID和超时时间戳(比如当前时间+30分钟)存到ZSET。
  • 后台每隔1秒查一次:哪些订单的超时时间 <= 现在时间?
  • 拿到这些订单,关闭它们,释放库存。

Java代码

java 复制代码
// 下单时:把订单加入延时队列
public void addDelayOrder(String orderId) {
    long expireTime = System.currentTimeMillis() + 30 * 60 * 1000; // 30分钟后
    redisTemplate.opsForZSet().add("order_delay", orderId, expireTime);
}

// 后台任务:每秒检查一次
@Scheduled(fixedRate = 1000)
public void checkExpiredOrders() {
    long now = System.currentTimeMillis();
    // 查出所有已超时的订单
    Set<String> expiredOrders = redisTemplate.opsForZSet()
        .rangeByScore("order_delay", 0, now);
    
    if (expiredOrders != null) {
        for (String orderId : expiredOrders) {
            closeOrder(orderId); // 关闭订单逻辑
            redisTemplate.opsForZSet().remove("order_delay", orderId); // 从队列移除
        }
    }
}

比扫库快多了,资源占用少,延迟基本在1秒内。

4. 热点数据统计:实时排行榜

场景 搞活动,要实时显示今日下单最多的10个用户。

问题 用MySQL实时聚合统计,每次查询都慢,页面卡顿。

Redis做法

ZSET,每个用户是成员,下单次数是分数。

  • 用户每下一单,就在排行榜里给他的分数+1。
  • 前端请求时,直接从Redis拿TOP10。

Java代码

java 复制代码
// 用户下单后,排行榜+1
public void addOrderToRank(String userId) {
    redisTemplate.opsForZSet().incrementScore("today_rank", userId, 1);
}

// 获取TOP10
public Set<ZSetOperations.TypedTuple<String>> getTop10() {
    return redisTemplate.opsForZSet()
        .reverseRangeWithScores("today_rank", 0, 9);
}

排行榜秒刷新,运营小姐姐天天夸我。

5. 消息队列:异步处理,太香了!

场景

用户注册后要发邮件、发短信、打标签、推数据到数仓......如果全在注册流程里同步做,页面会卡顿!

问题

核心流程被非核心任务拖慢,用户体验差。

Redis做法

List当简易消息队列。

  • 注册成功后,把"用户注册成功"这个事件扔进Redis的List里。
  • 另外起一个后台线程,不断从List里取事件,异步处理发短信等。

Java代码

java 复制代码
// 注册成功后,发消息
public void onUserRegistered(String userId) {
    redisTemplate.opsForList().leftPush("user_event_queue", "register:" + userId);
}

// 后台Worker:异步处理
@Scheduled(fixedRate = 100)
public void processEvents() {
    // rightPop:从List右边取一条消息,最多等1秒
    String event = redisTemplate.opsForList()
        .rightPop("user_event_queue", 1, TimeUnit.SECONDS);
    
    if (event != null) {
        handleEvent(event); // 异步处理发短信、打标签等
    }
}

注册秒完成,用户体验起飞。消息还能持久化,不怕丢。

PS:第5个真的太香了,简单、快、稳,小公司完全够用。

6. 全局唯一ID生成

场景

分布式系统,多个服务都要生成订单号,怕重复。

MySQL自增? 不行,跨库不好搞。

Redis做法

INCR命令,每次调用自动+1,返回唯一数字。

Java代码

java 复制代码
public String generateOrderId() {
    // 每次调用,自动+1
    Long id = redisTemplate.opsForValue().increment("order_id_seq");
    // 生成类似:ORDER_20240512123456_1001
    return "ORDER_" + System.currentTimeMillis() + "_" + id;
}

优点:简单粗暴,全局唯一,性能好。

7. 用户在线状态

场景

做IM或社交功能,要显示"张三在线"。

传统做法

数据库存状态,频繁更新,IO爆炸。

Redis做法

用户上线,Redis里存个key,30秒过期。客户端每20秒发个心跳,刷新这个key。

原理

Redis自动过期机制,不用手动删。key存在就是在线,没了就是离线。

Java代码

java 复制代码
// 用户上线或心跳
public void updateOnlineStatus(String userId) {
    redisTemplate.opsForValue()
        .set("online:" + userId, "1", 30, TimeUnit.SECONDS);
}

// 判断是否在线
public boolean isOnline(String userId) {
    return Boolean.TRUE.equals(redisTemplate.hasKey("online:" + userId));
}

几百万人在线,Redis内存才占几G,查状态毫秒级。

8. 防重提交:别让用户重复点赞

场景

用户狂点"点赞",数据库写入多条,统计全乱了。

Redis做法

SET记录用户对内容的操作。

  • 用户点赞时,先检查 like:user123:article456 这个key是否存在。
  • 如果存在,说明点过,拒绝。
  • 如果不存在,就创建key,设置1小时过期。

Java代码

java 复制代码
public boolean like(String userId, String articleId) {
    String key = "like:" + userId + ":" + articleId;
    Boolean exists = redisTemplate.hasKey(key);
    if (Boolean.TRUE.equals(exists)) {
        return false; // 已点赞
    }
    // 设置1小时过期
    redisTemplate.opsForValue().set(key, "1", 1, TimeUnit.HOURS);
    return true;
}

点赞去重,数据干净,再也不用半夜修数据了。

9. 临时会话存储

场景

用户登录后,把token和用户信息存哪?

存数据库? 慢!
存Cookie? 不安全!

Redis做法

登录成功后,把用户信息存到Redis,key是token,设置2小时过期。

Java代码

java 复制代码
// 登录成功
public String login(User user) {
    String token = UUID.randomUUID().toString();
    String userJson = JSON.toJSONString(user);
    redisTemplate.opsForValue()
        .set("session:" + token, userJson, 2, TimeUnit.HOURS);
    return token;
}

// 拦截器验证
public User getUserFromToken(String token) {
    String userJson = redisTemplate.opsForValue().get("session:" + token);
    return userJson != null ? JSON.parseObject(userJson, User.class) : null;
}

优点:快、安全、可集中管理(比如主动踢下线)。

10. 秒杀库存预减

场景

做秒杀,库存100件,怕超卖。

难点

MySQL扣库存并发高,容易出错。

Redis做法

提前把库存load到Redis。

  • DECR命令原子性地减库存。
  • 如果返回值 >=0,说明还有库存,可以下单。
  • 如果返回负数,库存不足。

Java代码

java 复制代码
public boolean seckill(String goodsId, String userId) {
    String key = "seckill:stock:" + goodsId;
    // 原子性减1,返回减后的值
    Long left = redisTemplate.opsForValue().decrement(key);
    if (left >= 0) {
        // 库存够,创建订单
        createOrder(goodsId, userId);
        return true;
    }
    return false;
}

注意:记得异步同步到数据库,防止Redis挂了丢数据。

总结

  • 限流
  • 分布式锁
  • 消息队列
  • 排行榜
  • 延时任务
  • 全局ID
  • 在线状态
  • 防重
  • 会话管理
  • 库存预减

当然Redis也不是万能的,适合数据量不大但访问频繁的场景。如果数据量特别大,还是得上专业解决方案。 工具不在多,用对才是王道。

我是大华,专注分享前后端开发的实战笔记。关注我,少走弯路,一起进步!

📌往期精彩

《写给小公司前端的 UI 规范:别让页面丑得自己都看不下去》

《只会写 Mapper 就敢说会 MyBatis?面试官:原理都没懂》

《别学23种了!Java项目中最常用的6个设计模式,附案例》

《Vue3+TS设计模式:5个真实场景让你代码更优雅》

《Elasticsearch 太重?来看看这个轻量级的替代品 Manticore Search》

相关推荐
言慢行善21 小时前
sqlserver模糊查询问题
java·数据库·sqlserver
专吃海绵宝宝菠萝屋的派大星21 小时前
使用Dify对接自己开发的mcp
java·服务器·前端
大数据新鸟1 天前
操作系统之虚拟内存
java·服务器·网络
Tong Z1 天前
常见的限流算法和实现原理
java·开发语言
凭君语未可1 天前
Java 中的实现类是什么
java·开发语言
He少年1 天前
【基础知识、Skill、Rules和MCP案例介绍】
java·前端·python
克里斯蒂亚诺更新1 天前
myeclipse的pojie
java·ide·myeclipse
迷藏4941 天前
**eBPF实战进阶:从零构建网络流量监控与过滤系统**在现代云原生架构中,**网络可观测性**和**安全隔离**已成为
java·网络·python·云原生·架构
迷藏4941 天前
**发散创新:基于Solid协议的Web3.0去中心化身份认证系统实战解析**在Web3.
java·python·web3·去中心化·区块链