一、背景
2014年4月9日凌晨,小米网的抢购系统做了最后的检查与演练。几个小时后,小米网今年开年来最重要的一次大型活动“米粉节”就要开始了。这次米粉节活动,是小米电商的成人礼,是一次重要的考试。小米网从网站前端、后台系统、仓储物流、售后等各个环节,都将接受一次全面的压力测试。
10点整,一波流量高峰即将到来,几百万用户将准点挤入小米网的服务器。
而首先迎接压力冲击的,就是挡在最前面的抢购系统。这个抢购系统是重新开发、刚刚上线不久的,这是它第一次接受这样严峻的考验。
系统能不能顶住压力?能不能顺畅正确地执行业务逻辑?这些问题不到抢购高峰那一刻,谁都不能百分百确定。
9点50分,流量已经爬升得很高了;10点整,抢购系统自动开启,购物车中已经顺利加入了抢购商品。
一两分钟后,热门的抢购商品已经售罄自动停止抢购。抢购系统抗住了压力。
二、第一版抢购系统基本原理
在PHP服务器上,通过一个文件来表示商品是否售罄。如果文件存在即表示已经售罄。
PHP程序接收用户抢购请求后,查看用户是否预约以及是否抢购过,然后检查售罄标志文件是否存在。对预约用户,如果未售罄并且用户未抢购成功过,即返回抢购成功的结果,并记录一条日志。
日志通过异步的方式传输到中心控制节点,完成记数等操作。
最后,抢购成功用户的列表异步导入商场系统,抢购成功的用户在接下来的几个小时内下单即可。
这样,流量高峰完全被抢购系统挡住,商城系统不需要面对高流量。
在这个分布式系统的设计中,对持久化数据的处理是影响性能的重要因素。
三、选用Redis服务器理由:
1、首先需要保存的数据是典型的Key/Value对形式,每个UID对应一个字符串数据。
传统数据库的复杂功能用不上,用KV库正合适。
2、Redis的数据是in-memory的,可以极大提高查询效率。
3、Redis具有足够用的主从复制机制,以及灵活设定的持久化操作配置。
四、第二版抢购系统基本原理
使用Go语言开发了部分模块,积累了一定的经验。
让Go程序常驻内存运行,各种配置以及状态信息都可以保存在内存中,减少I/O操作开销。对于商品数量信息,可以在进程内进行操作。
不同商品可以分别保存到不同的服务器的Go进程中,以此来分散压力,提升处理速度。
系统服务端主要分为两层架构:即HTTP服务层和业务处理层。
HTTP服务层用于维持用户的访问请求,业务处理层则用于进行具体的逻辑判断。两层之间的数据交互通过消息队列来实现。
HTTP服务层主要功能如下:
1、进行基本的URL正确性校验;
2、对恶意访问的用户进行过滤,拦截黄牛;
3、提供用户验证码;
4、将正常访问用户数据放入相应商品队列中;
5、等待业务处理层返回的处理结果。
业务处理层主要功能如下:
1、接收商品队列中的数据;
2、对用户请求进行处理;
3、将请求结果放入相应的返回队列中。
用户的抢购请求通过消息队列,依次进入业务处理层的Go进程里,然后顺序地处理请求,将抢购结果返回给前面的HTTP服务层。
商品剩余数量等信息,根据商品编号分别保存在业务层特定的服务器进程中。
我们选择保证商品数据的一致性,放弃了数据的分区容忍性。
原文链接: