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》

相关推荐
叫我阿柒啊5 小时前
从全栈工程师视角解析Java与前端技术在电商场景中的应用
java· 消息队列· spring boot· 微服务· vue3· 安全· 前端框架
程序边界5 小时前
Oracle到金仓数据库信创改造迁移实施规划方案(上篇)
后端
|CXHAO|5 小时前
使用tomcat本地部署draw.io
java·tomcat·draw.io
韦德说5 小时前
我的副业之 - 三年磨一剑,让非技术人员也能实现建站自由
后端·程序员·开源
祈祷苍天赐我java之术5 小时前
Maven 从入门到精通
java·maven
绝无仅有5 小时前
某大厂MySQL面试之SQL注入触点发现与SQLMap测试
后端·面试·github
没有bug.的程序员5 小时前
Redis 内存管理机制:深度解析与性能优化实践
java·数据库·redis·性能优化·内存管理机制
绝无仅有5 小时前
某互联网大厂的面试go语言从基础到实战的经验和总结
后端·面试·github
澡点睡觉5 小时前
【golang长途旅行第38站】工厂模式
开发语言·后端·golang