6.1 秒杀业务分析
6.1.1 需求分析
所谓“秒杀”,就是商家发布⼀些超低价格的商品,买家在同⼀时间⽹上抢购的⼀种销售⽅式。通俗讲就是商家为促销等⽬的组织的⽹上限时抢购活动。由于商品价格低廉,往往⼀上架就被抢购⼀空,有时只⽤⼀秒钟,所以成为秒杀
秒杀商品通常有两种限制:库存限制、时间限制。
需求:
(1)录⼊秒杀商品数据,主要包括:商品标题、原价、秒杀价、商品图⽚、介绍、秒杀时段等信息
(2)秒杀频道⾸⻚列出秒杀商品(进⾏中的)点击秒杀商品图⽚跳转到秒杀商品详细⻚。
(3)商品详细⻚显示秒杀商品信息,点击⽴即抢购实现秒杀下单,下单时扣减库存。当库存为0或不在活动
期范围内时⽆法秒杀。
(4)秒杀下单成功,直接跳转到⽀付⻚⾯,⽀付成功,跳转到成功⻚,填写收货地址、电话、收件⼈等信
息,完成订单。
(5)当⽤户秒杀下单5分钟内未⽀付,取消预订单,调⽤微信⽀付的关闭订单接⼝,恢复库存。
6.1.2 表结构说明
秒杀商品信息表
CREATE TABLE `seckill_goods_` (
`id_` bigint(20) NOT NULL AUTO_INCREMENT,
`spu_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 '秒杀价格',
`seller_id_` varchar(100) DEFAULT NULL COMMENT '商家ID',
`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) unsigned DEFAULT NULL COMMENT '剩余库存数',
`introduction_` varchar(2000) DEFAULT NULL COMMENT '描述',
PRIMARY KEY (`id_`)
) ENGINE=InnoDB AUTO_INCREMENT=11058 DEFAULT CHARSET=utf8;
秒杀订单表
CREATE TABLE `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 '⽤户',
`seller_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;
6.1.3 秒杀⽅案设计
秒杀技术实现核⼼思想是运⽤缓存减少数据库瞬间的访问压⼒!读取商品详细信息时运⽤缓存,当⽤户点击抢购时减少缓存中的库存数量,当库存数为0时或活动期结束时,同步到数据库。 产⽣的秒杀预订单也不会⽴刻写到数据库中,⽽是先写到缓存,当⽤户付款成功后再写⼊数据库。
当然,上⾯实现的思路只是⼀种最简单的⽅式,并未考虑其中⼀些问题,例如并发状况容易产⽣的问题。我们看看下⾯这张思路更严谨的图:
6.2 秒杀商品压⼊缓存
我们这⾥秒杀商品列表和秒杀商品详情都是从Redis中取出来的,所以我们⾸先要将符合参与秒杀的商品定时查询出来,并将数据存⼊到Redis缓存中。
数据存储类型我们可以选择Hash类型。
秒杀分⻚列表这⾥可以通过获取redisTemplate.boundHashOps(key).values()获取结果数据。
秒杀商品详情,可以通过redisTemplate.boundHashOps(key).get(key)获取详情。
6.2.1 秒杀服务⼯程
我们将商品数据压⼊到Reids缓存,可以在秒杀⼯程的服务⼯程中完成,可以按照如下步骤实现:
1.查询活动没结束的所有秒杀商品
1)计算秒杀时间段
2)状态必须为审核通过 status=1
3)商品库存个数>0
4)活动已开始 start_time > 开始时间段
4)活动没有结束 endTime >= 开始时间段 + 2⼩时
5)在Redis中没有该商品的缓存
6)执⾏查询获取对应的结果集
2.将活动没有结束的秒杀商品⼊库
我们⾸先搭建⼀个秒杀服务⼯程,然后按照上⾯步骤实现。
⼯程结构:
(1)秒杀⽗⼯程
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>legou-parent</artifactId>
<groupId>com.lxs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>legou-seckill</artifactId>
<packaging>pom</packaging>
<modules>
<module>legou-seckill-interface</module>
<module>legou-seckill-service</module>
</modules>
</project>
(2) 秒杀接⼝⼯程
legou-seckill/legou-seckill-interface/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>legou-seckill</artifactId>
<groupId>com.lxs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>legou-seckill-interface</artifactId>
<dependencies>
<dependency>
<groupId>com.lxs</groupId>
<artifactId>legou-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.core.Starter</mainClass>
<layout>ZIP</layout>
<classifier>exec</classifier>
<includeSystemScope>true</includeSystemScope>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
实体类
legou-seckill/legou-seckill-interface/src/main/java/com/lxs/legou/seckill/po/SeckillGoods.java
package com.lxs.legou.seckill.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.lxs.legou.core.po.BaseEntity;
import lombok.Data;
import java.util.Date;
@Data
@TableName("seckill_goods_")
public class SeckillGoods extends BaseEntity {
@TableField("spu_id_")
private Long supId;//spu ID
@TableField("sku_id_")
private Long skuId;//sku ID
@TableField("name_")
private String name;//标题
@TableField("small_pic_")
private String smallPic;//商品图⽚
@TableField("price_")
private String price;//原价格
@TableField("cost_price_")
private String costPrice;//秒杀价格
@TableField("create_time_")
private Date createTime;//添加⽇期
@TableField("check_time_")
private Date checkTime;//审核⽇期
@TableField("status_")
private String status;//审核状态,0未审核,1审核通过,2审核不通过
@TableField("start_time_")
private Date startTime;//开始时间
@TableField("end_time_")
private Date endTime;//结束时间
@TableField("num_")
private Integer num;//秒杀商品数
@TableField("stock_count_")
private Integer stockCount;//剩余库存数
@TableField("introduction_")
private String introduction;//描述
}
legou-seckill/legou-seckill-interface/src/main/java/com/lxs/legou/seckill/po/SeckillOrder.java
package com.lxs.legou.seckill.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.lxs.legou.core.po.BaseEntity;
import lombok.Data;
import java.util.Date;
@Data
@TableName("seckill_order_")
public class SeckillOrder extends BaseEntity {
@TableField("seckill_id_")
private Long seckillId;//秒杀商品ID
@TableField("money_")
private String money;//⽀付⾦额
@TableField("user_id_")
private String userId;//⽤户
@TableField("seller_id_")
private String sellerId;
@TableField("create_time_")
private Date createTime;//创建时间
@TableField("pay_time_")
private Date payTime;//⽀付时间
@TableField("status_")
private String status;//状态,0未⽀付,1已⽀付
@TableField("receiver_address_")
private String receiverAddress;//收货⼈地址
@TableField("receiver_mobile_")
private String receiverMobile;//收货⼈电话
@TableField("receiver_")
private String receiver;//收货⼈
@TableField("transaction_id_")
private String transactionId;//交易流⽔
}
(3)秒杀微服务⼯程
legou-seckill/legou-seckill-service/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>legou-seckill</artifactId>
<groupId>com.lxs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>legou-seckill-service</artifactId>
<dependencies>
<!-- redis 使⽤-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>