超卖问题浅析
问题难点
- 1 突发访问量
- 前端优化
- 秒杀倒计时,不能发送下单请求,下单键不可用
- 下单后,不能重复下单请求,下单键不可用
- JS程序设置用户请求最小间隔时间
- 使用前端缓存,相同页面直接从缓存刷新
- 后端优化
- 见后文
- 行业方案
- 使用队列组织 nginx 接收到的请求,业务服务器从队列中取,顺序处理
- 负载均衡
- 提升机器数量
- nginx 接入层筛选限流,再转发到服务器
- 前端优化
- 2 带宽限制
- 突发流量太大,需要从运营商临时购买
- 设置CDN缓存页面,分担服务器压力
- 3 大部分不会生成订单的请求
- 在 nginx 反向代理层,就进行筛选发送到服务器的请求
- 4 超卖问题
- 在同一时间内,数据库中没有及时更新库存量,多个用户抢到商品,但是只能有一个用户买到
- 行业方案
- MySQL
悲观锁:数据库层面设置的写锁,保证修改只能同时被一个session执行,在完成前其他都需要等待
- 优点:稳定
- 缺点:锁等待,资源消耗
- MySQL
乐观锁:程序设计时,增加的锁机制,比如增加版本号,在修改时先查询版本号是否被更改,没改动一次就更新版本号,此时只有一条SQL执行成功。
- 优点:比悲观锁并发量大
- 缺点:MySQL本身处理不了大量并发请求
- 队列:使用队列变成有序的请求处理
- 优点:稳定,可以处理大量并发请求
- 缺点:本身实时处理请求速率低,大量并发请求内存占用高
- Redis 分布式锁:SETNX
只能设置一次键值,返回1,再次设置会返回0。基于此,多个线程只有一个线程的
SETNX 返回 1。超时时间后,删除 SETNX 设置的key。
- 优点:只限制处理库存时,只允许单线程执行
- 缺点:线程级的限制,资源浪费
- Redis 乐观锁:Redis watch 机制,比如 watch
库存值得变化。当Redis事务开启后,库存值发生变化,就回滚当前事务。所以并发请求当有一个成功时,其他请求的事务都会回滚。
- 优点:没有限制线程的执行数量,只是打断冲突线程的执行。
- 缺点:在这些方法的比较中,缺点不明显,可以使用高性能的语言或者框架,进一步提升性能
- MySQL
悲观锁:数据库层面设置的写锁,保证修改只能同时被一个session执行,在完成前其他都需要等待
方案设计
- 静态资源处理,图片、js、css、页面等
- 使用CDN
- 加大带宽
- 业务请求处理
- Nginx 过滤大部分一定不会生成订单的请求
- Nginx-Lua + Redis 乐观锁解决超卖问题
- 服务器处理少量请求
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!