秒杀是典型的大量突发访问类应用,访问存在明显的峰值请求。而应对这类问题一般有三种性能优化思路:写入内存而非磁盘、异步处理而非同步、分布式处理而非单机。
假设流程如下:
-----------------
| 客户端发起请求 |
-----------------
|
-----------------
| Nginx做负载均衡 |
-----------------
|
-----------------
| PHP/Go |
-----------------
|
-----------------
| Redis |
-----------------
Redis List数据结构
Redis支持一种叫list的数据结构,其本质是一个链表,我们知道链表头尾的操作是非常快速的。因为秒杀的商品都是有数量限制的,只能是谁先抢到就给谁。而既然有先后那就可以看做是排队,先到先得。因此Redis的list数据结构就正好可以应用于此种场景。我们可以使用RPUSH将秒杀请求插入到list的尾部,当达到秒杀上限后则停止所有后续的插入并直接引导用户到一个活动的静态页面(注意一定要是一个静态页面,这个页面如果启用了CDN加速效果更好)。同时我们在后台可以同时启动多个工作进程,使用LPOP来读取秒杀成功者的数据并进行相关处理,一旦处理成功一个我们就执行INCR计数,当库存全部处理完毕就结束本次秒杀活动。
脚本攻击
使用脚本可以在短时间内发起多次请求,当大家都使用这样的脚本时就有可能把我们的系统压垮,比如12306的抢票软件。
解决这类问题其实可以归结为网络问题,现在很多交换机都有防止一个源IP发起过多请求的功能,像Nginx这样的Web服务器通过配置也可以做到软控制,控制每秒就让你请求多少次,超过次数的数据包直接丢掉。
如果单个交换机/Nginx撑不住了,我们还可以启用多个交换机/Nginx来服务。