老说Linux的坏话貌似有点不太好,咱搞IT的也要讲究平衡。说心里话,Linux虽然有很多不爽,但爽的时候也有,此时就会挺痛恨Windows里的一些问题。
Windows服务器里面的东西通常都比较傻瓜和简单,但是这也就造就了如下的一些问题:
1、出问题的时候,日志不一定有,或者有了也不够清晰,或者无法调整至更清晰的日志输出级别。相对应的,Linux里面较成熟的应用都很注重这类事情;
2、不开源,导致除了问题要查找非常的困难。如果遇到了文档齐全的还好,要是不齐全或者文档里面没有描述,那就可能需要你通过付费服务来解决了;(这又延伸出来另一个问题:比如说,开Ticket让微软来解决,那我的代码也有只是产权不便提供。可以想象,他们调试起来也会很困难,乃至盲人摸象。)
3、要做到傻瓜,就一定有额外的开销,甚至从设计上就有不同的考虑。除了可能导致占用更多的资源,还有可能会导致某些操作的速度急剧下降。
今天我们就遇到了一个例子,正好三个问题都沾点边。这个问题其实一直都存在:用户说访问好慢啊!只是原来不那么明显,再加上系统结构复杂,CDN、交换机、负载均衡、缓存、Web、数据库、存储,分别需要考察CPU、内存、带宽、硬盘IO等项目,也确实不容易。之前也找到一些有嫌疑的地方,但处理之后却没有太大的改善。今天又找到了另一个嫌犯,貌似它才是元凶:Web上面缓存目录中文件过多,导致读取该目录的文件时,速度非常的慢。
在Web上面做缓存,是为了避免每次都动态生成一些内容,减少后端DB的各种压力和Web端的CPU占用率。理论上来说,由于前端已经有缓存,后端的磁盘IO应当不是问题。比如假设每秒钟有100个链接需要通过缓存文件返回,每个文件平均大小10KB,大概也就1M的数据量。加上可能有很多文件是已经在内存中有缓存的,因此实际的磁盘读取量并不会那么大。
按道理来说这种级别的随机读取应该不是大问题,对吧?但是当一个目录中存在大量的文件时,就可能悲剧了:比如说这个目录下有10000个文件,平均每个文件的文件名为50个字符。这时候整个目录光是文件名的数据量就达到了:10000*50*2(Unicode嘛)=1MB。实际上一个文件名至少还需要对应上该文件所在的位置等信息(具体有哪些没去考究),而实际读取的数量也并非全部的数据。由于NTFS文件系统中记录大目录中的文件名所采用的是B-树的形式存储的,检索一个文件所需要读取的信息量也没有那么大,而实际读取数量取决于你要查找的文件所在的层次。后者则取决于这个目录中文件的数量,一次分配的Extend会有多大,文件增删的情况,以及恢复平衡的策略。(啥是Extent见这里)
这个B-树的层次会是只有2层,还是有多层,目前还没有找到确切的说明。一次分配的Extent会有多少个簇,也还没有找到确切说明,但貌似我看到的情况好像是1个。同时如果一个子树节点包含太多孙节点,检索效率也会明显下降,因此不可能很大。
为了便于理解和计算,假定一个文件会占用100个字节,而B-数的层次是多层,每次分配的Extent均为2个簇。因为MFT节点大小为1K,一个簇4K,我们可以得到:(大约)
- 根节点大约能保存5个文件;
- 每一个(层)子树可以保存8K/100 = 80个文件;
在树平衡的情况下,10000个文件保存的情况大致如下所示:
根:5=5
第一层:400=5*80
第二层:3600=5*80*80
第三层:5995<5*80*80*80=2,560,000
也就是说,比较糟糕的情况下,打开一个文件需要访问到第三层,也就是要访问1+8+8+8=25K的数据。对于平均状况而言,可能需要访问13K。如果我们套用之前的数据计算,每秒100个文件,文件数据大小10K,再加上这13K的目录数据访问量,每秒磁盘读取数量就达到了2.3M每秒了。
呃,好像也不是很大啊。但是当我们用ProcessMonitor来监视的时候,就发现了这样的一个记录:
ReadFile C:\XXX目录\YYY目录 SUCCESS
Offset: 6,664,192, Length: 4,096, I/O Flags: Non-cached, Paging I/O, Synchronous Paging I/O |
请注意,这个操作居然是Non-cached的!也就是说,这种操作很可能每次都要从磁盘中读取出来。我们知道,对磁盘的随机访问性能实际上是很低的。一块普通的7200转SATA盘,随机IO能达到1MBps基本上就已经到顶了。即便是1万转的4块盘做RAID10,随机IO能够达到10MBps已经很难了,一般也就维持在5-6MBps之间。按照上面的计算,使用后一种配置的服务器,每秒也就只能够撑大概700个文件的随机访问。(呃,如果你只有那么700个文件的话,那肯定不止这个数,因为文件会被缓存进来,甚至7000个都不成什么问题。但如果你有几十万个文件,并在这堆文件中来回随机访问的话……)
从上面的分析可以看出来,要提升性能,除了提升硬件性能外,还需要注意:
减少一个目录中文件的数量;
减少文件目录的层次;
缩短文件名的长度。
呃,等等,还有一个问题没解决:为什么是Non-cached的呢?是否因为这个原因导致了系统性能不佳呢?目前说实话还没有找到资料,也许是.NET框架的设计原因,也可能是操作系统的设计原因。
这,就是Windows系的一个大问题:很封闭。如果是在Linux世界,大不了直接看各个部分的代码,只要你能看得懂,也愿意看。反正只要花时间,那也一定是可以解决的。但是在Windows世界那就未必了,比如我之前遇到的一个问题,最后就不了了之了,我也无能为力。而在Linux世界里面,squid有个行为和我们想象的不一样,看代码发现有个地方的判断和想象的不一样。对于后者,我们就可以讨论,到底是改让后端用另一种方式输出呢,还是修改Squid。换做Windows系统,大多时候只能干着急,别无他法。比如说我们就遇到StateServer不时地报告连接中断的问题,但就是没有什么特别好的方法进行排查,也没有特别详细的文档去说明这一现象,最后仍然是不了了之。
也许有人说,NTFS资料还是挺多的啊。可是很多时候这些资料是不准确的,比如上面说到的保存目录的方法是B-数,在很多很多的地方都说是B+树,包括微软官方的页面、维基百科的页面等等。但是实际上请仔细看微软官方页面上提到的图二,在看看关于B+树和B-树在维基百科上的定义,你就会发现NTFS采用的显然是B-树。这个错误估计是某个半桶水的无证临时工,在编辑网页资料的时候将工程师写的"B-tree",私自修改成了"B+tree"造成的。也有可能是画图美工嫌工程师给的图不好看,随手给改成现在看到的图二,结果从B+树变成B-树的形态了。事实是如何的呢?因为没有代码可看,实在不清楚。而MFT内部结构据称也是非公开的,没有官方资料。当年曾经想自己写一个后台不停修复硬盘碎片服务的代码,其中有一步叫做查找碎片,Windows有API可以做但是很慢。我所看的那个帖子中也提到,如果直接通过MFT记录来查找碎片的话,会快很多,可惜这部分内容不公开,网上现有资料无法保证完全准确。
如果你在Windows中遇到的问题跟可能与你无关的部分有关,后续的工作通常很难开展。.NET类库算是一个例外,这也是我为啥这么喜欢这个平台的原因之一。
没有评论:
发表评论