秒杀的概念
中秋节搞活动,商家上架了一款mbp,价格比平时便宜1000块,数量20台,设定在凌晨12点开始售卖
中秋节放假,某公司有100个员工,下班以后这100个用户玩手机的时候发现了这款mbp,皆欲购置于麾下,大家晚上熬夜到12点开始一起抢这20台
示例场景,单机多线程
100个并发访问数量不大,单机服务就能解决
实际场景,多机多线程
业务隔离
后端单独编写业务接口,卖家要参加秒杀这种营销活动需要单独报名,与其他业务隔离
前端将不涉及秒杀的静态数据缓存在页面,发送请求只更新涉及到的按钮和页面
系统隔离(runtime的隔离)
- 与其他业务所属系统分开部署
- 单独申请域名,使请求落到不同的集群中
数据隔离
秒杀所调用的数据大部分都是热数据,单独启用Redis集群或MySQL数据库来放热点数据
流量削峰
拉长下单窗口时间,秒杀请求前置图形验证码,答题验证等
链路热点维护
上游服务的热点数据提前下透到下游服务,提前异步订阅热点日志,维护热点数据
Java层
- 直接使用Servlet处理请求,不用mvc,绕过封装
- 直接输出流数据,resp.getOutputStream()
- 裁剪日志堆栈信息
- 减少序列化,即rpc/网络调用
高并发读单点瓶颈 LocalCache
- 将商品固有不变的属性推送的单点机器本地缓存起来一直到活动结束
- 动态数据被动拉取,间隔数秒去从缓存数据库拉取
脏数据读取问题,在写时保证最终一致性
本地缓存定时更新可能会出现脏数据,在更新数据库的时候去判断,保证最终一致性能
热点商品高并发读写影响数据库的其他商品
热点商品放到单独的热点库,单独部署
并发锁
每个节点的应用层做单机限流排队
多结点分布式锁,Redis/Zookeeper
超卖和卖不出去
超卖是因为高并发情况下库存的缓存数据没有及时更新,写时没有进行可用库存判断
缓存层
- 使用 Redis 6.0 之前的版本,6.0之前是单进程,可以保证数据在缓存层的一致性
数据层
- 通过事务来判断,即保证减后库存不能为负,否则就回滚
- 二是直接设置数据库字段类型为无符号整数,这样一旦库存为负就会在执行 SQL 时报错
- 三是使用判断语句
UPDATE item SET inventory = if(inventory < 1, 0, inventory - 1);
卖不出去是因为都哥用户下单之后一直未支付导致,一般是恶意的网络攻击
需要检测重复下单不支付的用户并取消其秒杀资格
高可用系统构建
架构阶段、编码阶段、测试阶段、发布阶段、运行阶段,结合devops