上一篇我们详解了Redis监控工具(Redis-Stat+Redis-Exporter),搭建了全天候监控体系,能提前预警故障、快速定位问题。但Redis的核心价值,远不止做缓存和监控,更是支撑高并发、实现复杂业务的利器。
无论是电商秒杀、接口限流防刷,还是社区排行榜、热度排序,这些高频业务场景都离不开Redis。本篇就聚焦秒杀、接口限流、实时排行榜三大真实业务,从业务痛点、方案设计、核心代码到避坑指南,手把手教你用Redis落地实战,直接复用进生产项目。
核心思路:利用Redis高性能、原子操作、丰富数据类型的特性,承接高并发流量、分担数据库压力,实现业务的高效稳定运行。
一、场景一:电商秒杀 —— 扛住瞬时高并发,杜绝超卖
秒杀业务痛点极其突出:瞬时QPS暴涨、库存超卖、数据库被打垮、恶意刷单,传统数据库架构完全扛不住。Redis凭借内存读写、原子操作、分布式锁三大特性,成为秒杀架构的核心。
1. 秒杀核心方案设计
- 库存预热:活动开始前,将商品库存从MySQL同步到Redis(String结构存储),提前加载热点数据
- 流量拦截:Redis层做限流、库存校验、用户限购校验,提前拦截无效请求,避免流量击穿到数据库
- 原子扣减:用Redis DECRBY原子命令扣减库存,单线程执行无锁竞争,从根源避免超卖
- 异步下单:扣减成功后,将请求写入MQ异步同步数据库,削峰填谷,保护数据库稳定性
2. 核心代码实现(Java)
/**
* 秒杀核心逻辑:Redis原子扣减库存
*/
public Result secKill(Long userId, Long goodsId) {
// 1. 构造规范Key,便于后续维护和排查
String stockKey = "seckill:stock:" + goodsId;
String userLimitKey = "seckill:user:" + goodsId + ":" + userId;
// 2. 判断用户是否已抢购(限购1件,防止恶意刷单)
Boolean hasBuy = redisTemplate.hasKey(userLimitKey);
if (Boolean.TRUE.equals(hasBuy)) {
return Result.fail("您已抢购过该商品,限购1件");
}
// 3. 原子扣减库存(核心:DECRBY保证原子性,杜绝超卖)
Long remainStock = redisTemplate.opsForValue().decrement(stockKey, 1);
// 4. 库存不足判断,及时回滚避免库存负数
if (remainStock < 0) {
redisTemplate.opsForValue().increment(stockKey, 1);
return Result.fail("商品已售罄");
}
// 5. 标记用户已抢购,设置过期时间(活动结束后自动清理)
redisTemplate.opsForValue().set(userLimitKey, "1", 24, TimeUnit.HOURS);
// 6. 异步发送MQ,同步数据库订单,避免同步写库阻塞
mqSender.sendSecKillMsg(userId, goodsId);
return Result.success("抢购成功,请尽快支付");
}
/**
* 活动前:库存预热到Redis,提前加载避免活动时卡顿
*/
public void initStockToRedis(Long goodsId, Integer stock) {
String stockKey = "seckill:stock:" + goodsId;
redisTemplate.opsForValue().set(stockKey, stock);
}
3. 秒杀避坑要点
- 严禁直接用数据库扣减库存,必须在Redis层完成库存校验与扣减,杜绝数据库雪崩
- 强制加入用户限购逻辑,结合用户ID/设备ID做限制,防范恶意刷单
- 库存扣减为负时,立即执行回滚操作,避免库存数据异常
- 配合分布式锁加固,防止极端并发下同一用户重复抢购
- 活动结束后及时清理Redis临时数据,释放内存空间
二、场景二:接口限流 —— 防刷防攻击,保护服务稳定性
接口被恶意刷请求、爬虫频繁调用,会导致服务资源耗尽、正常请求无法响应。利用Redis的计数器+过期时间,可快速实现多种限流策略,成本低、见效快,适配绝大多数业务场景。
1. 常用限流策略:固定窗口计数器(Redis实现)
核心逻辑:单位时间内,限制单个用户/IP的请求次数,超过阈值直接拦截,实现简单、性能高效,适用于绝大多数接口防刷、防攻击场景。
2. 核心代码实现(通用限流)
/**
* Redis接口限流:固定窗口计数器
* @param key 限流标识(用户ID/IP+接口路径,保证唯一性)
* @param limitCount 单位时间限制次数
* @param time 窗口时间(秒)
*/
public boolean allowRequest(String key, int limitCount, int time) {
String redisKey = "request:limit:" + key;
// 1. 原子自增计数,无并发安全问题
Long count = redisTemplate.opsForValue().increment(redisKey);
// 2. 首次请求设置过期时间,窗口关闭后自动重置计数
if (count == 1) {
redisTemplate.expire(redisKey, time, TimeUnit.SECONDS);
}
// 3. 判断是否超过阈值,返回是否放行
return count <= limitCount;
}
// 业务使用示例:限制单个用户1分钟内最多调用60次
@GetMapping("/api/user/info")
public Result getUserInfo(Long userId, HttpServletRequest request) {
String ip = IpUtils.getIpAddr(request);
// 拼接唯一限流Key,区分用户+接口
String limitKey = userId + "_" + request.getRequestURI();
// 执行限流校验
if (!redisLimitService.allowRequest(limitKey, 60, 60)) {
return Result.fail("请求过于频繁,请稍后再试");
}
return Result.success(userService.getUserInfo(userId));
}
3. 拓展限流方案
- IP限流:针对公共接口,用客户端IP做限流标识,拦截恶意IP批量请求
- 滑动窗口限流:用Redis Sorted Set实现,解决固定窗口临界值突刺问题,限流更精准
- 令牌桶限流:结合Redis List实现,支持突发流量控制,兼顾限流与业务体验
三、场景三:实时排行榜 —— 热度排序、积分榜单
游戏积分、主播热度、商品销量、帖子排行、用户等级等业务,需要实时排序、快速查询排名、支持分数动态更新。Redis的Sorted Set(ZSet)有序集合,自带分数排序属性,查询效率极高,完美适配实时排行榜场景。
1. ZSet核心原理
- 每个元素绑定一个score(分数),Redis根据分数自动完成升序/降序排列
- 支持分数累加、排名查询、范围榜单查询、元素删除等常用操作
- 时间复杂度低,百万级数据的排序、查询操作均可实现毫秒级响应
- 天然支持去重,同一用户多次更新仅保留最新分数,无需额外处理
2. 核心代码实现(积分排行榜)
/**
* 排行榜通用Key
*/
private static final String RANK_KEY = "rank:user:score";
/**
* 1. 更新用户积分(支持新增/分数累加)
*/
public void updateUserScore(Long userId, Integer score) {
redisTemplate.opsForZSet().incrementScore(RANK_KEY, userId.toString(), score);
}
/**
* 2. 查询用户排名(降序:0为第一名,返回值+1适配日常展示)
*/
public Long getUserRank(Long userId) {
Long rank = redisTemplate.opsForZSet().reverseRank(RANK_KEY, userId.toString());
// 未上榜返回-1,上榜则排名+1(0→1,1→2)
return rank == null ? -1 : rank + 1;
}
/**
* 3. 查询TopN排行榜(默认前10名,带排名+分数+用户ID)
*/
public List<Map<String, Object>> getTopRank(int topN) {
// reverseRangeWithScores:降序获取带分数的元素集合
Set<ZSetOperations.TypedTuple<String>> tuples = redisTemplate.opsForZSet()
.reverseRangeWithScores(RANK_KEY, 0, topN - 1);
if (CollUtil.isEmpty(tuples)) {
return new ArrayList<>();
}
List<Map<String, Object>> result = new ArrayList<>();
int rank = 1;
for (ZSetOperations.TypedTuple<String> tuple : tuples) {
Map<String, Object> map = new HashMap<>();
map.put("userId", Long.parseLong(tuple.getValue()));
map.put("score", tuple.getScore());
map.put("rank", rank++);
result.add(map);
}
return result;
}
/**
* 4. 清理过期榜单数据(日/周/月榜重置专用)
*/
public void clearRank() {
redisTemplate.delete(RANK_KEY);
}
3. 排行榜拓展场景
- 日榜/周榜/月榜:构造带时间维度的Key(如rank:20250620),定时任务重置榜单
- 商品销量榜:score设为销量值,用户下单后实时更新,展示热销商品
- 去重榜单:ZSet天然去重,同一用户多次操作仅保留最新分数,无需手动去重
四、三大场景核心选型总结
| 业务场景 | Redis数据类型 | 核心命令 | 核心价值 |
|---|---|---|---|
| 电商秒杀 | String(库存)+ String(限购标记) | DECRBY、INCR、SETNX | 原子扣减、防超卖、扛高并发 |
| 接口限流 | String(计数器) | INCR、EXPIRE | 防刷防攻击、保护服务 |
| 实时排行榜 | Sorted Set(ZSet) | ZINCRBY、ZREVRANK、ZREVRANGE | 实时排序、快速查排名、高性能 |
落地口诀:秒杀靠原子扣减防超卖,限流靠计数过期防刷量,排行靠ZSet排序做实时,Redis承接高并发,数据库只做持久化。
总结与下篇预告
秒杀、限流、排行榜是互联网业务的三大高频场景,Redis凭借自身高性能、原子操作、丰富数据类型的特性,用极简的代码实现了高并发、高性能的业务需求,大幅降低了架构复杂度。本篇代码均可直接复用,只需根据业务微调参数、Key规则即可上线使用。