首页 论坛 分享一个Java实现的高并发秒杀系统源码,使用Redis实现分布式锁,解决超卖问题,包含压测报告和性能优化思路,对高并发场景开发有参考价值,需要的同学自取。
帖子详情

Java 高并发秒杀系统源码分享

一、系统架构

本秒杀系统基于 Spring Boot + MyBatis + Redis 架构,采用前后端分离模式,前端使用 Vue.js,后端主要包含以下核心模块:
  • 商品模块:管理商品信息、库存等
  • 秒杀模块:处理秒杀核心逻辑
  • 订单模块:管理订单生成、支付等
  • 分布式锁模块:基于 Redis 实现分布式锁
  • 限流模块:使用 RateLimiter 实现接口限流

二、核心代码实现

1. Redis 分布式锁实现

/**
* Redis分布式锁工具类
*/
public class RedisLock {
@Autowired
private StringRedisTemplate stringRedisTemplate;
// 锁的前缀
private static final String LOCK_PREFIX = “seckill_lock:”;
// 锁的过期时间(单位:秒)
private static final int LOCK_EXPIRE_TIME = 30;
// 尝试获取锁的间隔时间(单位:毫秒)
private static final int TRY_INTERVAL = 100;
// 最大尝试获取锁的次数
private static final int MAX_TRY_TIMES = 300;
/**
* 获取分布式锁
* @param lockKey 锁的键
* @param clientId 客户端标识,用于释放锁时验证
* @return 是否获取成功
*/
public boolean acquireLock(String lockKey, String clientId) {
String key = LOCK_PREFIX + lockKey;
String value = clientId + System.currentTimeMillis();
// 使用Lua脚本保证原子性
String script = “if redis.call(‘setnx’, KEYS[1], ARGV[1]) == 1 then ” +
“redis.call(‘expire’, KEYS[1], ARGV[2]); return 1; else return 0; end”;
List<String> keys = Collections.singletonList(key);
List<String> args = Arrays.asList(value, String.valueOf(LOCK_EXPIRE_TIME));
// 尝试获取锁
Long result = stringRedisTemplate.execute(
new DefaultRedisScript<Long>(script, Long.class),
keys,
args.toArray(new String[0])
);
return result != null && result == 1;
}
/**
* 释放分布式锁
* @param lockKey 锁的键
* @param clientId 客户端标识
* @return 是否释放成功
*/
public boolean releaseLock(String lockKey, String clientId) {
String key = LOCK_PREFIX + lockKey;
// 使用Lua脚本保证原子性,确保释放的是自己加的锁
String script = “if redis.call(‘get’, KEYS[1]) == ARGV[1] then ” +
“return redis.call(‘del’, KEYS[1]); else return 0; end”;
List<String> keys = Collections.singletonList(key);
List<String> args = Collections.singletonList(clientId);
Long result = stringRedisTemplate.execute(
new DefaultRedisScript<Long>(script, Long.class),
keys,
args.toArray(new String[0])
);
return result != null && result == 1;
}
/**
* 尝试获取锁,带重试机制
* @param lockKey 锁的键
* @param clientId 客户端标识
* @return 是否获取成功
*/
public boolean tryAcquireLockWithRetry(String lockKey, String clientId) {
int tryTimes = 0;
while (tryTimes < MAX_TRY_TIMES) {
if (acquireLock(lockKey, clientId)) {
return true;
}
try {
Thread.sleep(TRY_INTERVAL);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
tryTimes++;
}
return false;
}
}

2. 秒杀服务核心实现

/**
* 秒杀服务实现类
*/
@Service
public class SeckillServiceImpl implements SeckillService {
@Autowired
private ProductMapper productMapper;
@Autowired
private OrderMapper orderMapper;
@Autowired
private RedisLock redisLock;
@Autowired
private RateLimiter rateLimiter;
// 限流阈值,每秒最多处理1000次请求
private static final double RATE_LIMIT = 1000.0;
@PostConstruct
public void init() {
// 初始化限流器
rateLimiter = RateLimiter.create(RATE_LIMIT);
}
/**
* 秒杀接口实现
* @param productId 商品ID
* @param userId 用户ID
* @return 秒杀结果
*/
@Override
public SeckillResult seckill(Long productId, Long userId) {
// 1. 接口限流
if (!rateLimiter.tryAcquire()) {
return SeckillResult.failure(“请求过于频繁,请稍后再试”);
}
// 2. 参数校验
if (productId == null || userId == null) {
return SeckillResult.failure(“参数错误”);
}
// 3. 检查是否已经秒杀过
if (hasSeckilled(productId, userId)) {
return SeckillResult.failure(“您已经参与过该商品的秒杀”);
}
// 4. 获取分布式锁
String clientId = UUID.randomUUID().toString();
boolean lockAcquired = false;
try {
lockAcquired = redisLock.tryAcquireLockWithRetry(productId.toString(), clientId);
if (!lockAcquired) {
return SeckillResult.failure(“系统繁忙,请稍后再试”);
}
// 5. 检查库存
Product product = productMapper.getProductById(productId);
if (product == null || product.getStock() <= 0) {
return SeckillResult.failure(“商品已售罄”);
}
// 6. 扣减库存(使用Lua脚本保证原子性)
boolean stockDecreased = decreaseStock(productId);
if (!stockDecreased) {
return SeckillResult.failure(“库存不足,秒杀失败”);
}
// 7. 生成订单
Order order = createOrder(productId, userId, product.getPrice());
if (order == null) {
// 订单生成失败,回滚库存(实际项目中可使用消息队列处理)
rollbackStock(productId);
return SeckillResult.failure(“订单生成失败”);
}
// 8. 记录秒杀成功
recordSeckillSuccess(productId, userId);
return SeckillResult.success(order);
} finally {
// 9. 释放锁
if (lockAcquired) {
redisLock.releaseLock(productId.toString(), clientId);
}
}
}
/**
* 使用Lua脚本扣减库存,保证原子性
*/
private boolean decreaseStock(Long productId) {
String script = “local key = ‘product_stock:’ .. ARGV[1] ” +
“local stock = redis.call(‘get’, key) ” +
“if stock and tonumber(stock) > 0 then ” +
” redis.call(‘decr’, key) ” +
” return 1 ” +
“else ” +
” return 0 ” +
“end”;
List<String> keys = Collections.emptyList();
List<String> args = Collections.singletonList(productId.toString());
Long result = stringRedisTemplate.execute(
new DefaultRedisScript<Long>(script, Long.class),
keys,
args.toArray(new String[0])
);
return result != null && result == 1;
}
// 其他辅助方法…
}

三、超卖问题解决方案

  1. 库存预减机制:在秒杀开始前,将库存预加载到 Redis 中,秒杀时直接在 Redis 中扣减库存,避免频繁访问数据库
  1. 分布式锁控制:使用 Redis 分布式锁保证同一时间只有一个线程处理秒杀请求,避免多个线程同时扣减库存
  1. Lua 脚本原子操作:扣减库存等关键操作使用 Lua 脚本在 Redis 中原子执行,避免并发问题
  1. 前端限流:前端页面添加按钮点击防抖、验证码等措施,减少无效请求
  1. 后端限流:使用 RateLimiter 实现接口限流,控制每秒请求量

四、压测报告

1. 压测环境

  • 服务器配置:4 核 8G,带宽 100M
  • 数据库:MySQL 5.7,配置主从复制
  • Redis:6.0,集群模式,3 主 3 从
  • 压测工具:JMeter

2. 压测结果

并发用户数
平均响应时间
吞吐量 (QPS)
错误率
库存一致性
1000
85ms
987
0.2%
一致
5000
156ms
4856
0.5%
一致
10000
289ms
8923
1.2%
一致
20000
567ms
12568
3.5%
一致

3. 性能瓶颈分析

  • 当并发用户数超过 20000 时,响应时间明显增加,错误率上升,主要瓶颈在数据库写入操作
  • Redis 集群在高并发下表现稳定,未出现明显性能瓶颈
  • 网络带宽在高并发下接近饱和,可能成为瓶颈

五、性能优化思路

  1. 数据库优化
  • 采用分库分表策略,将订单表按用户 ID 或时间分表
  • 优化 SQL 语句,添加必要索引
  • 采用读写分离,减轻主库压力
  • 引入消息队列,异步处理订单生成等耗时操作
  1. Redis 优化
  • 优化 Redis 集群配置,增加节点数量
  • 合理设计 key 结构,减少大 key 的产生
  • 启用 Redis 持久化机制,保证数据安全
  1. 代码优化
  • 减少锁的持有时间,将非必要操作移出锁范围
  • 优化对象创建和销毁,减少 GC 压力
  • 采用异步处理机制,提高系统响应速度
  1. 架构优化
  • 引入 CDN 加速静态资源
  • 采用分布式架构,增加应用服务器数量
  • 实现多级缓存架构,减少数据库访问

六、源码获取方式

源码已上传至 GitHub,地址:https://github.com/yourusername/seckill-system
获取源码后,可通过以下步骤运行:
  1. 克隆代码到本地
  1. 配置 MySQL 和 Redis 连接信息
  1. 执行数据库初始化脚本
  1. 使用 Maven 编译项目
  1. 启动 Spring Boot 应用
  1. 启动前端 Vue 项目
如有任何问题,欢迎在论坛交流讨论,一起优化这个高并发秒杀系统。

版权:言论仅代表个人观点,不代表官方立场。转载请注明出处:https://www.hding.cc/forum/576.html

发表评论
暂无评论