项目介绍: 小型秒杀项目。采用乐观锁防止超卖+令牌桶算法限流+md5签名+单用户频率访问限制。
项目地址: SmallSecKill
主要参考: 如何基于springboot优雅设计一个秒杀系统乐观锁解决超卖、Redis缓存、令牌桶桶限流等方案,已完结!
前期准备
在数据库创建两张表
-
库存表
stock
DROP TA`seckill`BLE IF EXISTS `stock`; 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`) )engine=InnoDB DEFAULT CHARSET=utf8;
-
订单表
order
DROP TABLE IF EXISTS `stock_order`; 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 '商品名称',`stock_order` `create_name` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY(`id`) )ENGINE=INNODB DEFAULT CHARSET=utf8;
安装依赖
-
mysql、mybatis
等<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version> <optional>true</optional> </dependency> <!--数据源--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.21</version> </dependency>
创建 controller、dao、entity、service包,编写相关文件
- 具体参考视频即可。
安装jmeter工具
- 具体参考视频。
- 运行命令:
jmeter -n -t [jmx file] -l [results file] -e -o [Path to web report folder]
超卖问题及解决方法
-
出现原因:并发的线程数量远远高于实际的库存数量,在不加锁的情况下,会出现超卖问题。
-
秒杀代码:
@Service @Transactional public class OrderServiceImpl implements OrderService{ @Autowired private StockDAO stockDAO; @Autowired private OrderDAO orderDAO; @Override public int seckill(Integer id) { //根据商品id校验库存 Stock stock = stockDAO.checkStock(id); if(stock.getSale().equals(stock.getCount())){ throw new RuntimeException("库存不足"); }else{ //扣除库存 stock.setSale(stock.getSale()+1); stockDAO.updateSale(stock); //创建订单 Order order = new Order(); order.setSid(stock.getId()).setName(stock.getName()).setCreateDate(new Date()); orderDAO.createOrder(order); return order.getId(