Performance

《构建高性能Web站点》笔记:8 共享式/分布式文件系统

共享文件系统    主流实现:NFS          a.直接基于RPC协议,client/server通信模型 b.client再把服务端的ip映射为本地磁盘 c.RPC默认使用UDP,在应用层即RPC协议里做差错重传等操作 d.服务端采用多进程模型 e.NFS的性能天花板非常低,在高并发环境下(比如前置10台web服务器,每个web服务器100个并发数)非常容易成为瓶颈 f.唯一的好处就是简单 g.支持异步写。server接到异步写的请求后会立即返回"OK",然后再在后台把文件写入磁盘 文件系统中影响性能的因素:     1.磁盘吞吐率     2.服务器的并发处理能力     3.网络带宽 ========================== 共享文件系统很容易成为瓶颈; 替代方案是要让web服务器上也有文件,同时又保证服务器中各个节点上都有相同文件的副本。那怎么创建这些副本呢?   1. 可以主动地将文件传给别的服务器   a. 可以通过高级语言里的SSH Client模块 (具体的SSH应用是SCP和SFTP),将文件复制出去   b. 也可以走Http WebDAV协议(subversion走的就是WebDAV) 2. 也可以被动地轮循别的服务器,下载这段时间内更新过的文件; linux下的rsync就是这样一个工具。 3.另外,文件的复制可以用多级的方式实现。如中心服务器把文件复制到各省服务器,各省再复制到各市等等 ========================== 分布式文件系统    这里的文件的概念不是操作系统中的文件的概念,目录也不是操作系统里的目录(MogileFS甚至没有目录的概念);分布式文件系统自己定义了一套文件的概念,它运行在用户态。 分布式文件系统的作用:    1.在使用者眼里,一个分布式文件系统是一个单一的整体    2.但实际上,一个分布式文件系统确可以跨越多台服务器,并在服务器之间进行自动的文件复制 原理:    1.文件存储在Storage Node中    2.Storage Node之间可以根据规则自动复制文件    3.Tracker放在Nodes前端,作为客户端的入口      …

《构建高性能Web站点》笔记:8 共享式/分布式文件系统 Read More »

《构建高性能Web站点》笔记:9 数据库的扩展

数据库的扩展问题可以拆为两个子问题:   1. 高并发时,单台数据库服务器会不堪重负       2. 大数据量时,针对一个大数据集进行数据读写,响应时间会比较长   前端和后端 出于分而治之的理念,可以想到一种理想的方案:把DBMS拆分为数据服务系统和数据存储系统,前者作为用户请求的入口,后者则存储数据,两者的关系类似于web服务器和文件服务器;分离之后,再分别采用针对性的扩展方案,比如说数据服务系统可以像web服务器一样简单地集群,数据存储系统则可以采用类似于分布式文件系统的方案,然后再让“前端”调用“后端”,整体上像一个分布式系统   遗憾的是,传统的DBMS内部似乎并不支持这种分布(TBD)。一个DBMS进程中,一般都既要响应请求,又要存取数据;这种感觉就像,文件只能存放在web服务器里,如果要复制web服务器,就一定要考虑文件怎么处理。 接受“拆不开”这个事实后,再来看目前流行的各种方案。 首先,可以看看如何提高并发能力。 1.搞N个一模一样的数据库服务器,每个服务器分摊一部分用户请求   每台服务器都提供服务,每台服务器上都包含全量数据,这是最直接的方案       问题在于,实时同步各台服务器上的数据本身比较耗费性能:一台服务器上来了新数据,就立即广播出去,然后大家都从它那里复制数据;还没复制完,又有一台服务器广播了一点更新消息。。。如果大家总是忙于跟它人搞双向复制,就没有余力处理用户的请求了 2.服务器之间单向复制数据,并使用单个数据更新源    单向复制比双向复制快的多。一台服务器更新数据后顺便更新一下自己的日志(主服务器),其余服务器只根据这个日志更新自己的数据(从服务器),不再接收用户的数据更新请求。那“从服务器”对高并发又起什么作用呢?他们可以为用户的数据查询请求提供服务。 这就是读写分库,把读和写分摊掉了。     这种方案的问题在于,如果写请求很多,主服务器忙不过来,加再多从服务器也没有用。 3.分摊写请求     主服务器本身可以根据数据更新的目的地的不同,而分拆成不同的结点。 如果一个服务器中有两个库,可以把其中一个库挪到另一台服务器中去(前提是A库和B库基本不join),这样一来,对不同库的数据更新请求就落到了不同的服务器上(当然,读的请求也分摊了)。你也可以把库中不同的表放到不同的服务器上。 这种分法叫垂直分区。 但垂直分区仍有个极限:如果某张表只有一个字段,而且对这张表的请求非常多,你怎么办? 4.再次分摊写请求     可以把1万行的表,拆成10张1000行的子表,放到十台服务器中,这样可以再次分摊请求; 而且理论上说,这种分摊没有极限,数据每增多一点,服务器就多加一台。 这种分法叫做水平分区。其实水平分区不仅可以分摊请求,同时也可以对付大数据集的问题。夸张地说,以前表里有1万行数据,按索引查数据都很慢;现在表里只有1000行数据,全表扫描都无所谓了 大数据量的问题     上面提到的水平分区,已经可以用来对付大数据量的问题。这里就不再提了 最后罗列一下上述各种方案中可以运用的技术或工具  1. 实时、双向同步数据: mysql clustering(TBD) 2. 读写分离: mysql主从复制可以实现读写分离,mysql proxy则可以前置于主从服务器前面,自动决定读、写的归属,为应用程序提供透明的服务 3. 水平分区:     如果要根据user.user_id对user及其相关表分区,对于一个user_id,应该放到哪个区呢?    a. 取模:  5,10,15…放A区, 6, 11, 16…放B区 … …

《构建高性能Web站点》笔记:9 数据库的扩展 Read More »

《构建高性能Web站点》笔记 – 6 写缓存

读缓存都好明白,这里就不说了 写缓存,实际上是写“缓冲”,即 数据进入到后端(如数据库)之前,先累积在一个缓冲区内,等满足了一定阈值再一次性写入。 这样对后端写只需要执行一次写操作,节省不少性能。 可以用缓存框架如memcached实现这个缓冲

《构建高性能Web站点》笔记 – 7 集群与负载均衡

集群与负载均衡 负载分配的策略:    1. 按业务需要分配。比如“镜像下载”这种应用可以按用户所在地分配一个就近的实际服务器    2. Round Robin依次分配。实现这个策略需要记录上一次分发的去向。在高并发条件下,记录这个东西需要使用锁,因此对吞吐率有影响    3. 随机分配。做不到精确地平均分配,但一般情况下还是可以比较平均的 分发请求的手段:    1.前端服务器通过 http跳转将请求转发到实际服务器。缺点是用户可能会绕过前端服务器直接访问实际服务器,这样的话负载就不能均衡    2. DNS里一个域名配多个IP        a. DNS服务器一般采取Round Robin策略,但一般也支持其他智能策略;不管怎么样,分配策略的灵活度都比较有限,尤其是当你使用第三方的DNS服务时        b. DNS服务器不会成为性能瓶颈,这是它的最大优点        c. 由于DNS缓存问题,处理故障转移(某IP对应的服务机当机了)不是那么实时        d. 总的来说,DNS负载均衡简单可行,但比较粗放    3. 反向代理(第7层负载均衡)        a. 方便实现自己的调度策略,比如可以让好的机器多分担一些任务. Nginx就允许你设置“权重”        b. 反向代理本身要有比较强的性能,否则就会沦为瓶颈。当反向代理的能力到达极限时,后端再添加多少服务器都无济于事        c. 可以用作负载均衡器的反向代理:Nginx, HAProxy        d. 总的来说,反向代理做负载均衡比较灵活、好用,但额外开销比较大,容易成为瓶颈    4. DNAT: (传输层负载均衡)修改TCP数据包的目标IP和端口        a. …

《构建高性能Web站点》笔记 – 7 集群与负载均衡 Read More »

《构建高性能Web站点》笔记:4.1 动态内容缓存

动态html缓存是指 将动态的内容如 detial?id=xxx 缓存起来,存成文件; 当收到同样的请求时,直接返回缓存的文件,而不必再去执行一次完整的请求处理过程,比如查数据库什么的。 缓存除了放文件里,还可以放内存里,或者一台独立的缓存服务器中。 具体实现的方案包括:   1. web/mvc框架:如php的smarty,它提供了缓存操作的API   2. web服务器:如apache的mod_cache,它根据http头里的一些字段来决定缓存的刷新策略 (在这里,缓存的key是url)   #2比#1的代码侵入性要低的多、甚至可以说没有侵入; 但反过来说,这也意味着缓存的设计依赖了web server,并且应用程序对缓存失去了直接控制 前端 另一个重要的缓存点是动态脚本编译后的opcode,如jsp编译后的字节码。 将opcode缓存起来,可以避免每次请求时都编译一次   1.php/jsp都可以玩这一套,其中jsp还可以pre-compile   2.这种缓存只能节省CPU/内存的开销,跟I/O没什么关系; 如果你的应用的瓶颈是在I/O,那这种做法就没什么效果

《构建高性能Web站点》笔记:4.2 动态内容静态化

把本应被动态的内容在被请求之前就存成静态文件,用户访问时不经web controller判断,而是让服务器直接返回静态文件;比如新闻网站的新闻,就可以直接存成html。 静态化对性能的帮助非常大。 静态化不属于缓存范畴,因为这里并没有缓存命中、过期等问题。    1. 不过, 静态文件仍有过期的问题,如新闻被修改了怎么办? 策略有两种,一是定时刷新,二是新闻在后端被修改时更新前端对应的静态文件    2. 局部化问题。 一个页面有一部分经常修改,有一部分基本不变,只有后一部分可以静态化。这种情况下应该把后一部分做成SSI文件,再包含到整体页面中; 不过,扫描SSI的include标签是比较耗CPU的;使用SSI机制时,应该告诉web服务器不要去不含SSI的文件中扫描include    3. 这样做可以大幅提升qps, 不过,你的服务器带宽要足够大才能最大地发挥它的优势

缓存 V.S. 缓冲

缓存(cache)是指把远端的数据在本地复制一份,下次获取数据时在本地获取就可以了,以节省开销 缓冲(buffer)则是用于解决高速设备与低速设备的桥接问题。 高速设备如果直接把数据丢给低速设备(或从低速设备那里读),则数据的流速就受限于低速设备,高速设备也沦为了低速设备; buffer的作用就是让高速设备先按自己的速度把数据输出到缓冲区中,低速设备再来读取,这时高速设备可以干别的事,最大化地利用自己的吞吐量。 当然, 这里要有个时间差。

《构建高性能Web站点》笔记:4.3 反向代理的缓存

用反向代理提供缓存:用户执行一次请求后,反向代理将请转发到后端服务器,转发应答时,反向代理将其内容缓存;下次再来同样的请求时,反向代理直接以自己的缓存应答   反向代理中如果不引入缓存,对性能可能会产生伤害;因为它引入了一次请求转发 提供缓存的反向代理:   1. Apache里可以mod_cache + mod_proxy联用实现代理,Nginx也有类似的模块。它们好用,但还不够强大   2. Squid是最著名的反向代理,它很强大,但过于重量级,使用比较复杂   3. Varnish则比较专注于反向代理,而且比较好用 Varnish   1.通过修改http头来决定是否缓存   2.它的配置文件是用一套长得很像c++/java的DSL写的,所以配置非常灵活   3.varnish的8011端口可以接受命令以清除缓存,你可以通过vinishadm工具直接向这个端口发送命令,也可以从它处向这个端口发送一个http请求   4.varnishstat提供了监控界面,给出诸如cache hit/cache miss之类的数据; varnish还有一个web监控界面 有了反向代理缓存,还有必要搞动态内容缓存(后端服务器自己的缓存)吗? 如果你的应用会有下列情形,则搞搞也无妨。   1. 有的请求不会通过反向代理,直接到达后端;这时后端的缓存就能发挥作用   2. 首次访问一个页面是没有缓存的,这时反向代理缓存就会miss。如果后端服务器预先实现了静态化(严格地说,这不算缓存),就可以解决这个问题    3. 如果一个后端服务器前面有N个反向代理,可能会出现缓存不均匀的情况;如果有的请求落在了缓存不丰满的反向代理服务器上,性能表现就会不佳;如果后端服务器有缓存,就可以为不丰满的反向代理服务器“补位”

《构建高性能Web站点》笔记:5 web组件分离

让你的网站的不同组件使用不同的域名 并且/或者 使用不同的服务器,如 www.glasswall.org          #信息、宣传相关的应用, 服务器名home bbs.glasswall.org          #论坛应用, 服务器名bbs image.static-glasswall.org    #图片,服务器名static js.static-glasswall.org       #javascript,服务器名static html.static-glasswall.org     #静态html,服务器名static 假设首页 /index既包含动态内容,又包含图片和js,使用上述方案有什么好处? 1. 不同域名,可以提高浏览器下载的并发数。 一个浏览器,对同一个域名只能同时下载5、6个组件;现在域名分开了,浏览器可以同时下载5、6个图片和5、6个js, 并发能力迅速翻倍 2. 不需要cookie的组件采用独立域名,避免不必要的cookie发送。你可以让动态内容发送的cookie只使用glasswall.org域,浏览器在请求*.static-glasswall.org时就不会发送cookie;这样可以减少http header的大小 3. 使用不同的硬软件服务器,以适应不同应用的特征;经过恰当的组合,可以获得最理想的总体请求时间或总体成本控制。 比如bbs服务器需要比较好的cpu/内存,而static服务器则需要比较好的带宽和磁盘,以及配置了非阻塞I/O的apache httpd和反向代理缓存。一台cpu强的机器 + 一台磁盘好的机器, 总价格小于两台 cpu/磁盘都强的机器 不同类型的内容,使用不同的优化方案 1. 图片   a.一个页面图片多而小,则可以使用长连接,避免频繁打开建立连接   b.图片内容一般不怎么变,所以Expires可以设得很大,以充分利用浏览器缓存 2. css/js: 变化频率比较小,所以Expires可以设得很大; 当真的发生变化时,可以给css的url加上新的版本号或时间戳,使浏览器原来缓存的内容失效 3. 大文件下载   a.服务器要接入高速宽带   b.磁盘有较好的并行能力   c.web服务器可以打开linux sendfile选项

《构建高性能Web站点》笔记:3.3 系统调用对性能的影响

系统调用    a.有些高级的操作需要进行系统调用,比如获取当前时间、读写文件等    b.进行这些操作前,进程会先从用户态转入内核态,执行完后再转回来    c.这种转换是有一定开销的,所以要尽量避免不必要的系统调用 可以用strace -pid 来监视进程的系统调用 (如果进程代码是gcc编译的并且使用了static选项,则可以得到更简洁的输出)