本节主要讨论几乎所有大型web应用程序的核心因素:services, redundancy, partitions, and handling failure。
用户可以方便的将图片upload到Web服务器,并且也可以从Web服务器方便的下载。核心的指标是:upload image的能力和查询一个图片能力。该系统的要求:
在考虑可伸缩的系统设计时,将功能解耦,并将系统的每个部分看作具有明确定义的接口的自己的服务。在实践中,以这种方式设计的系统被称为具有面向服务的体系结构(SOA)。
在该图片系统中,可以将图片上传和下载拆分成2个服务。并且将read/write分成2个资源,防止共享一个带宽造成的网络拥塞。appache web server 最多支持500个并发。拆分还带来的好处是
Redundancy可以避免单点故障的问题。无论是存储图片的服务器还是服务程序都要做Redundancy。
如果将数据存储在一台Server上,会造成性能瓶颈。要考虑垂直或水平扩展。
如下图:存储图片的时候可以利用一致性Hash算法,去选择图片应该存储到哪个服务器,图片的名字可以用自增一。
本小结介绍比较难的一部分,扩展对数据访问的速度。简单的Web应用程序如下图。
随着用户数量的增加,主要的问题是: scaling access to the app server and to the database。在分布式系统设计中,Web Server一定是互相不share的架构(无状态),这样可以水平扩展。
如果允许用户随机的访问TB的数据量,如下图:
主要的问题是加载TB的数据到内存是非常花费时间的。磁盘的访问速度要远远小于内存。还好有些方案可以解决这个问题,用caches, proxies, indexes and load balancers。
缓存利用了:最近请求的数据可能会被再次请求。它可以用于计算的每一层:hardware, operating systems, web browsers, web applications等。在API层加入一层cache,如下图
在分布式中,是多个request node,在每个request node上都会有cache。如果前面接一个load balancer,而 load balancer是随机的请求某个Server,那会miss 一些caches,可以通过global caches 或分布式caches实现。
所有的node 用同一份cache。
有如下两种:如果从global cache中没有找到数据,则通过cache请求底层数据库或者通过数据库异步加载到cache中。大多数情况下,第一种被采用。
分布式缓存,每一个节点都要自己的一部分cache。当有request的时候,用一致性Hash算法,计算请求的资源在哪个节点,如果该节点的cache不存在,则请求DB并将数据放入cache中。
分布式缓存不好的一点是如何修复缺失的节点。一些分布式缓存通过在不同的节点上存储数据的多个副本来解决这个问题,但这会使得系统变得复杂。
缓存可以访问数据更快,然而带来的是额外的存储资源增加或者昂贵的内存消耗。典型的开源的缓存框架是Memcached。
接下来讨论下,如果数据不在缓存中应该怎么做呢?
基本的代理服务器是:可以将客户端收到的请求,发送到后端原始Server,可以用来过滤请求,传输请求(增加/移出headers, 加密/解密等)。
代理也可以将多个客户端一样的请求合并成一个发送到后端服务器,这样只需要后端从磁盘读取一次。如下图,客户端都请求littleB的数据,proxy server把请求combine成一个去在disk中请求数据。
Proxy用的另一种方式,不仅仅combine请求对于同样的数据,而且会将请求数据附近的数据也拿到从而可以放在cache中。如下图,请求partB1的数据,会把bigB拿到。bigB包括了partB1,partB2,partB3。
一般来讲会将Proxy和Cache一起使用,并把cache放到proxy之前。常用的代理软件是: Squid and Varnish。
用索引加快数据的访问速度,大家都是在DB中用到的。索引要在增加的存储开销和较慢的写操作之间进行权衡(因为必须同时编写数据和更新索引),以获取更快的读取数据的速度。
主要对请求进行分布式的分发到不同Server。
针对这一部分,可以参考我之前写的高可用架构中 LVS,Keepalived,HAproxy解析以及实战
到目前为止,我们已经讲到了多种快速访问数据的方式,还有一个重要的是对于写数据的效率。在一个复杂的系统中,写数据可能要花费更多的时间,因为要写到不同的服务器,这样client可能会花费更多的时间去等待。提高效率的一个好的方式是用queue,让系统异步。
当一个task进入后,它被添加到队里里,然后workers取到下一个任务去执行。这些任务可能是DB的写操作或者其他复杂操作等。这样client可以周期性的查询queue执行结果,并且同时也可以服务于其他请求。
设计能够快速访问大量数据的高效系统是令人兴奋的,而且有许多优秀的工具可以支持各种新应用程序。本文只涉及了几个例子,仅仅触及了表面,但是还有很多,而且在这个领域还会有更多的创新。
翻译:http://aosabook.org/en/distsys.html