# second-kill-demo **Repository Path**: xiangzuodalaodecaigou/second-kill-demo ## Basic Information - **Project Name**: second-kill-demo - **Description**: 秒杀demo,使用SpringBoot、SpringCloud、Redis、RabbitMQ - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-01-24 - **Last Updated**: 2022-07-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # second-kill-demo #### 介绍 秒杀demo,使用SpringBoot、SpringCloud、Redis、RabbitMQ #### MySQL库表设计 秒杀商品信息表 ``` CREATE TABLE `tb_seckill_goods` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `sup_id` bigint(20) DEFAULT NULL COMMENT 'spu ID', `sku_id` bigint(20) DEFAULT NULL COMMENT 'sku ID', `name` varchar(100) DEFAULT NULL COMMENT '标题', `small_pic` varchar(150) DEFAULT NULL COMMENT '商品图片', `price` decimal(10,2) DEFAULT NULL COMMENT '原价格', `cost_price` decimal(10,2) DEFAULT NULL COMMENT '秒杀价格', `create_time` datetime DEFAULT NULL COMMENT '添加日期', `check_time` datetime DEFAULT NULL COMMENT '审核日期', `status` char(1) DEFAULT NULL COMMENT '审核状态,0未审核,1审核通过,2审核不通过', `start_time` datetime DEFAULT NULL COMMENT '开始时间', `end_time` datetime DEFAULT NULL COMMENT '结束时间', `num` int(11) DEFAULT NULL COMMENT '秒杀商品数', `stock_count` int(11) DEFAULT NULL COMMENT '剩余库存数', `introduction` varchar(2000) DEFAULT NULL COMMENT '描述', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; ``` 秒杀订单表 ``` CREATE TABLE `tb_seckill_order` ( `id` bigint(20) NOT NULL COMMENT '主键', `seckill_id` bigint(20) DEFAULT NULL COMMENT '秒杀商品ID', `money` decimal(10,2) DEFAULT NULL COMMENT '支付金额', `user_id` varchar(50) DEFAULT NULL COMMENT '用户', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `pay_time` datetime DEFAULT NULL COMMENT '支付时间', `status` char(1) DEFAULT NULL COMMENT '状态,0未支付,1已支付', `receiver_address` varchar(200) DEFAULT NULL COMMENT '收货人地址', `receiver_mobile` varchar(20) DEFAULT NULL COMMENT '收货人电话', `receiver` varchar(20) DEFAULT NULL COMMENT '收货人', `transaction_id` varchar(30) DEFAULT NULL COMMENT '交易流水', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ``` #### Redis缓存设计 hash键 "SeckillGoods_"+startTime 保存秒杀商品信息,2h超时 ``` redisTemplate.boundHashOps("SeckillGoods_"+startTime).put(seckillGood.getId(),seckillGood) redisTemplate.expireAt("SeckillGoods_"+startTime,DateUtil.addDateHour(dateMenu, 2)); ``` hash键 SeckillOrder 保存用户的秒杀订单 ```redisTemplate.boundHashOps("SeckillOrder").put(username,seckillOrder);``` list键 SeckillOrderQueue 保存秒杀订单信息的队列 ``` 下单时向list左边push,另一个线程异步从list右边pop进行下单 redisTemplate.boundListOps("SeckillOrderQueue").leftPush(seckillStatus); redisTemplate.boundListOps("SeckillOrderQueue").rightPop(); ``` hash键 UserQueueStatus 保存用户抢单状态 ``` 异步下单前保存用户抢单状态 redisTemplate.boundHashOps("UserQueueStatus").put(username,seckillStatus); 抢单排队成功,更新订单状态 seckillStatus.setStatus(2); seckillStatus.setOrderId(seckillOrder.getId()); redisTemplate.boundHashOps("UserQueueStatus").put(username,seckillStatus); ``` hash键 UserQueueCount 自增,用户只能抢一次,防止用户重复抢单 ``` redisTemplate.boundHashOps("UserQueueCount").increment(username, 1); if(userQueueCount>1){ //StatusCode.REPERROR = 100:表示有重复抢单 throw new RuntimeException(String.valueOf(StatusCode.REPERROR)); } ``` list键 SeckillGoodsCountList_+秒杀商品id 队列防止超卖 ``` 每个商品对应一个大小为秒杀数量的队列 Long[] ids = pushIds(seckillGood.getStockCount(), seckillGood.getId()); redisTemplate.boundListOps("SeckillGoodsCountList_"+seckillGood.getId()).leftPushAll(ids); 从队列中获取一个商品 redisTemplate.boundListOps("SeckillGoodsCountList_" + seckillStatus.getGoodsId()).rightPop(); ``` hash键 SeckillGoodsCount 控制数量 ``` //商品导入缓存时,创建自增计数器 redisTemplate.boundHashOps("SeckillGoodsCount").increment(seckillGood.getId(),seckillGood.getStockCount()); //异步下单成功后,商品库存-1 redisTemplate.boundHashOps("SeckillGoodsCount").increment(id, -1); //没有库存时,同步到MySQL,删除秒杀商品 seckillGoodsMapper.updateByPrimaryKeySelective(goods); redisTemplate.boundHashOps("SeckillGoods_" + time).delete(id); //如果有库存,则将数据重置到Redis中 redisTemplate.boundHashOps("SeckillGoods_" + time).put(id,goods); ```