1.全局ID生成器
1.1.订单表id说明:
- 1.在大数据量的情况下,订单会越来越多,上千亿万条,不适合采用自增长类型
- 2.在如下的用户抢购场景中,就会生成订单并保存到
tb_voucher_order
这张表中,而订单表如果使用数据库自增ID就存在一些问题:
- 1.id的规律性太明显
- 2.受单表数据量的限制:
- 每张表存储的数据量是有限的,那么单张表无法存储全部数据,就需要把数据存储到多张表中,但是如果存储到多张表时候,每个表的ID字段采用自增长类型,就会导致ID重复,但是订单ID是不可以重复的
1.2.什么是全局唯一ID:
- 1.全局ID生成器,是一种在分布式系统下用来
生成全局唯一ID的工具
- 2.全局唯一ID一般要满足下列特性:
1.3.全局生成ID的特点:
- 1.为了增加ID的安全性,我们可以不直接使用Redis自增的数值,而是拼接一些其它信息:
- 2.ID的组成部分(
共64bit
):- 符号位:1bit,永远为0
- 时间戳:
31bit
,以秒为单位,可以使用69年 - 序列号:32bit,秒内的计数器,支持每秒产生2^32个不同ID
1.4.全局唯一ID生成策略:
a.策略举例:
- UUID
Redis自增
- snowflake算法
- 数据库自增:单独一张表专门存储订单ID,然后订单表的ID都是来自于这个单独存储ID的表
b.Redis自增ID策略:
- 每天一个key,方便统计订单量
- ID构造是 时间戳 + 计数器
1.5.编码实现:
a.获取起始时间:
- 1.获取时间戳,需要一个起始时间,在这里我们先获取一个起始时间的格式:
b.
2.实现优惠券秒杀下单
1.1.优惠券介绍:
- 1.表关系如下:
tb_voucher
:优惠券的基本信息,优惠金额、使用规则等tb_seckill_voucher
:优惠券的库存、开始抢购时间,结束抢购时间。特价优惠券才需要填写这些信息
1.2.设置秒杀券:
a.说明:
- 1.通畅我们通过管理页面,发布一些秒杀券和普通券来进行引流,这里我们通过postman来实现秒杀券的发布,未实现前端管理页的开发:
b.编码实现:
- 1.controller层实现:
- 2.service层实现:
1.3.实现客户端的秒杀功能:
a.功能说明:
- 1.下单优惠券时需要判断两点:
- 秒杀是否开始或结束,如果尚未开始或已经结束则无法下单
- 库存是否充足,不足则无法下单
- 2.接口说明:
b.编码实现:
3.超卖问题:
3.1.高并发情况说明
- 1.对于秒杀功能,一般都是会发生并发的情况,无数的用户一起抢购优惠券,所以需要对高并发这种情况进行处理:
3.2.模拟高并发情况:
- 对于高并发的情况的模拟,我们一般是使用Jmeter工具:
3.3.超卖现象分析:
3.4.加锁方式解决超卖问题:
a.悲观锁
- 1.认为线程安全问题一定会发生,因此在操作数据之前先获取锁,确保线程串行执行。
- 2.例如Synchronized、Lock都属于悲观锁
b.乐观锁:
- 1.认为线程安全问题不一定会发生,因此不加锁,只是在更新数据时去判断有没有其它线程对数据做了修改。
- 如果没有修改则认为是安全的,自己才更新数据。
- 如果已经被其它线程修改说明发生了安全问题,此时可以重试或异常。
c.乐观锁实现方式:
c1.方法1:版本号法:
c2.CAS方案:在做更改动作的时候,判断条件和一开始查询出的库存值是否相同:
c3.编码实现乐观锁:
c4.乐观锁弊端:
- 1.比如当有100个线程对同一个数据更改的时候,当有一条更改成功后,另外的99条线程都会失败,导致了失败率大大提高。虽然没有超卖,但是呢,效率却降低了跟多
c5.库存乐观锁弊端的改善:
- 2.在乐观锁弊端问题中改善低效率有很多办法,在库存问题中,最佳的改善方法就是在更新库存的条件中,把更新的库存数量条件改为大于0
4.一人一单:
4.1.需求说明:
- 1.需求:修改秒杀业务,要求同一个优惠券,一个用户只能下一单
4.2.添加查询是否已经购买过的逻辑:
- 1.先添加查询购买与否的逻辑:
- 2.在多线程情况下,上述查询可能存在多个线程同时查询到未购买过的情况,然后就产生了多条记录插入到表中,因为知识查询的功能,未涉及到修改问题,所以需要添加悲观锁来解决
3.5.集群模式下的超卖问题:
a.说明:
- 1.通过加锁可以解决在
单机情况
下的一人一单安全问题,但是在集群模式下就不行了。我们将服务启动两份,端口分别为8081和8082
b.模拟集群:
c.修改nginx配置:
- 1.修改nginx的conf目录下的
nginx.conf
文件,配置反向代理和负载均衡:
- 2.现在,用户请求会在这两个节点上负载均衡,再次测试下是否存在线程安全问题