有关CPU占用和缓存的一些见解,希望给大家一些启发

 

首先说说缓存的问题,比如我们有1000000个字符串构成的List<string>需要缓存在内存中,大约占用了100M的内存空间。如果需要对这些数据进行检索的话,很明显直接由于数据量太大,即使在内存中检索效率也不会太高。

一般可以想到的办法就是利用层次话的字典结构来解决,比如SortedList<char, Sorted<char, List<string>> 这样就构成了2层结构,可以把字符串的第一个和第二个字母作为字典的Key,这样一下子可以把检索的数据量下降2个数量级都不止。

当然,在现实应用中可能存放的不是List<string>而是一个List<Entity>,这个Entity里面可能有N个属性,我们可能需要检索的就不止是一个字段这么简单了,可能需要为了检索Entity中的ID属性做个Dictionary<int, Entity>,也可能需要为了检索其中的Name属性做一个Dictionary<char, List<Entity>>,这就需要多份字典。

前面说过了,这个原始数据List<string>占用100M内存,那么你可能会想了,再来一个SortedList<char, Sorted<char, List<string>>那么就需要200M内存了,如果多份字典的话,更占内存?但是想想字典结构的效率真是高啊,怎么取舍呢?其实,如果你试验一下的话可以发现,再弄个SortedList<char, Sorted<char, List<string>> 可能只会占用1M内存,加起来一共是101M而不是200M,这是为什么呢?

记得引用类型和值类型的区别吗,如果你的数据结构大部分是引用类型,比如string的话,一个string的List,它其实存放了N个处于托管堆上的string的指针,那么如果我们再来一个字典结构的话,不是说我们就多存放了这些string,而是多存放了一套指针,外加Key所占用的一些空间。所以,遇到这种情况不用吝啬多一些字典结构来提高效率。

上面的例子我想说明一个问题,缓存多份数据不一定会占用相同的多份内存

还有,缓存的数据可以放在那里呢?一般来说有这么几种,static变量,.NET类库提供的HttpRuntime.Cache或者就是一些分布式的缓存解决方案,比如memcached。

有一些误解,很多人非常不喜欢static变量,我想有两个原因,一是可能觉得把100M数据存放到一个变量中,这是很可怕的事情,一个变量竟然占用100M内存,其实我们大多时候缓存的是一个引用类型,变量存放的只是一个指针罢了,100M的值类型确实可怕,二是很多做ASP.NET的人会觉得不能乱用static变量,就产生了这个错觉。其实,我觉得static变量的问题在于它没有线程安全的机制,二是它作为缓存方案不能过期,一旦创建后也不能删除,只能把它设置为空引用,等待GC回收。但对于某些情况,静态变量是适合的,比如我们缓存的数据是只读并且永远不需要过期的,静态变量作为缓存的确足够简单,也不用担心“丢失”!

HttpRuntime.Cache之类的方案的优势在于它提供了过期、依赖、线程同步的支持,也是我们用的最多的。

最后说说分布式缓存解决方案,比如memcached。其实我觉得在一般情况下不要去随便使用分布式的缓存方案,除非我们能满足以下的需求:1、缓存的数据非常多,一台机器的内存不够用,确实需要分布。2、需要确保数据不能丢失,在多台机器上做备份。为什么说不要轻易用,因为分布是有代价的,如果应用服务器和缓存服务器分开的话,势必就需要TCP通讯,这是一个消耗,另外一个消耗是缓存的数据需要序列化和反序列化。分布式缓存使用缓存一些直接能绑定到UI的数据,或者说直接用于呈现的数据,对于大块的数据非常不适合存放到memcached中。比如之前提到的List<string>,如果这个数据是100M,它不能直接用需要做一些计算再返回给WEB服务器,那么我们做的事情就是序列化后缓存到memcached中,然后再反序列化从TCP获取,放到内存中计算后返回给WEB服务器,然后内存中的100M数据等待GC回收。其中白白折腾了网络、CPU和内存(虽然会回收,但是GC一直回收100M的数据,谁受得了)。

上面的例子我想说明一个问题,缓存的方案需要根据应用来选择,静态变量不一定不好,分布式缓存也不一定好

最后,还有一个有关CPU占用的有趣问题,很多人很怕服务器占用CPU很高,觉得CPU占用一高就有性能问题。其实我想说的是,有的时候CPU占用的高不一定是坏事情。假设有两个网站。A网站它的数据源是数据库,每一个请求(假设是同步请求数据库)都需要1秒来从数据库获取数据,由于IO操作的时间会比CPU处理数据时间长很多,所以这个网站即使很繁忙,WEB服务器的CPU占用还不是很高,用户一个页面打开至少要等1秒,网站管理员看着WEB服务器CPU常年处理20%的情况觉得很满意,但想想这真能说明网站性能高吗?还有一个网站B,它数据都是从内存中读取的,由于内存的操作会比IO操作快很多,CPU处理一个请求的数据只需要20毫秒,由于网站页面速度快,用户满意度和数量比A网站高很多,CPU很忙,常年处于90%的负荷,网站管理员看了觉得很害怕,是不是哪里出现性能问题了,服务器会不会瘫痪。其实,我觉得B网站的管理员应该高兴,因为B网站的用户比A网站高一个数量级,服务器虽然很忙,但页面打开速度很快,CPU得到了充分的利用。

上面的例子我想说明一个问题,只要不是死循环等恶性代码问题的话,CPU如果占用的高不一定就是坏事情

很多时候事情往往不是像我们想的那样,很多时候好的东西往往也不一定是适合的。希望这三个小例子能给大家一些启发。

最后,想说一下最近使用LINQ的经验。不可否认,如果不分层,在网站中直接使用LINQ TO SQL和LINQ进行数据的存取以及转换,结合匿名类型等特性,至少提高开发效率一倍。有很多人关心LINQ的效率,其实我们应该要区分LINQ还是LINQ TO SQL,是使用LINQ TO SQL进行数据的转换还是使用LINQ进行内存中数据的转换很关键。要知道,尽量从数据库中拿少点东西,尽量少的进行数据库访问,所以说LINQ TO SQL的代码不适合放在循环中,如果要对大量数据操作的话,要先ToList()加载到内存中,然后之后的工作就是LINQ(TO OBJECT)了。如果项目对性能要求高的话,尽量在做好项目之后,多看看SQL监视器,观察一下页面究竟访问了几次数据库,因为程序写的很爽的时候可能会迷糊,导致页面访问N次数据库,这个时候要使用LoadWith来“化解”。

posted @ 2009-05-07 21:12  lovecherry  阅读(4581)  评论(17编辑  收藏  举报