秒杀系统特点
- 读多写少:缓存
- 高并发:缓存,限流(令牌桶,漏斗 ),负载均衡(集群),异步,队列
- 资源冲突:
原子操作(不会被其他线程中断)(分布式集群的 jvm 锁不可以解决集群环境)
异步
原子操作:共用
- 数据库锁:乐观锁,悲观锁
- 分布式锁:redis,zk
- 其他原子操作:redis desc
集群:数据一致性,tomcat 集群后连的同一套数据库
基本架构
带宽
应用层(常见产品:浏览器/APP)
浏览器缓存,本地缓存,按钮控制,图形验证码
(为了限制不必要的流量)
网络层(网络路由)
CDN(超大并发秒杀很有必要)
负载层(nginx)
nginx,负载均衡,动静分离,反响代理,限流
服务层(Java 应用)
动态页面静态化(用作缓存),应用缓存,分布式缓存,异步,队列,限流(代码中),原子操作保障(分布式锁,redis 集群)
数据库层(mysql)
原子操作保障(乐观锁,悲观锁)
解决方式
计算秒杀的人群和规模,确定一个最终的技术选型以及服务器容量。
用户终端
- 缓存(静态流量:CSS)
- 图形验证码(垃圾流量:防止机器人刷单)
- 按钮(多余流量)
CDN
部署静态资源
nginx
- 动静分离,静态资源可以做缓存
- 限流(依赖漏斗)
new Thread(new Request()).start;
countDownLatch.countDown();
限流算法
令牌桶:按照固定的速度放令牌,填满则丢弃令牌,拿到令牌的人可以进入系统
漏斗:按照固定的速度漏水
conf:limit_req_zone,可以针对远程 ip 地址进行限流
容量设置为 8,可以成功 9 个,因为第一个不阻塞,直接透过去了。
- 负载均衡:进入下一个
分布式限流
令牌桶:大小一般根据商品数量来限制的,可以略为大于秒杀数量):进入我们的 tomcat 服务器
tomcat 集群
tomcat
如果没有分布式限流,那么有
- 限流:java 的令牌桶限流算法
- 队列:只有 10 个,其余抛出异常
- 队列 MQ:可以在 tomcat 之前加一层 MQ,然后 MQ 通知数据库一个一个扣。
数据库
乐观锁,悲观锁直接扣库存
或者 tomcat redis 直接扣
乐观锁
线程 A
select version,counts from goods where id=#id#:0,100
update goods set count=count-1,version=version+1 where id=#id# and version=#version#
commit
线程 B
select version,counts from goods where id=#id#:0,100
update goods set count=count-1,version=version+1 where id=#id# and version=#version#
commit
悲观锁
select * from goods id=#id# for update
高并发下悲观锁效率更高,因为乐观锁一直失败,一直 for 自旋重试,知道更新成功为止
读多写少:乐观锁
高并发:悲观锁,效率高