秒杀系统特点

  • 读多写少:缓存
  • 高并发:缓存,限流(令牌桶,漏斗 ),负载均衡(集群),异步,队列
  • 资源冲突:
    原子操作(不会被其他线程中断)(分布式集群的 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 自旋重试,知道更新成功为止

读多写少:乐观锁
高并发:悲观锁,效率高