2012年3月30日星期五

分享一个很久以前的前公司的例子

俺过去曾经啥都干过,包括开发、排查Bug、数据库部署维护、服务器采购、机房迁移,乃至公司的电话机布线和程控机的编程……好吧,事情是杂了些,但不代表不专业。

不过销售为主导的公司,一定会发生一件事情,就是在技术上销售比技术还要牛叉。在以前的公司就曾经有这么一件事情发生在我身上:

某天开始,服务器每天都会时不时的速度很慢,然后就突然无法访问。该问题长时间排查都找不到原因,现象倒是挺明确的:不存在数据库连接泄露,某种偶然情况下Web进程内存占用会突然缓慢持续增加,当到达接近1GB时会突然崩溃,日志显示内存不足。

这个现象很不好理解,因为:

  1. 并非必然发生,而是阵发性的;
  2. 网络带宽只占用了30%-50%左右;
  3. 内存并没有占用满32位操作系统的用户内存空间上限,甚至连一半都没有用到,怎么就内存不足呢?
  4. 代码拉网筛查,以及跑profile也没有发现任何内存泄漏;
  5. 按道理来说,.NET托管内存是不可能存在泄漏的,而Web应用很少会有大量对象长期生存,也不可能占用内存至内存不足。

无论原因如何,也不可能是网络带宽的原因。可是搞笑的是,销售的一个头头居然大发雷霆,发邮件短信说技术反应迟钝不作为,这么久都不增加带宽。当时真是非常无语,尽管之前已经通报过情况,也说明不可能是带宽的问题。但既然已经惊动老总,只好按照其要求将带宽翻倍。可想而知,该现象依然如故,于是就没话可说了。

此问题非常棘手的主要原因是现象1,导致你只能一直远程并盯着内存量,当发现开始增长并持续一段时间之后,就要果断做Dump。可是,有可能你正好手头有事或者谁跟你说一件事,这个现象就错过了;又或者做Dump太晚了,导致CPU已经开始大量占用无法启动Dump程序;又或者Dump了半天快结束的时候进程被IIS自动Kill掉了;又或者Dump的太早什么都没有Dump出来。

经过很长时间的联系,终于熟练的抓到了一个Dump,结果发现实际内存占用量非常少,大约只有300M左右,但是内存提交量却达到了2G。最后实在没辙了翻查GCHeap,结果找到少量极其巨大的string对象,比如20MB。这些string对象指向了一个位置:日志。原来在系统中有一个数据库表的字段,是用来存放最后一次某些特地操作的。按照设计,这个字段应该只保留最后一次,但实际情况是该字段不知道为何被改成一直累加。于是乎,一些频繁访问的用户就会累积出这种巨大日志记录。只有当这些用户频繁访问的时候,才会造成系统崩溃。这很好的解释了前面所说的5个现象,尤其是阵发性。

也许有人会问了,那GC不应该回收么?对,GC会回收,但是GC回收会有一个副作用,就是CPU占用会变高,导致系统变得很慢。此外,还有一个无法在32位操作系统中很不好解决的问题:由于.NET设计上将大于85kb的对象放在一个特殊的Large Object Heap(LOH),而这个Heap和普通的GCHeap有一个明显的不同,就是GC发生时并不会进行对象拷贝(也就是碎片整理)。最后,连续的内存地址将会全部碎片化,此时如果需要分配大对象的时候,将会再也找不到任何连续地址用于存放此对象。虽然所有空余空间加起来远远超过几百兆,你也可能无法分配一个30兆的内存空间出来。于是才有了明明内存占用很少,却报告内存不足。

你看,这个问题挺技术的吧。可偏偏有那种什么也不懂的人,跑出来很技术的要求增加网络带宽。从这个事情上,我明白到合作的事情,你必须相信你的伙伴在他的领域内是专业的,不要傻了吧唧的在那里指手画脚。如果你不信任,那你就应该立即换人,并同时承担看错人的成本(包括新人和旧人)。否则,指手画脚除了显示出你的不专业,搞坏团队气氛之外,不能带来什么好处。除非,你认为以飞机失事的概率成功解决一件事情能算是成功。

哦,对了,这种事情从来不会停止发生。比如说上面说的那个销售老大技术真是完全不懂,否则根据我在通告中说明的“内存不足”异常,可以在带宽倍增无效之后,继续要求给服务器增加1TB的内存,然后再增加100个CPU和600个网卡。最后实在不行了,还可以赖.NET不如Java先进,要求更换至Java平台然后使用Oracle数据库。至于操作系统嘛,可以换成EXASOL EXACluster OS……

没有评论:

发表评论