秒杀抢购系统的设计
电商的一种非常常见的场景,在15年的时候有过分析12306的文章,现在的12306做的与秒杀业务有些相似,点击买票后会提示前面有多少人排队,然后等待系统顺序的处理。
秒杀业务的特点是,在1秒内会有突然很大的流量,而且很多是针对同一个商品的流量,而且与正常的没有参加秒杀的业务比,这些流量甚至有90%以上都是无效的(没有可能抢购成功)流量。根据这个特点,秒杀业务的设计关键点就是让这绝大部分的无效流量在系统尽量靠前的位置快速处理掉,不要进入系统太深,影响到系统,更不能在从数据库走一遍了。
而且秒杀业务,还有个特性,用户会在快到了秒杀时间的时候,一直的刷页面,或者从别的页面跳转到秒杀的页面,页面的请求流量也会增大。所以,前端页面的设计要尽量简单,最好就是静态页面的方式,直接放在负载均衡的nginx的缓存,靠近系统入口的地方。
这里先只考虑后端的设计,入手点主要是如何快速的处理掉,大部分的无效的流量。在去除大部分的无效流量后,因为进入系统的流量会峰值特别明显,需要队列来削峰,对这些进来的流量直接放入队列中,要快,最好放在堆内存中搞,而不是在redis中。还有一点,就是上面说的,进入系统的流量到达一定的量后,后面的流量要及时的阻断,直接返回一个友好的抢购失败的页面。
这样,就会有部分的流量进入系统,但是还有个问题,像12306一样,然我排队当然可以,但是要让我知道排在什么位置,前面还有多少人才轮到我,像12306每隔个几秒中就会更新下排队的情况,这就需要周期性的查询排队的位置,最快的当然是hash的方式更具用户id进行查询了,但是还要对进入系统的用户进行顺序排队,正好有个数据结构LinkedHashMap能够满足这两个需求。而且在Redis中的ZSet也可以满足此需求,ZSet中也是有两个数据结构,一个score(可以是用户进入的顺序seq)一个dic(hashmap)支持快速查询。
如果没有秒杀业务,正常的业务情况,虽然一般也都会有限流、流量削峰、等机制只不过场景与方式不一样。主要特殊的就是这排队机制秒杀特有,而且需要特殊的数据结构支持,个人理解,秒杀业务直接想起这LinkedHashMap或ZSet就解决了一大半。