高性能架构

performance
上周参加了一个在阿里云上实施的高性能架构交流,本文结合我们产品的实践,从前端、应用层、数据层等维度,分别总结一下高性能架构的一些常规做法

总述

性能包含多种维度的考量,最直观的是2个指标:单次请求时间和吞吐量

单次请求时间直接影响到用户的体验,即网页打开快不快。吞吐量反映的是系统的并发能力

要提升系统的性能,需要系统每个环节的协同作用

前端高性能

前端的架构优化,往往能够直观地缩短单次请求时间。除此以外,对提升系统吞吐量也有价值,比如说如果设计得好,前端会给后台带来更小的压力,也就间接地提高了系统的并发响应能力

CDN

CDN是前端性能的神器。同样的架构,是否使用了CDN在性能上有很大差别

要使用CDN,首先要求应用的架构是动静分离,如果是后端渲染页面,那动态页面基本上每次都要回源,CDN就失去意义了

另外如果把静态资源和服务接口部署在不同的域名下,在某些移动端浏览器上可能会有跨域问题。所以可以考虑把html和接口部署在同一域名下,用路径过滤掉动态请求的缓存,直接回源

压缩文件,减少网络传输量

开发态的css和javascript直接部署到生产环境,会增加http请求的传输量,所以这部分资源需要压缩。可以用gulp或grunt来简化

合并请求,减少网络请求数

同理,把javascript和css合并,减少请求数,也会提升前端性能

优化html本身的写法

这里面有很多细节的问题,就不展开讲了。比如说,应该把script写在尾部,避免脚本阻塞页面加载和渲染,还有提升DOM操作的效率等

其他更牛逼的技术

比如facebook的BigPipe等。现在业内也有一些公司在用,我只听过别人分享案例,能理解原理但没有实践过

应用层高性能

相比起数据层,在应用层提升性能相对简单一点。最核心的一个原则就是需要保持server是无状态的,因为这样就很容易水平扩展,利用集群提高系统的负载能力

集群 + 负载均衡

这是最常规的做法了。基本上只要server是无状态的,水平伸缩建立集群就不会太困难

为了实现无状态服务,通常需要把各种本地依赖都外置,比如session,本地存储的文件等等,都需要从服务本身剥离出去

建立集群虽然容易,不过困难的是工程化,能够实现快速按需伸缩,以及提高集群的可用性等,这是更复杂的主题

分布式 + 异步消息队列

当系统成长到一定规模,一般会将其拆分成更小的分布式系统。然后通过异步队列的方式,将业务逻辑拆分成小粒度的单元,使每个请求变成多个轻量级的请求,再通过异步消息串联起来,削峰,减少单机上的资源竞争

实际上,采用分布式架构,不只是出于性能的考量。即使在系统还比较小的时候,也鼓励适当的拆分。因为分布式系统除了性能方面的考虑之外,还有别的好处

比如说每个小组可以维护更小的系统,对开发比较有好处;从运维角度,各子系统可以单独启停,边缘业务的故障不会影响到主流程;也有利于服务重用,比如底层基础的账户子系统如果独立出来,上层业务模块就不需要重复写账户相关的代码

缓存

无需多言,缓存也是系统性能的神器。缓存数据本质上在内存里,速度和并发能力都远远超过硬盘IO。一个高并发的系统,没有缓存是不可想象的

在架构上,使用缓存是不言而喻的,主要需要考虑的是如何提高缓存的命中率,以及提升缓存的可用性

数据层高性能

数据层面,尤其是关系数据库,要做到高性能是比较麻烦的

因为数据库本质上是单机系统,天然就是带状态的(数据就是它的状态),所以难以像应用服务器一样方便地水平伸缩

另一方面,当单数据库饱和以后,拆分数据库也很麻烦,因为这种拆分对于应用层往往不是透明的,需要应用层做额外的处理。比如数据库的路由,跨库join,跨库事务等,都比单库复杂很多

SSD

有句话叫“最厉害的优化就是买SSD”

索引

对频繁读取,以及经常join的大表,建立了合适的索引之后,性能的提升非常酸爽

读写分离

主库负责读写,从库只读,这也是常规的手段了

分库分表

当单库或者单表达到容量上限时,就需要考虑拆分,拆分时有垂直拆分和水平拆分

垂直拆分是把表根据业务关联性,拆分到不同的库里。主要解决的问题是表与表的IO竞争

水平拆分是把单表拆成多表,不解决表与表的IO竞争,但是可以解决单表容量过大的问题

举一个例子,比如一个库里原先有2张表,用户和订单。把用户表放到db1,把订单表放到db2,这就是垂直拆分。把订单表一分为二,分为已完成订单表,和未完成订单表,这就是水平拆分

显然,分库分表是需要应用层感知的

NOSQL

可以考虑将非结构化数据放到nosql中,比如mongodb等。缺点是牺牲了传统关系数据库的一些特性,主要是事务和join能力。但是通常nosql数据库的查询效率和容量都是超过关系数据库的