## 秒杀小结

---


**秒杀特点：**

短时间内，大量用户涌入，集中读和写有限的库存。

**解决方案：**

层层拦截，将请求尽量拦截在系统上游，避免将锁冲落到数据库上。

* 	第一层：客户端优化

产品层面，用户点击“查询”或者“购票”后，按钮置灰，禁止用户重复提交请求；
JS层面，限制用户在x秒之内只能提交一次请求，比如微信摇一摇抢红包。
基本可以拦截80%的请求。

*	第二层：站点层面的请求拦截（nginx层，写流控模块）
	
怎么防止程序员写for循环调用，有去重依据么？IP？cookie-id？…想复杂了，这类业务都需要登录，用uid即可。在站点层面，对uid进行请求计数和去重，甚至不需要统一存储计数，直接站点层内存存储（这样计数会不准，但最简单，比如guava本地缓存）。一个uid，5秒只准透过1个请求，这样又能拦住99%的for循环请求。
对于5s内的无效请求，统一返回错误提示或错误页面。

这个方式拦住了写for循环发HTTP请求的程序员，有些高端程序员（黑客）控制了10w个肉鸡，手里有10w个uid，同时发请求（先不考虑实名制的问题，小米抢手机不需要实名制），这下怎么办，站点层按照uid限流拦不住了。

*	第三层：服务层拦截

方案一：写请求放到队列中，每次只透有限的写请求到数据层，如果成功了再放下一批，直到库存不够，队列里的写请求全部返回“已售完”。

方案二：或采用漏斗机制，只放一倍的流量进来，多余的返回“已售完”，把写压力转换成读压力。
读请求，用cache，redis单机可以抗10W QPS,用异步线程定时更新缓存里的库存值。

还有提示“模糊化”，比如火车余票查询，票剩了58张，还是26张，你真的关注么，其实我们只关心有票和无票。


*	第四层：数据库层

浏览器拦截了80%，站点层拦截了99.9%并做了页面缓存，服务层又做了写请求队列与数据缓存，每次透到数据库层的请求都是可控的。
db基本就没什么压力了，通过自身锁机制来控制，避免出现超卖。


**总结：**

1. 尽量将请求拦截在系统上游（越上游越好）；
2. 读多写少的多使用缓存（缓存抗读压力）；

**参考资料：**

http://www.infoq.com/cn/articles/flash-deal-architecture-optimization



































































