Python linux java centos Firefox HTML5 google Ubuntu linux命令 php Windows 微软 apache mysql shell 程序员 开源 Android nginx wordpress

Linux Web服务器小文件优化之道-文件成组

昨天在群中,又有很多人在问,我的服务器跑不上量,我的服务器只能跑十几M 流量,为什么别人能跑上 G 的流量。为什么?

服务器基本很少是为了跑量的,只有到了最后,追求成本的时候这时,每台服务器能跑多少流量就成为关键了。但我不得不提一定就是,提升服务器的性能的前提是稳定。稳定是运维第一要素。

另外,服务器处理能力我们可以分开来看:

  • 动态内容: 动态网页知道吧,这个不用详细解释了吧。
  • 静态内容: 服务静态文件,象图片服务器,css js 的服务器,象文件服务器,象 cache CDN 的代理服务器,大多都是静态的。

我们在谈论服务器为什么跑不起量时,就得分开来考虑这二种大的分类了。

动态文件 因为动态文件大多时候追求的是处理数量,不是流量了。另外动态文件的性能,90% 是动态文件执行,写法本身的影响.象 PHPperl 象 JSP 这些程序本身加上不同的应用。是否连接数据库,是否写得太烂。这些内容大多和程序员本身就有关系,另外就是网站架构和程序架构的问题了。我们就不上升到架构问题了。所以这提到的跑量主要是指服务静态文件时最影响性能的有哪些。

静态文件 我们在处理静态文件时,最需要关注的就是 WEB 服务的模型。也就是这台服务器是处理的请求有什么样的特点。简单来看看,可以关注以下几点:

WEB 服务的模型

  1. 连接 : 连接主要关注是同时有多少连接在服务器上,每秒能处理掉多少,连接是否使用 keepalive
  2. 命中率: 命中率可能大家不明白,就是程序是否能缓存这个数据。如 squid 服务,命中率关注二点,程序本身,系统本身
  3. 响应大小: 响应大小就在静态服务中基本就是指文件大小,可能是服务的模型中最重要一点,下面会重点讲这个。
  4. 建连的时间: 建连的时间在小文件时比较关注,一个网页比如有 100 个元素这样建连时间就很重要了,主要的影响有 DNS ,服务器本身的处理,链路
  5. 硬盘的性能: 这个是服务模型中另一个比较重要的点,这个和响应的大小是关系在一起的。
  6. 处理每个对象的其它开销: 这个算是其它。有时服务器会主动压缩之类,还有 acl 的影响之类。这个大多时候是比较次要的。

比如我现在讲二个相反的模型

1. 小文件大请求模型

  • 大量连接
  • 命中率普通
  • 响应文件非常小
  • 建连时间中
  • 硬盘 4 个普通 SATA
  • 其它开销小

2. 大文件少量请求的模型

  • 连接不多
  • 命令率小
  • 响应文件比较大
  • 建连时间慢
  • 硬盘 4 个普通 SATA
  • 其它开销小

象这个是上面的静态文件中最常见的二种 WEB 服务的模型,第一种是图片的服务,第二种是视频和下载的服务从上面我们可以看出什么不?第一种流量小,第二种流量高,主要是由于响应大小决定,其它的影响都会小些。好我们来分析为什么我们知道普通读离散文件时硬盘的有效 IOPS 的 50 。有时 50 还达不到。如果文件是小图片,一个就几百个字节 到 几K。比如这个网页我们看到唐凤的图是3.05 KB。我们来计算一下,在离散文件,不是顺序读写时 4 个硬盘的时候,硬盘的能力是多少。

3.05k * 4HDD * 50IOPS = 610k
#换算成网络流量
610k * 8 ~= 4.9M.

知道了吗?在这种 WEB 服务模型时,只能服务 4.9 M 的流量。所以第二影响的变成了命令率,这时操作系统能 Cache 多少,和服务本身能 Cache
多少就成了关键。

视频和下载服务的第二种 WEB 模型我想不分析,大家也知道,普通硬盘顺序读写能达到 80M – 100M/s 这时流量很容易跑到网卡的极限。

这时,我们提到重点了,怎么提高小文件的性能,这个问题是个很老的问题,在 Linux 中想了非常多的法子,比如 Russel 在 1997 年就提出四种基本的I/O性能优化策略。

  • 避免 I/O 这是最有效的方法,避免磁盘访问就避免了I/O操作的延迟和开销。缓存是避免访问存储设备的基本机制。
  • 顺序化 顺序访问可以使磁盘数据通道的有效利用率最大化。在有多个I/O负载的情况下,做不到完全的顺序访问,也可以通过增加I/O 大小来尽量减少寻道次数。寻道操作越少,在数据定位上花费的时间就越少,磁盘能达到的吞吐量就越大。
  • 异步化 异步 I/O 把所需数据提前加载到内存,以便向应用程序隐藏 I/O 延迟。它可以使 CPU 和磁盘同时工作,从而提高计算机系统的利用率。
  • 并行化 把多个磁盘组织成一个以RAID为代表的阵列形式,并展开并行I/O,是提升存储系统吞吐量的一个基本方法。进一步的方法是以集群的方式组织多个存储服务器进行并发数据服务。通过提升阵列或集群的规模,并进行并发I/O,存储系统原则上可以满足任何 I/O 带宽的需求。

这个大多是操作系统中做了,操作系统会给读写进行合并和排序二种操作,然后给连续的 I/O 的请求放到一起,但这个是很后端来做这个。就象 LVS 和 Nginx 均衡一样。LVS 工作在 4 层,所以不能做应用级的优。我这就是要提出一种方案,能做应用层的 I/O 优化。

这个怎么做啦,就是给用户要的请求,放到一起合并成组成一个大文件。我们知道读大文件效率很高,这样他一次读一个大文件后,在给大文件中的内容提取出来成一个个小的对象来回应客户端的响应。

这样,比如上面那种大量小图的网页,我给一个网页中所有的小的图片存成一个文件,然后有用户请求这个网页时就会接着请求这个网页中所有的图片元素。这样我只要读一次文件,就能给所有的图都读出来。

原来很多离散的 I/O 变成了一次顺序的 I/O.我们在这变读小文件为读大文件。 这相当于在应用层做了合并和排序这二样事情。这种优化最重要的一点是怎么确认那些文件要成组成一个块。这样来保证这个块的命中率。简单来讲可以有几个方法:

  • 网站结构: 这个是最好确认的,给同一个网页的小元素成组到一个块。
  • 请求顺序: 同一个用户的一次请求大多是连续的,另一个用户同一个链接打开的时候也会是一样的情况,也可以成组合并成一个文件块,但不如上面的方法
  • 网站来源: 象刚才那个网页,基本上所有的这个网页中图片元素的 referer 基本是同一个,对同一个 referer 也可以组成一个文件块。当然还有很多其它的方法。

这种技术的另一个重点是,WEB 服务器的特别要求

  1. 能分解成组合并后的块为单独的元素
  2. 能一次读入这个块到内存中,不读多次

实现这种技术也有很多方法,比如有现成的服务器就是用的这个 Squid 的 COSS 的存储。我们可以给 COSS 看成会根据请求顺序来给文件成组合并成一个块并存起来,也能很好的分解成单个元素的一个不错的实现,当然要是能根据网站结构,比如图片网站每页来存成一个块就更加强大了。

如图,打开 http://hexten.net/cpan-faces/

如图,只要有人请求http://hexten.net/cpan-faces/,就会给整个这个网页图象这个块打开,然后其它的从  img0.img 到 img100.img 都在内存中取,不在读取硬盘。

延伸阅读

评论