天猫双11成了全民购物狂欢节。自2009年开始,天猫双11已经经历了11个年头,11年的时间,双11的成交额从5000万增长到了2019年的2600亿,整整翻了4000多倍。今年的双十一,1分30秒左右,交易额突破100亿,平均每秒1.05亿的交易额,1小时的时候,交易额突破1000亿,TPS达到了54万。而QPS和PV等指标远远大于这个值,这是真正的高并发系统。那阿里是怎么打造这么一个强大的系统?今天我们就聊聊高并发系统。
一、高并发系统特点
高并发系统的特点非常明显,就是请求量非常大,请求量可以用QPS、TPS、PV等指标来表示。当单机的QPS高于10000的时候,就算的上一个真正的高并发系统。像淘宝、百度、12306这都属于高并发的系统。
高并发系统都会有一个流量的高峰期,像天猫的双11、12306每年春节的抢购火车票以及各大电商网站举办的秒杀活动。这些特定的时期,网站的流量会急剧增加,是平常的十倍百倍。所以,这就要求系统能自动扩容伸缩,当流量高峰期到来的时候,可以快速扩容机器来应对大量的请求,到流量高峰期过去,可以缩减机器或者用这些机器去做别的业务,减少成本。
二、高并发系统瓶颈
一个应用系统的正常运行需要计算机的各种资源,如CPU、内存、磁盘、数据库连接池等。当这些资源任何一项达到瓶颈,系统就达到了它的最大承受范围。
- 2.1 CPU使用率过高
CPU使用率过高是在高并发的系统最常见的一个现象。当有大量的请求的时候,CPU使用率瞬间飙到90%以上。因为此时服务器需要处理大量的请求,会创建大量的tcp连接。应用系统中如果有大量的计算型逻辑和不正确的编码,也会增加CPU的负担,导致CPU使用率过高。
- 2.2 内存使用率过高
高并发系统中,会创建大量的线程,而每一个线程都会创建很多对象,每一个对象都会占用内存。如果对象的生命周期很长,不能及时被GC,那就会占用大量的内存空间,当内存耗尽,系统就会发生OOM,最终终止服务。
- 2.3 数据库压力大,连接池不够用。
高并发系统中,对于后端数据库是一个很大的考验。因为应用服务器可以很容易的进行扩展,但是,数据库很难扩展。当大量的请求发送到数据库端,数据库的每一次查询耗时会明显增大,导致连接不能释放,最终会耗尽数据库的连接数。如果业务中存在慢sql,大量的查询会导致数据库死锁,最终停止服务。
三、高并发系统技术点
当系统遇到高并发的瓶颈的时候,我们需要排查的瓶颈的原因,然后针对这些原因进行排查优化。作为一个高并发的系统,以下技术必须具备。
- 3.1 分布式集群
由于硬件设备的限制,单机的吞吐能力有限,不管采用什么手段优化,都不可能突破单机硬件的瓶颈。所以,高并发系统我们需要分布式集群部署,通过集群的能力来解决高并发的问题。在集群搭建上我们应该分地区搭建。假如业务是国际性,在全球范围内都用业务,所以我们可以考虑在美东、美西、日本、新加坡等地分别建立服务器集群,这样通过DNS让域名就近访问,可以避免网络延迟,提高用户端内容的响应速度。
- 3.2 缓存
在高并发的业务当中,数据库的压力非常大。磁盘IO和网络IO的消耗非常大,所以,我们需要采取缓存技术。
通过缓存技术将数据放到离CPU最近的地方,减少与数据库交互的次数,减少传输时间时间,从而提高系统的响应时间。
在使用缓存的过程当中,我们可能会碰到缓存穿透、缓存击穿、缓存雪崩等问题,我们针对这三个问题做一个简单的分析。
- 缓存穿透
在设计缓存的时候,如果查询不到数据不写入缓存,会导致这个不存在的数据一直查询数据库,失去了缓存的意义。
解决方案:如果查询不到数据,仍然写入缓存,但是这个key的过期时间很短。
- 缓存击穿
当热点key值过期的时候,正好这个key有大量的请求发送过来,因为缓存已经失效了,请求就会大量的落到DB层,最后导致DB被压垮。
解决方案:使用互斥锁(mutex key),当查询到key值不存在的时候,使用可以缓存框架提供的某些带成功返回值的操作去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。在redis中可以使用setnx方法。
- 缓存雪崩
缓存的key设置了相同的过期时间,导致某一时刻所有缓存全部失效,请求全部转发到DB,导致DB雪崩。
解决方案:设置缓存失效的时间的时候,随机一个特定范围内的数字,缓存的时间为原有的时间加上这个随机的数字。
- 3.3、CDN
高并发系统CDN并不可少。像淘宝、京东等电商网站,他们的前端页面全部都是通过CDN进行缓存。用户根据地理位置访问距离自己最近的CDN服务器,这样减轻了应用服务器的压力,提升了用户的访问速度。
通过配置CDN,不仅可以提升静态页面的的加载速度,同时可以增加内容冗余,转移故障、节省带宽和防DDOS攻击,保障服务安全。关于CDN的工作原理,我看过一篇文章,说的很清楚,在这里引用一下:
CDN其实更像是放在应用服务器与用户之间的一层缓存。所以如果DNS的时候,返回给客户端的是CDN机器的IP而不是应用的IP,那么自然就走到了CDN机器上。
为了实现上述目的,我们会为该域名配置一个 CNAME(大家注意上面提到的CNAME与A记录的优先级),那么这个CNAME是最终如何解析到对应的CDN机器呢?其实流程与DNS解析是一样的。当发现一个域名设置了CNAME时,DNS解析器会继续解析这个CNAME别名(其实就是另一个域名)。对这个CNAME解析的时候会用到全局负载DNS解析,它会根据访问者的地理位置信息返回对应的IP(CDN机器的IP)。因此客户端实际上得到的是距离它最近的CDN机器的IP地址。
如果说用户访问CDN,但是CDN上没有对应内容会怎么办?此时CDN机器其实会根据自身专用的DNS解析服务,根据域名得到源站的IP,然后向源站发送请求获取数据,并把这些数据缓存到本地,方便后续使用;同时返回本次结果,完成本次请求的访问。
需要说一下的是,CDN其实也是分层的。距离用户最近的称之为边缘节点。而CDN的中心服务器集群被称为二级缓存。在上面就是应用部署的源站。一般边缘节点没数据就去找二级缓存,二级缓存没数据就去找源站(被称为回源)。
- 3.4、分库分表
这是数据库层面的解决方案。高并发系统对于数据库的读写次数特别频繁,除了在应用层面使用缓存技术减轻DB压力,分库分表是必须的。通过分库分表将海量数据分散,减少单表的数据量,从而提升查询效率。
分库分表一般分为垂直拆分和水平拆分。
水平拆分:将一个表的数据拆分到一个或多个库的不同表中,每个表的数据结构是相同的。通过减轻单表的数据,可以极大的提升查询效率。同时,通过多个数据实例,也可以增加数据库可以承受的最大并发。
垂直拆分:将一个有很多字段的宽表拆分成小表,每个表的字段不一样。可以将热点字段放到一张表中,不常用的字段放到另一张表中。然后通过缓存将不常用的字段进行缓存,提高查询效率。
五、高并发系统自我保护
一个良好高并发系统必须要有系统级别的自我保护功能。当请求数超过服务器的最大极限的时候,不是让用户无限的等待,而是需要系统启动自我保护机制,拒绝接受请求。
通常我们采用限流的机制来保护系统。当请求达到限制的速率的时候,系统拒接请求。常用的限流算法有令牌桶算法、漏桶算法和计数器算法。
令牌桶: 令牌桶是指按照固定周期向桶中添加令牌,令牌的数目可以根据实际QPS进行调整,请求时需要从桶中获取令牌,如果没有令牌,可以选择等待,或者放弃。
漏桶:一个装满水的桶,每秒向外漏一滴, 如何当前请求能接到,那就可以正常请求,否则,就只能等待下一滴水。
两种方法看起来很像,但还是有区别的。漏桶的速率是固定的,而令牌桶则是只要桶中有令牌,请求就可以拿。所以这在一定程度允许并发。假设桶中有10个令牌,同时有10个请求发起,那这10个请求都可以拿到令牌。
令牌桶是实际应用比较广泛的限流算法。很多限流算法都是基于令牌桶的思想实现的。
计数器算法:主要用来限制总并发数,对全局总请求数或者单位时间内的总请求数进行限流。
最后
高并发系统是对一个企业的技术最大的考验。高并发系统不仅需要良好的架构和技术选型,还需要实际高并发的业务场景来全链路进行测试。阿里正是在通过每一年的双十一活动一次次的验证系统、优化架构和技术,最终打造了世界上吞吐量最高的系统,每秒54万的交易次数。一次正常的交易过程需要很复杂的业务处理,牵涉到很多系统的相互调度,获取商品、获取用户信息、获取地址、提交订单、支付、库存处理、日志等等业务,这是一个很长很复杂的处理流程,能做到每秒54万的交易次数,平均一次交易的时间不到 0.00185,将机器的性能发挥到了极致。天猫双十一不仅是一场购物狂欢节,更像是一场技术狂欢节。