本篇文章我们学习架构演进的初始阶段。我们来看一下,互联网系统是如何从小网站逐步变大的,我们过一遍架构演进的历程,让大家对系统的演化心中有个谱。但是,这里面首先说明一点,并不是所有的网站都是像我下面所说的这样演进的,我说的只是个大概,不是统一的表达,就像我们前面所说的架构设计,没有统一的模式。
初级阶段
我们从网站的初始阶段开始说起。大型的网站都是从小网站发展起来的。假如我们现在是一个创业公司,我们要做一个项目,我们设计了一个架构,然后就上手开发了一个web程序,只有一台物理机器,我们把应用程序部署到这台服务器上。然后还需要好多的文件,比如有用户上传的文件,需要用到一个磁盘目录或者说是一个文件系统,不过都是在这台服务器上。然后应用程序还需要数据库。
这就是我们最开始的一个网站,很简单的一个模型,因为最开始这个项目使用的人特别少,数据量也不是很大,我们用一台服务器足够了。应用程序、数据库和文件的所有的资源在一台机器上,通常我们服务器的操作系统用linux。
服务器拆分
然后,随着网站业务的进一步发展,一台服务器无法满足需求。越来越多的用户访问了,导致我们的性能越来越差;越来越多的数据,导致我们的磁盘空间越来越不够。
那我们怎么优化呢?做服务器拆分。目前阶段来说,公司开发项目大部分都是这么做的,我们把文件拿出去,把数据库拿出去,服务器上面只放着我们的应用程序。文件单独放到一个服务器上,数据库单独放到一个服务器上,我们分别用三台机器去部署。
因为原来只有一台机器,现在有三台,就把一台服务器的压力分散到了三台服务器之上。这样的话,我们的系统,又能扛一段时间。并且这样做,还有一个好处,我们可以按需分配资源,比如说应用服务器需要处理大量的业务逻辑,需要的CPU资源比较多,因此我们可以给它更快更强大的CPU;数据库服务器需要更快的写入速度和查询速度,我们可以给它更快的硬盘;还有文件服务器,文件服务器需要有更快的上传下载的速度以及更大的存储空间,我们就可以扩大这边的带宽。
缓存
然后再往后走,项目保持这个样子运行一段时间之后,再一次面临挑战。比如说数据库压力太大了,数据越来越多,用户请求一个接口的时候响应越来越慢,导致用户的体验受到影响,这个时候该怎么做呢?
我们可以考虑加缓存。网站访问数据遵循二八定律,也就是说80%的业务操作集中在20%的数据上。比如说,在一个电商系统中,商品的详情页承担了大量的用户请求;淘宝买家浏览的商品集中在少部分交易数比较多、好评比较多的商品;上百度搜索的关键词,也是集中在少部分热门的词汇上。既然是业务集中访问的一小部分数据,那么如果把这一小部分数据直接缓存到内存中,就可以更快的响应用户,也可以减少数据库的压力,提高整个网站的响应速度,改善数据库的写入性能。
加缓存有两种情况啊,一种是在应用内部加缓存,就是本地缓存,它受到应用服务器内存的限制。还有一种缓存,把它存到分布式缓存服务器上,就是单独的缓存服务器,我们就可以部署大内存的服务器作为专门的缓存服务器,可以做到在理论上不受内存的限制。使用缓存后,数据库的压力就得到了有效的缓解。
集群
但是现在还有一个问题,就是单一的应用服务器能够处理的请求连接还是有限的,在网站访问的高峰期。应用服务器就成为了整个网站的一个瓶颈。
那么这个时候我们需要怎么去做呢?集群是解决高并发大流量的常用手段,也就是相当于拆分。
当一台服务器的处理能力、存储空间不足时,不要企图去换更大的服务器。一般的单机服务器的配置相对来说还算可以,如果说性能扛不住,不会说再加点CPU,再加点内存,再加点硬盘,不会这么做。一般是再加一台机器,并且现在加一台机器也不贵。就算退一万步讲,对一个大型网站而言,不管多么强大的服务器都满足不了网站持续增长的业务需求。假设我们就一台服务器,性能足够强大,那么这台服务器在双11秒杀的时候能否扛得住全国人民的请求压力呢?答案是扛不住的,因为那个时候网络带宽就限制死了,一个双11的请求能把一个中型城市的网络全部击垮。
那么这种情况下,最恰当的方法就是增加一台服务器,分担原有服务器的访问和存储的压力。以此类推,只要能通过增加一台服务器的方式改变负载的压力,就可以同样的方式增加多台服务器不断的优化系统的性能,从而实现系统的可伸缩性,并且在它的前面加上负载均衡。
请求先到负载均衡,再通过负载均衡去分发。通过负载均衡调度器,可将来自用户的访问请求分散到应用服务器集群中的任何一台服务器。如果有更多的用户就在集群中加入更多的应用服务器,减轻单台应用服务器的压力。
数据库读写分离
随着时间的积累,用户的数据量越来越多,虽然前面使用了缓存,绝大部分数据读操作都可以不通过数据库就能完成。但是,仍有一部读操作,比如说缓存没有命中。同时,所有的写操作,都需要去写我们的数据库,慢慢的,数据库的负载压力就会过高,成为了网站的瓶颈。数据库就有了读压力,有了写压力。
那么我们怎么去解决这个压力呢?目前我们大部分主流的数据库,都提供主从热备的功能。通过配置两台数据库的主从关系,可以将一台数据库的数据更新同步到另外一台服务器。利用这一功能,我们可以往一个数据库里写,那么这个数据就会同步到另一个从的数据库。读的时候,从那个从库去读就可以了,其实相当于将请求在数据库上又做了一次负载均衡,也就说写只写到主库里,读可以从缓存中读,也可以去从库中去读,这样的话就把数据库的压力又分散了。
我们可以抽象出一个数据访问模块,或者说有一些成熟的框架比如MyCat、ShardingJDBC。
本篇文章,我们先讲解到这里,架构演进到读写分离后面的内容,我们后面的文章继续讲解。