秒杀系统业务流程如下:
由图可以发现,整个系统其实是针对库存做的系统。用户成功秒杀商品,对于我们系统的操作就是:1.减库存。2.记录用户的购买明细。下面看看我们用户对库存的业务分析:
记录用户的秒杀成功信息,我们需要记录:1.谁购买成功了。2.购买成功的时间/有效期。3.付款/发货信息。这些数据组成了用户的秒杀成功信息,也就是用户的购买行为。
为什么我们的系统需要事务?看如下这些故障:1.若是用户成功秒杀商品我们记录了其购买明细却没有减库存。导致商品的超卖。2.减了库存却没有记录用户的购买明细。导致商品的少卖。对于上述两个故障,若是没有事务的支持,损失最大的无疑是我们的用户和商家。在MySQL中,它内置的事务机制,可以准确的帮我们完成减库存和记录用户购买明细的过程。
MySQL实现秒杀的难点分析:当用户A秒杀id为10的商品时,此时MySQL需要进行的操作是:1.开启事务。2.更新商品的库存信息。3.添加用户的购买明细,包括用户秒杀的商品id以及唯一标识用户身份的信息如电话号码等。4.提交事务。若此时有另一个用户B也在秒杀这件id为10的商品,他就需要等待,等待到用户A成功秒杀到这件商品然后MySQL成功的提交了事务他才能拿到这个id为10的商品的锁从而进行秒杀,而同一时间是不可能只有用户B在等待,肯定是有很多很多的用户都在等待拿到这个行级锁。秒杀的难点就在这里,如何高效的处理这些竞争?如何高效的完成事务?在后面第4个模块如何进行高并发的优化为大家讲解。
我们这个系统需要完成秒杀的哪些功能?先来看看天猫的一个秒杀库存系统:
大家看了是不是觉得很复杂?当然不用担心,我们只是实现秒杀的一些功能:1.秒杀接口的暴露。2.执行秒杀的操作。3.相关查询,比如说列表查询,详情页查询。我们实现这三个功能即可。接下来进行具体的编码工作,首先是Dao层的编码。
数据库设计主要在两张表,秒杀库存表和秒杀成功明细表,编码如下。
--数据库初始化脚步
--创建数据库
CREATE DATABASE seckill
--使用数据库
use seckill;
--创建秒杀库存表
CREATE TABLE seckill(
`seckill_id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品库存id',
`name` varchar(120) NOT NULL COMMENT ' 商品名称 ',
`number` int NOT NULL COMMENT ' 库存数量 ',
`start_time` timestamp NOT NULL COMMENT ' 秒杀开启时间 ',
`end_time` timestamp NOT NULL COMMENT ' 秒杀结束时间 ',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ' 创建时间 ',
PRIMARY KEY (seckill_id),
key idx_start_time(start_time),
key idx_end_time(end_time),
key idx_create_time(create_time)
)ENGINE = InnoDB AUTO_INCREMENT = 1000 DEFAULT CHARSET=utf8 COMMENT="秒杀库存表";
--初始化数据
insert into seckill(name,number,start_time,end_time)
values
('100元秒杀iPhone6',100,'2020-03-28 00:00:00','2020-03-29 00:00:00'),
('100元秒杀iPhone7',200,'2020-03-28 00:00:00','2020-03-29 00:00:00'),
('100元秒杀iPhone8',300,'2020-03-28 00:00:00','2020-03-29 00:00:00'),
('100元秒杀iPhoneX',400,'2020-03-28 00:00:00','2020-03-29 00:00:00'),
('100元秒杀iPhoneXS',500,'2020-03-28 00:00:00','2020-03-29 00:00:00');
--秒杀成功明细表
create table success_killed(
`seckill_id` bigint NOT NULL COMMENT '秒杀商品ID',
`user_phone` bigint NOT NULL COMMENT '用户手机号',
`state` tinyint NOT NULL DEFAULT -1 COMMENT '状态标志:-1:无效 0:成功 1:已付款',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ' 创建时间 ',
PRIMARY KEY(seckill_id,user_phone),/*联合主键*/
KEY idx_create_time(create_time)
)ENGINE = InnoDB DEFAULT CHARSET = utf8 COMMENT='秒杀成功明细表';
--连接数据库控制台
mysql -uroot -p77777777
--为什么手写DDL
--记录每次的上线DDL
--上线V1.1
ALTER TABLE seckill;
DROP INDEX idx_start_time;
--上线V1.2
insert into seckill(name,number,start_time,end_time)
values
('100元秒杀ViVO',100,'2020-03-31 00:00:00','2020-04-01 00:00:00'),
('100元秒杀ViVO',100,'2020-04-01 00:00:00','2020-04-02 00:00:00'),
('100元秒杀OPPO',200,'2020-04-02 00:00:00','2020-04-03 00:00:00');