跳至主要內容

03 秒杀系统的表设计

Java突击队大约 5 分钟

03 秒杀系统的表设计

前言

前面一篇文章主要介绍了一下秒杀系统的系统设计。

今天这篇文章,重点跟大家介绍一下秒杀系统的表设计。

1 分析需求

我们之前开发的苏三商城系统中,已经有用户、商品和订单相关的表和功能了。

而这些表正是秒杀系统所需要的。

由于之前商城系统中,已经有了用户登录接口,有验证码校验,而且当时是用的redis保存的验证码,和登录成功之后的用户信息。

但之前会从数据库中查询数据,在一般的业务场景下问题不大,但很显然在高并发的秒杀场景下,变得有点不太适用。

我们需要提供一个全新的用户登录接口,直接从redis中查询用户信息。

但还是使用susan_mall库下的sys_user表。

到时候我们在秒杀系统中,写一个job,将sys_user表的关键字段数据,定期同步到redis中。

我们还需要商品信息,而这个商品的价格和数量,跟普通的商品有区别。

为了更好的维护秒杀商品信息,我们需要在秒杀系统中,增加一张秒杀商品表,专门用来保存秒杀商品。

还需要创建一张表,记录用户秒杀了哪些商品。

用户通过秒杀接口秒杀商品成功之后,直接给自己发送一条MQ消息。

秒杀系统的消费者消费这条消息,然后写数据到用户商品秒杀表中,并且调用商城下单接口,或者发生MQ消息给商城系统下单,都可以。

如果是给商城系统发MQ消息,考虑到,MQ消息可能存在消息丢失的请求,我们最好创建一张消息发送表。

考虑到后面,可能存在未及时退款,或者用户取消订单的情况。

我们的库存设计成两个:

  1. 预扣库存
  2. 剩余库存

后面需要定期校验这两个值是否相等,需要修复问题数据。

2 表设计

按照上面的设计,秒杀系统跟商城系统是通过MQ通信的。

我们需要创建下面这几张表:

  1. 秒杀商品表
  2. 用户秒杀商品表
  3. MQ消息发送表

正常情况下的秒杀系统,还需要:用户表和订单表。

由于这两张表,我们之前在商城系统中已经创建过了,可以直接复用。

2.1 秒杀商品表

秒杀商品跟正常的商品差不多,但商品的价格和库存可能不一样的。

我们需要单独创建一张秒杀商品表。

秒杀商品就是基于现有商品,增加了一下额外的字段,表结构如下

CREATE TABLE `seckill_product` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `product_id` bigint NOT NULL  COMMENT '商品ID',
  `with_hold_quantity` int(4) NOT NULL COMMENT '预扣库存',
  `remain_quantity` int(4) NOT NULL COMMENT '实际剩余库存',
  `price` decimal(10,2) NOT NULL COMMENT '秒杀价格',
  `create_user_id` bigint NOT NULL COMMENT '创建人ID',
  `create_user_name` varchar(30) NOT NULL COMMENT '创建人名称',
  `create_time` datetime(3) NOT NULL COMMENT '创建日期',
  `update_user_id` bigint DEFAULT NULL COMMENT '修改人ID',
  `update_user_name` varchar(30)  DEFAULT NULL COMMENT '修改人名称',
  `update_time` datetime(3) DEFAULT NULL COMMENT '修改时间',
  `is_del` tinyint(1) DEFAULT '0' COMMENT '是否删除 1:已删除 0:未删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='秒杀商品表';

product_id是商品ID。

with_hold_quantity是预扣库存。

remain_quantity是实际剩余库存。

price是秒杀价格。

2.2 用户秒杀商品表

在用户秒杀商品成功之后,我们需要记录用户秒杀的商品是什么。

需要创建一张用户秒杀商品表。

该表的表结构如下:

CREATE TABLE `seckill_user_product` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `user_id` bigint NOT NULL  COMMENT '用户ID',
  `product_id` bigint NOT NULL  COMMENT '商品ID',
  `quantity` int(4) NOT NULL COMMENT '购买商品数量',
  `price` decimal(10,2) NOT NULL COMMENT '秒杀价格',
  `seckill_time` datetime(3) NOT NULL COMMENT '秒杀时间',
  `create_user_id` bigint NOT NULL COMMENT '创建人ID',
  `create_user_name` varchar(30) NOT NULL COMMENT '创建人名称',
  `create_time` datetime(3) NOT NULL COMMENT '创建日期',
  `update_user_id` bigint DEFAULT NULL COMMENT '修改人ID',
  `update_user_name` varchar(30)  DEFAULT NULL COMMENT '修改人名称',
  `update_time` datetime(3) DEFAULT NULL COMMENT '修改时间',
  `is_del` tinyint(1) DEFAULT '0' COMMENT '是否删除 1:已删除 0:未删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户秒杀商品表';

user_id是用户ID。

product_id是商品ID。

quantity是购买的商品数据。

price是秒杀价格。

seckill_time是秒杀时间,由于是MQ异步保存数据的,seckill_time跟create_time可能有点差异。

2.3 MQ消息发送表

为了保证用户秒杀成功,创建了用户秒杀商品表之后,给商城系统发生的MQ小的可靠性。

我们专门创建了一张MQ消息发送表。

这张表的结构如下:

CREATE TABLE `seckill_message_send` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `message_id` varchar(32) NOT NULL  COMMENT '消息ID',
  `user_id` bigint NOT NULL  COMMENT '用户ID',
  `product_id` bigint NOT NULL  COMMENT '商品ID',
  `quantity` int(4) NOT NULL COMMENT '购买商品数量',
  `price` decimal(10,2) NOT NULL COMMENT '秒杀价格',
  `read_status`  tinyint(1) NOT NULL COMMENT '阅读状态 0:未阅读 1:已阅读',
  `create_user_id` bigint NOT NULL COMMENT '创建人ID',
  `create_user_name` varchar(30) NOT NULL COMMENT '创建人名称',
  `create_time` datetime(3) NOT NULL COMMENT '创建日期',
  `update_user_id` bigint DEFAULT NULL COMMENT '修改人ID',
  `update_user_name` varchar(30)  DEFAULT NULL COMMENT '修改人名称',
  `update_time` datetime(3) DEFAULT NULL COMMENT '修改时间',
  `is_del` tinyint(1) DEFAULT '0' COMMENT '是否删除 1:已删除 0:未删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MQ消息发送表';

message_id是消息的ID,是一个全局唯一的字符串。

user_id是用户ID。

product_id是商品ID。

quantity是购买商品数量。

price是秒杀价格。

read_status是消息阅读状态。

秒杀系统的MQ消息生产者,在发送了MQ消息之后,消息阅读状态是未阅读。

商城系统消费完该条消息之后,会回调秒杀系统一个notify接口,通知该条消息的阅读状态变成已阅读。

这个做法跟调用支付宝支付接口,完成支付之后,支付宝会回调我们一个notify接口通知我们的支付结果类似。

该方案是分布式事务解决方案中的:尽最大努力通知方案。

当然还有很多很多细节,我们在后面的秒杀系统开发中,会给大家一一揭晓答案。