JCSprout项目:秒杀系统架构设计与优化实战

JCSprout项目:秒杀系统架构设计与优化实战

JCSprout 👨‍🎓 Java Core Sprout : basic, concurrent, algorithm JCSprout 项目地址: https://gitcode.com/gh_mirrors/jc/JCSprout

秒杀系统核心挑战

秒杀系统是电商领域最具挑战性的场景之一,它面临的核心问题可以概括为"三高":

  • 高并发:短时间内大量用户同时抢购
  • 高可用:系统必须稳定可靠,不能崩溃
  • 高性能:响应速度要快,用户体验要好

基础架构设计

系统分层架构

典型的秒杀系统采用分层架构设计:

  1. Web层:处理HTTP请求,使用Spring MVC框架
  2. Service层:业务逻辑处理,通过Dubbo实现分布式服务
  3. 数据层:MySQL数据库持久化存储

基础数据库设计

系统使用两张核心表:

-- 库存表
CREATE TABLE `stock` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名称',
  `count` int(11) NOT NULL COMMENT '库存',
  `sale` int(11) NOT NULL COMMENT '已售',
  `version` int(11) NOT NULL COMMENT '乐观锁,版本号',
  PRIMARY KEY (`id`)
);

-- 订单表
CREATE TABLE `stock_order` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `sid` int(11) NOT NULL COMMENT '库存ID',
  `name` varchar(30) NOT NULL DEFAULT '' COMMENT '商品名称',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
);

问题发现:超卖现象

在最初的实现中,系统出现了典型的超卖问题。通过JMeter进行300并发测试时:

  • 库存正确扣减为0
  • 但订单生成了124条记录,远超库存量

原因分析

这是由于在高并发环境下,多个线程同时读取库存,都认为还有库存可卖,导致超卖。

优化方案一:乐观锁机制

实现原理

通过版本号机制实现乐观锁:

  1. 读取数据时获取版本号
  2. 更新时检查版本号是否变化
  3. 如果版本号一致则更新,否则重试或失败

核心代码

@Override
public int createOptimisticOrder(int sid) throws Exception {
    // 校验库存
    Stock stock = checkStock(sid);
    
    // 乐观锁更新库存
    saleStockOptimistic(stock);
    
    // 创建订单
    return createOrder(stock);
}

private void saleStockOptimistic(Stock stock) {
    int count = stockService.updateStockByOptimistic(stock);
    if (count == 0) {
        throw new RuntimeException("并发更新库存失败");
    }
}

XML实现

<update id="updateByOptimistic" parameterType="com.crossoverJie.seconds.kill.pojo.Stock">
    update stock
    <set>
        sale = sale + 1,
        version = version + 1,
    </set>
    WHERE id = #{id} AND version = #{version}
</update>

效果验证

优化后测试结果显示:

  • 库存和订单数量严格一致
  • 大量并发请求会快速失败
  • 系统稳定性显著提升

优化方案二:水平扩展

为提高系统吞吐量,实施水平扩展策略:

  1. Web层扩展:使用Nginx进行负载均衡
  2. Service层扩展:部署多个服务实例
  3. 自动化部署:编写Shell脚本实现CI/CD

自动化部署脚本示例

#!/bin/bash
# 构建web消费者
appname="consumer"

# 停止现有进程
PID=$(ps -ef | grep $appname | grep -v grep | awk '{print $2}')
for var in ${PID[@]}; do
    kill -9 $var
done

# 代码更新与构建
cd ..
git pull
mvn -Dmaven.test.skip=true clean package

# 部署到多个Tomcat实例
cp target/*.war /path/to/tomcat1/webapps/
cp target/*.war /path/to/tomcat2/webapps/

# 启动服务
/path/to/tomcat1/bin/startup.sh
/path/to/tomcat2/bin/startup.sh

优化方案三:分布式限流

限流必要性

即使库存只有10个,也可能有数百万请求涌入,其中99%都是无效请求,会压垮数据库。

Redis限流实现

使用Redis+Lua脚本实现分布式限流:

public boolean limit() {
    Object connection = getConnection();
    Object result = limitRequest(connection);
    return (FAIL_CODE != (Long) result);
}

private Object limitRequest(Object connection) {
    String key = String.valueOf(System.currentTimeMillis() / 1000);
    if (connection instanceof Jedis) {
        return ((Jedis)connection).eval(script, 
            Collections.singletonList(key), 
            Collections.singletonList(String.valueOf(limit)));
    } else {
        return ((JedisCluster) connection).eval(script, 
            Collections.singletonList(key), 
            Collections.singletonList(String.valueOf(limit)));
    }
}

效果验证

  • 数据库连接数显著下降
  • 系统资源利用率提高
  • 无效请求被快速拒绝

优化方案四:Redis缓存

缓存策略

  • 库存信息缓存在Redis中
  • 查询操作走缓存
  • 更新时同步缓存和数据库

核心代码

@Override
public int createOptimisticOrderUseRedis(int sid) throws Exception {
    // 从Redis校验库存
    Stock stock = checkStockByRedis(sid);
    
    // 乐观锁更新库存和Redis
    saleStockOptimisticByRedis(stock);
    
    // 创建订单
    return createOrder(stock);
}

private Stock checkStockByRedis(int sid) throws Exception {
    Integer count = Integer.parseInt(redisTemplate.opsForValue()
        .get(RedisKeysConstant.STOCK_COUNT + sid));
    Integer sale = Integer.parseInt(redisTemplate.opsForValue()
        .get(RedisKeysConstant.STOCK_SALE + sid));
    
    if (count.equals(sale)) {
        throw new RuntimeException("库存不足");
    }
    
    // 构造Stock对象返回
    // ...
}

优化方案五:异步处理

架构设计

引入Kafka消息队列实现异步处理:

  1. 请求通过校验后发送订单信息到Kafka
  2. 立即返回响应给用户
  3. 消费者服务处理消息,完成订单创建

优势

  • 请求响应时间大幅缩短
  • 系统吞吐量显著提升
  • 实现业务解耦

终极架构设计

经过上述优化后,系统最终架构包含以下组件:

  1. Nginx负载均衡层
  2. Web应用集群
  3. 分布式限流组件
  4. Redis缓存集群
  5. Kafka消息队列
  6. 订单消费者服务
  7. 数据库集群

性能优化总结

  1. 拦截无效请求:尽早拦截,减少系统压力
  2. 减少数据库访问:利用缓存,降低DB负载
  3. 快速失败:Fail Fast原则,保护系统资源
  4. 异步处理:提高系统吞吐量
  5. 水平扩展:增强系统处理能力

这套优化方案不仅适用于秒杀场景,对于其他高并发系统也有很好的参考价值。开发者可以根据实际业务需求和系统规模,选择合适的优化策略组合。

JCSprout 👨‍🎓 Java Core Sprout : basic, concurrent, algorithm JCSprout 项目地址: https://gitcode.com/gh_mirrors/jc/JCSprout

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宫俊潇Gresham

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值