无论是程序员,DBA还是网管,似乎所有的ITer都会遭遇同一问题:性能优化。NAS从业者也不例外,而且NAS的问题更加棘手,因为它所涉及的协议和设备很多。本系列博文将从共享协议入手,再到网络,最后到NAS服务器本身,逐层分析NAS性能的影响因素。
NAS的共享协议,就是之前介绍过的NFS和CIFS.他们的读写方式总体相似,但是细节处又有不同。我们先来看CIFS(SMB)的工作方式。
当我们在Windows Explorer上打开一个128KB的文件时,底层的读操作是这样的:
1. 客户端:我想读某文件。
2. NAS: 你有权限,读吧。
3. 客户端:先读64KB.
4. NAS:给你64KB.
5. 客户端:再读接下来的64KB.
6. NAS:给你64KB.
如果这个文件不只128KB,5和6就不断重复,直至整个文件读完。在这个过程中,影响性能的因素有:
一、客户端总是在收到上一个读请求的回复后,再发送下一个请求。这其实是一种低效率的工作方式。就像某人今晚想吃肯德基的鸡翅和汉堡,他先叫鸡翅外卖,等鸡翅送达后,再叫汉堡外卖。合理的方式是鸡翅和汉堡外卖一起叫。在读文件时体现为:
1. 客户端:我想读某文件。
2. NAS: 你有权限,读吧。
3. 客户端:先读64KB.
4. 客户端:再读接下来的64KB.
5. NAS:给你64KB.
6. NAS:给你64KB.
由于3和4(两个请求),以及5和6(两个回复)可以接连发送,所以节省了往返时间(如下图所示)。SMB2的读操作就是以这种方式工作的。为了优化性能,建议把Windows客户端升级到Windows Vista或以上,然后启用SMB2.
二、 客户端每次读的数据大小,也会影响到性能。在上面的例子中客户端每次读64KB,是一个比较好的数值。如果读的值特别小(比如4KB),会增加读操作和往返次数,从而影响性能。这个值的大小是由客户端和服务器协商决定的。客户端上完全取决于应用程序,服务器上一般有设置选项。比如Windows服务器提供了SizReqBuf这个注册表键值供用户设置。
三、 另外一个性能影响因素是服务器的响应时间。对于读操作,最常用的优化方式是启用"prefetch".即服务器在回复了前一个读请求后,立即把接下来的数据从硬盘中读出,等着回复下一个请求。
CIFS的写操作和读操作方式相似,对于相同点就不再赘述。不同点主要体现在响应时间的优化方式上。服务器为了优化写操作的响应时间,一般采用write cache的方式。也就是服务器先把客户端写过来的数据存在cache里,然后向客户端确认。接下来再慢慢把cache里的数据刷进磁盘。当然这种方式存在一定的风险,如果服务器突然断电,cache里的数据就会丢失。客户端的应用程序可以启用write throuth来避免write cache.
下面,我们再看看NFS的工作方式。
和CIFS不同,NFS共享在使用前需要挂载(mount)。挂载时使用的参数很大程度上影响了读写的性能。列举如下:
1. UDP或TCP:在网络非常稳定的情况下,UDP理论上比TCP性能好一点,因为UDP包在协议上的消费比例低。但是如果有网络包丢失,TCP就显示出优势。因为UDP包丢失时,整个读/写操作的所有包都要重传;而TCP包丢失时,只需重传丢失的那个包即可。
2. rsize和wsize,每次读写的最大值。该值对性能的影响和CIFS的第二点是一样的,所以不再赘述。
3. sync和async,sync意味着服务器需要把数据写到磁盘再确认;async则意味着服务器可以把数据存到cache里就确认,然后再慢慢把cache里的数据刷进磁盘。有一个经常被忽视的严重问题:sync参数会强制wsize变成4KB,这会大大降低写性能。
本文介绍的几种优化方式,比如启用SMB2,增加rsize等,除了它们本身能优化性能,还能提高传输层的性能。
为了检验"叫外卖"的方式能提高多少效率,他在NAS和作为客户端的Windows 7上都启用了smb2,果然看到读写性能大幅度提升。
熟悉网络的读者可能会存疑:在局域网里的往返时间(RTT)很短,读写的总时间其实大多消费在服务器的响应上。smb2的改进看起来只节省了RTT,可能达到大幅度提升(比如数倍)的效果吗?这个质疑完全正确,但这位读者的实验结果也是真实的。怎么解释这个矛盾呢?答案就在TCP协议上。本文将详解TCP中影响NAS性能的各个因素,包括对以上矛盾的解释。
在逐条分析之前,先让我们复习一下TCP的重传机制,因为后面会多次谈及它。当有TCP包在网络上丢失时,TCP有两种机制来实现重传:超时重传和快速重传。下图展示了这两种情况:
图1, 发送方只给接收方传送了一个包。不幸的是这个包在网络上丢失了,所以发送方迟迟等不到来自接收方的确认。在经过一段时间(RTO)之后,发送方认为该包已经丢失了,所以重新传了一次。这个机制就是超时重传。RTO能达到数百毫秒,这在计算机世界可以算"浪费很长时间"了,NAS对一个读写请求的响应也就几个毫秒。除此之外,超时重传还会使TCP发送窗口降到最小,更是雪上加霜。如果网络中有超过0.1%的超时重传,我们就能看到明显的性能问题。减少超时重传对提高性能有明显的改善。
图2,发送方要给接收方传送5个包,不幸的是第一个就丢失了。由于这个发送窗口>=5个MTU,所以发送方在没有接收方确认的情况下继续发送了四个包。接收方在收到这些包的时候,可以通过包号知道第一个包丢失了。所以收到第n个时(n=2,3,4,5),接收方就发一个"收到n,但1还没收到呢"给发送方(如下图的红线所示)。发送方在收到四个"但是1还没收到呢"的消息后,意识到1可能已经丢失了,就赶紧重传一个。这个机制称为快速重传。由于这个过程没有等待时间,所以对性能影响较小。实现超时重传的条件是发送方在丢了一个包后,接下来还有4个或以上包可以传。
明白了这两个机制后,我们再逐条分析TCP对性能的影响因素:
1、TCP滑动窗口:如果要把10块砖从A地搬到B地,你是一次搬一块,总共搬10次,还是一口气搬10块呢?在力气允许的条件下,自然是一口气搬完速度快,因为节省了往返时间。网络传输也是如此,如果有10个TCP包要传,在带宽允许的情况下应该一起发送,而不是发一个就等确认,然后再发下一个。举个例子,假如往返时间RTT是2毫秒,那10个包逐个传至少要花20毫秒;一起传就只需要2毫秒多一点,对性能的提高是显而易见的。除此之外,在发生丢包的时候,大窗口可以提高快速重传的概率,减少了超时重传。比如10个包一起传时,前6个包中任何一个丢失都可以由接下来的4个包触发快速重传。而每次传4个包是永远等不到快速重传的机会的。
2、多线程:在一个TCP session里,如果存在多个线程,也可以在丢包时提高快速重传的概率。还记得《NAS性能优化之一》里关于smb2的"叫外卖"图片吗?在第一个请求没有完成的情况下,就可以发送第二个请求。如果第一个请求有丢包,那第二个请求的包可以帮忙凑满四个,从而触发快速重传。本文开头提到的读者在测试smb2时得到大幅度的性能提升,很可能就得益于此。除了SMB2和NFS协议,EMC免费提供的EMCOPY工具也能在SMB中实现多线程拷贝。
3、超时重传时间(RTO):这是一个动态值。RFC规定了计算该值的方法,但是结果比较大,已经不适用当今的网络环境了。有些NAS(比如Celerra)提供一个设置,允许强制把该值改小。
4、Jumbo Frame:中文好像翻译为巨帧。就是把MTU增大到9000,从而减少TCP头和IP头在一个网络包中所占的比例。理论上这是能提高性能的,但是实际效果却不一定。因为大包的丢失概率更大一点,而且包数少了,就更有可能发生超时重传。
5、网络拥塞:除了网络配置出错(比如两端的speed/duplex不符合),另外一个导致丢包的因素就是网络发生拥塞。如何避免呢?最有效最简单的方法当然是购买更高端的switch,但这也是最难被接受的建议。有一个将就的办法,就是人为的把TCP滑动窗口强制在拥塞点以下。宁愿每次少传一点,也不要丢包。有些NAS(比如Celerra)提供了强制最大滑动窗口的设置,但是要确定一个合适的拥塞点比较麻烦,需要抓大量的包分析。