中国开发网: 论坛: 程序员情感CBD: 贴子 232631
haitao
节约内存是一种罪恶? --评论似乎更牛
节约内存是一种罪恶?
节约内存是一种罪恶?

回福州几天了,有好多东西想写,可是由于一些个人问题而中断了,那怎么办呢?今天来个重量级别高一点的内容吧。这可是我花了一两年时间才明白的东西哪。。。是的,一两年。
似乎从我的第一位程序语言老师开始,任何人都这么告诉我:“写出来的程序,要跑得又快占的内存又少”,不是吗?(第一位程序老师?噢,陈老师您好!不,可能还有更早的?是的80年代的basic教程。呵呵,别说我是80年代出生的,但是我的的确确很认真的读过80年代的basic教程,虽然那时我还小)。所以现在的人们看到这种说法似乎觉得合情合理,是啊,跑得又快占的资源又少。可是这往往是做不到的,你想让马跑得又快,吃得又少或者像以前的地主们总是希望长工吃得少干得好这可能吗?有可能,不过有一个度,我们只能尽可能的把握这个度,让程序跑得足够快,又使用合理的系统资源,不是吗?但是在这硬件技术快速发展的今天,我们应该认真考虑一下是否为了节省10M的内存占用而去花费两个小时时间来优化程序。这么做似乎损失的不只是你的两个小时,还有系统的速度和稳定性,您看得没错还有“稳定性”。让我们先看看下面这则故事(如果觉得相当无趣,直接看后面的说明吧)。
记得遥远的公元2002年,程序员leo(这名字怎么这么熟悉?呵呵,是我年轻的时候)接到一个开发任务,开发一个两个系统间用户资料同步的程序,然后这个程序跑在一台hp 6000服务器(我希望您明白PC机,服务器,小型机,大型机是一个逐级上升的设备排列,虽然很长时间我并不明白。),4个intel至强CPU,3G内存,在win200系统上面跑了一个负荷很小的bea tuxedo应用服务器以及一个用以程序版本升级的ftp服务器。CPU利用率5%以下,内存占用率750M以下。leo似乎明白了要做什么,并且写出了简单的功能说明:从A系统取到用户ID,然后从B系统获取用户资料,判断资料是否相同,不同则更新A系统的用户信息。很简单是吗?是的按照leo那时的经验和想法,只花了一个半小时就用delphi写出这么个程序,一个TQuery控,从A系统的用户表中获取用户ID,有如:select msisdn from vip_users; 然后对这个ResultSet进行循环,直到eof前一直做:
1.获取当前记录用户id,既:msisdn字段。
2.从当前(A)系统获取用户资料。
3.从B系统获取当前用户资料。
4.调用或个函数或者存储过程比较两系统的资料。
5.如果资料不一致将B系统的信息更新到A系统中。

“啊哈,完成了。哥们,我写出了一个可执行程序只有500K大小,占用内存不超过20M的程序来完成这个功能。”

第二天系统上线了,30万数据的用户资料跑了5个小时完成数据同步,于是兄弟们都上前为leo祝贺,“你完成了一个伟大的功能”,“那我们让它每天晚上2点自动运行,这样早上上班的时候人们就可以看到同步后的最新信息了,真棒!”

一个月,两个月过去了,没出过任何问题,人们似乎忘记了leo曾经写过这么一个伟大的程序。而由于客户的业务开展良好,进入vip_users的用户也越来越多了,一切都是那么的美好....

一天9点半的时候,leo还在沉浸在梦中与美女相会,客户经理打来电话,“怎么回事,我们的数据与B系统中的不一致?”,“不可能,我们的同步程序每天都在定时运行”............

---------呵呵,大家不要扔砖头,我是一个很憋脚的故事作者,呵呵,第一次用这种方式写东西。。。

leo到公司查一下确实资料不是同步的,实际上同步程序还没运行完成,经过几天的观察同步程序不是在上班时间前跑不完成就是在更新过程中由于数据库(长时间连接)异常退出了。。。。

由于系统用户的增多,更新程序已经无法在5,6个小时内完成任务了,于是大家讨论出了解决方法:启多个进程每个进程更新一部分数据。
呵呵,程序还是那么的小,占用那么少的资源,只是跑多个程序,这已经足以让leo再自豪一阵子了,看我写得程序多么具有可扩展性,在不修改任何代码的情况下能适应系统的新需求。于是大家将程序复制一份修改了取资料的语句,原来的:select msisdn from vip_users; 变成了
select msisdn from vip_users where area_code in(a,b,c); 和另一份的:select msisdn from vip_users where area_code in(d,e,f);

就这样在后面的很长一段时间内,程序不停止的被拷贝,sql语句里的where子句不停的修改。因为大家都觉得这不是程序的问题,这是数据量增大的问题,因为leo的程序写得又简单占用资源又小,这完全符合老师们跟我们说的"好程序"的标准哪。

这种情况在leo离开这间公司后仍然延续着............

好的,故事讲完了,聪明的您是否看出了leo犯的错误,是否对于故事的内容有很多疑问?是否对前面说的:“而这么做似乎损失的不只是你的两个小时,还有系统的速度和稳定性”有疑问?怎么影响稳定性了?

好,让我们看看leo离开这间公司一年后(这是真的),对于这个问题的解法:

一年后仍在维护这处系统的哥们找到leo,说“救救我吧,我被这处程序折磨的要疯了..”
leo给出了下面的解法:

1.将A系统需要同步的所有用户ID和资料全部(或部分)一次性加载到内存的链表,比如delphi中的T***List中。
2.循环链表,取得B系统中当前用户ID的资料
3.比较两系统的资料.
4a:方案一,将资料不同的记录放入需要更新的链表。(推荐)
全部比较完将需要更新的链表中的数据更新到数据库。
4b:方案二,直接更新需要同步的数据到A数据库中。


当然数据量非常大的时候把数据全部加到内存是愚蠢的,所以你可以在上面步骤的外层加一个循环,比如每次加载5万或是10万的记录到内存来处理。

非常简单的修改,带来了巨大的改变,更新速度快了,而且再没出现过更新过程由于数据库长时间连接的错误而退出的情况了,那哥们说“哈哈,我又有时间去泡武汉大学的MM了”。

这两个方法其实非常相似为什么效果有这么区大的差别呢?
首先你需要明白:

1,数据库资源是非常宝贵的。占用一个长时间的数据库连接,远比您的程序占用1G的内存来得罪恶。
2,数据库是非常慢的。(??是的,我没说错,在我们现在的系统是在IBM 690上跑的数据库,而整个系统中最慢的那些程序就是与数据库相关的程序,而这些应用往往只是做简单的插入和更新,但这并不是因为我们不懂如何使用数据库或是语句没经过优化之类的,oracle,sybase,db2很快吗?无论数据库查询或是修改速度再快您觉得它会快过链表操作或是平衡二叉树操作吗?)
3,外部系统(这个故事里指数据库),是不可控的。 您可以任意的修改您的代码,却无法修改外部系统的任何东西,哪怕只是想把某个变量声明得更可读些。甚至您根本不知道它们会在什么时间出一个什么样的错误。

第二种方法改变的只是将长时间依籁于数据库系统的操作改为依籁于应用内部资源使用的方法,这样您只是多占用一些原来就空余的内存,少占用一个长时间的数据库资源,这样您就不会由于长时间占用数据库的临时表空间,内存资源,回滚段资源,进程资源,而发生的数据库强制断开连接或是网络错误。数据库也不必长时间保存您的操作结果而不能为其它人服务了。。(您记得前面我说的3G内存只用了750M吗?)

并且由于您查询的数据全部放到您的进程地址空间,而一切都变得都由您来控制。

“但是您占用了很多内存哪?”,是占用了比原来多很多的内存,不过利用这些空闲内存解决天天烦恼您的问题,较之于闲置着几G的内存而让您天天看着系统资源利用率不到5%那种意淫得来的快感是不是舒服很多?

对于一般的系统来说:输入和输入是最不可控的,我们应当尽量减少由于输入输出而引起的软件异常对于系统处理流程的影响,最直接的做法就是限制系统中对于输入、输出的操作点和操作频率。把这种最可能出现的异常限制在最少的地方和最少的时间。
所以我们所建议的系统流程是:

1.加载系统输入数据。一次性加载全部或是一大批,而不是一次一条。
2.数据处理逻辑。这里只是在内存中处理数据,尽可能的不涉及到输入和输出操作,因为毕竟内存操作的出错可能性远比文件系统、数据库或者网络来说小得多得多。
3.处理结果输出数据。也是一次把您前一次载入的输入的处理结果输出,而不是处理一条输出一条。

从大的范围来说这种方法对于异常的处理是最经济的,对于事务完整性的处理也最简便,加载输入,处理数据和处理输出中的任何异常您都可以简单的限定在不同操作的范围内,不需要考虑单条处理过程的那种复杂的事务处理。

所以我们应该尽量多的使用系统空闲资源(你知道为什么用top之类的命令看linux[或者说unix like]系统的资源利用率总是很高吗?),忘记了老师教给你的“吃得又少跑得又快”的马吧。


因为浪费系统资源,节约内存是一种罪恶。


当然本文讨论的内容并不适合所有情况,大家仁者见仁,智者见智吧,当您由于对PC应用叫嚣这种说法而被人满街追杀时千万别躲到的我地盘http://www.upulife.com中来,那时候我也罩不了你的。



为保证读性,文中故事内容进行了部分夸大。。。呵呵,哈哈。
文中使用的例子用的是delphi,而让我明白上面道理的工作与delphi没有任何关系。C是一门让你改变很多思想的语言。(不要跟我争论哪种语言好,各人所处环境和阶段不同罢了)
MD,打字打了我两三个小时,实在打累了后半段的论述实在不是很满意改天再说吧,呵呵,改天?改天我想说的就是:《系统参数使用方法》了,不过换了Gvim的确不会出现UE那种自动换行乱码的情况,一个字:好爽,欢迎拍砖:hjleochen@hotmail.com。


H.J.LeoChen 2005.10.20 fuzhou fujian.

顺便骂一下上海F1,Md什么东西出问题不好,跑道出问题了,真是丢人。
顺便骂一下东航,Md什么时候你能明白,我们要的不是多好的服务,多好的机上点心,要的只是你能准点,安全的把我送到目的地。分清主营业务是什么再搞你的乱开八糟的服务好不好?不是一次两次了,为什么每次坐你们航班都搞这种飞机?
顺便赞一下中国联通,没想到拿了一张钻石卡(A类大客户,哈哈想当年我也是做大客户系统的.),去机场竟然可以到头等仓休息室(我是穷人,以前没享受过.),让我爽得不行。。。呵呵。





Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=510761

[点击此处收藏本文] 发表于 2005年10月21日 11:14 AM



JJ 发表于2005-11-07 12:09 AM IP: 211.155.137.*
如果是我来完成这个工作,我会在找出需要更新的用户ID的过程中多考虑一下,全部查出再依次比较是一种很粗鲁的做法,应该斟酌一下这个SQL语句

例如

SELECT a.id FROM A, B WHERE A.f1 != B.f1 [AND WHERE A.f1 != B.f1]^n

如果SQL 不能提供比较的规则,我会考虑编写一个 DataBase的扩展来完成比较的任务

同样,我也会斟酌 WHERE 条件的先后次序,先判断最不可能改变字段,这样会较大的缩小比较范围

所以,我获取ID的时间是可控的,对于 30w 条记录的表,即使每天需要更新的有 1W的数量级,这个查询应该可以在 30min 内完成

当然,如果是一个对更加专业的SQL开发人员,他可能会编写一个
UPDATE B ... 这样的 SQL 来完成整个的更新过程,然后制作一个定时器来进行更新,当然,我不是这样的开发人员,但我知道这么干是可行的,而且是最有效率和最易于维护的。

楼主用了一个错误逻辑论证了一个错误的观点,在任何的情况下,编写占有资源小而且运行速度快的程序都是一个专业的程序员应该考虑的事情,专业的程序员应该具有比较宽的知识面,会从数据库中SELECT表不代表会使用数据库,事实上,我很为楼主几年来所蹉跎的时间感到惋惜,因为你偏离走向专业程序员的道路。

“一次性加载全部或是一大批,而不是一次一条”这个观点是正确的,不过话说回来,如果你做过细致的测试,你会发现 每次读取 5w 条的效率实际上跟每次读取4K条是差不多的

顺便,偶是C程序员,最近有跟 SQL打交道的工作,所以就稍微看了一些 SQL 相关的文档,有点班门弄斧了,呵呵。


wjjchess 发表于2005-11-07 9:16 AM IP: 220.163.20.*
首先,我评论一下,这个文章写得很好,写出了我得心声

谈一下我得感受:
在看这个文章前半部分得时候,觉得说得不错,可是看到后面,
我发现,文章作者忽略了一个情况---
-节约资源,是整个系统节约才算,而不是单个系统节约就算好的

看到后面,我有点索然无味了


不过,对于那些刚刚介入开发系统的工程师或者刚刚大学毕业的学生来说,这个文章非常又意义


迷恋向日葵 发表于2005-11-07 9:44 AM IP: 218.14.89.*
同意JJ的看法,


路过 发表于2005-11-07 9:53 AM IP: 211.99.223.*
大概看了一下,从已有的数据库中查询数据再对比再同步,这种做法是比较愚蠢的。

我的做法,是在修改数据的时候来实现同步。

具体如何实现修改数据时同步,实现的过程很有多,就看设计者是如何实现的了。

我对动不动就要跑在小型机,大型机之上的程序总是表示怀疑。

节约成本才是重要的,我宁愿自己写一个pc机的集群,也不愿意用什么型机.


H.J.LeoChen 发表于2005-11-07 10:22 AM IP: 222.79.4.*
首先感谢wjjchess的评价,不过我似乎不是很明白您说的:
-节约资源,是整个系统节约才算,而不是单个系统节约就算好的
的具体意思。如果说整个系统特别指电信行业系统,一般是按具体功能划分每个大功能一个模块,一个应用程序,然后用某种所谓的胶水语言,或者调度程序调度,具体性能问题出在几个主要功能模块上,当然这个还是比较容易找到问题和解决的,近阶段比较关心模块间流转所消耗的时间和性能。。。


也谢谢JJ的精彩评论,有些想法不是很赞同,造成应该是我的问题,我的本意并不想在这个故事上花太多时间(实际上花了太多时间),也不想在这里讨论具体的数据同步算法(当然有哪位对syncML之类的感兴趣我们哪天可以好好谈谈)
本文我真正想说的是:

1,数据库资源是非常宝贵的。
2,数据库是非常慢的。
3,外部系统(这个故事里指数据库),是不可控的。

而最重要的是:外部系统是不可控的,要减少外部系统对稳定性及性能的影响,我觉得比较好的办法是减少对外部系统的访问点,对于外部系统的依赖尽量集中在少的地方,减少因外部系统的错误的影响面,使得错误处理及事务处理也得以限制。
而这种减少外部系统的访问次数一般就要增加系统资源的使用量,我想说的是不要为了减少一些资源占用而频繁的对外部系统进行访问。
而外部系统不可控这很容易理解,比较数据库系统表空间满了?或者这个月的表没建,等等。。。。

我确实没测试过4K和5w条的效率有何区别,因为我不是很关心这个,因为所谓的系统性能也绝对不是系统跑得快就好,
但我想我明白对数据库的100次操作和1次操作哪个引起故障的可能性大。

我也从来没否认过:“编写占有资源小而且运行速度快的程序都是一个专业的程序员应该考虑的事情”,我只是说不要因为过度的节约资源而牺牲系统的其它性能比如:稳定性,正确性。

虽然近阶段我比较迷茫,但是不到为我自己几年来所蹉跎的时间感到惋惜时候。

顺便说一句,我也是C程序员。再次感谢。


Ivony... 发表于2005-11-07 10:24 AM IP: 222.240.184.*
对于大数据量操作,重要的是策略而不是优化。怎样让自己干的事情少一些而不是干得快一些。例如在同步处理上引入哈希比较,脏标志。

如果要你在两台大型机之间同步几个T的数据,你也是全部读过来然后什么什么的吗?


dreamhead 发表于2005-11-07 10:45 AM IP: 202.118.4.*
程序设计其实就是一个不断选择的过程,这里的矛盾在于没有把握好时间和空间。对于“度”的掌控就是体现出一个程序员修炼功力的地方。


lyqq 发表于2005-11-07 10:54 AM IP: 10.167.38.38, 10.167.39.11, 202.106.154.*
不知道google 的大数据 是在什么机器上面跑的?

我的同学 曾经写过一个简单的字符串比较的程序, 当然,他没有怎么优化算法, 结果在一个PC机上(CPU:2.8G, MEM:1G)从晚上11点跑到早上8点半, 但是用PC服务器(CPU:Xeon×2,MEM:4G)跑了一下子, 大概是 半个小时就出来了。不过,没有测试过 小型机跑。

不要拍砖,路过而已。




yippeesoft 发表于2005-11-07 10:56 AM IP: 218.85.0.*
因为leo的程序写得又简单占用资源又小,这完全符合老师们跟我们说的"好程序"的标准哪。

呵呵,我记得好程序的标准是效率

30万记录要跑5个小时

还不如直接把数据库文件拷贝过去算了~

或者把分区GHOST过去


H.J.LeoChen 发表于2005-11-07 10:56 AM IP: 222.79.4.*
"节约成本才是重要的,我宁愿自己写一个pc机的集群,也不愿意用什么型机."

虽然我对PC集群之类的很感兴趣,比如yahoo,google,但可能是我见识的少,至今没见过2个T以上的数据量的系统跑在PC集群上面的。.....


yippeesoft 发表于2005-11-07 11:00 AM IP: 218.85.0.*
原来阁下作行业软件的
算我没说

效率高了怎么卖服务呢~~~~

呵呵呵


inshua 发表于2005-11-07 12:07 PM IP: 218.18.142.*
连接池~


再次路过 发表于2005-11-07 12:11 PM IP: 211.99.223.*
2个T的数据能表示什么?运算能力还是存储能力?

如果用你的方法,在两个系统同步一下2T的数据,你需要什么样的大型机?在同步的时候,竟然没有用到cache,竟然硬生生的查询-》对比-》同步,真是服了你了。

我从来没有听说过我周围的同行会把1G以上的数据存放在一台机器上,或者上千万行的数据存在一台大型机上并且依赖一个db server.
据说电信行业的牛人们就喜欢这么做?
他们的设计理理念就是:“给我一个足够快的机器,我就能运算所有的工作了”.

google的数据量比你们大多了,为什么没有听说google有用到什么型机?google全是最便宜的pc机来组成的.

一台pc机(p4 2.4 + 2G的memory)可以很轻松的存储500万行的数据.再大的数据经过分布存储之后,也只是每台机器百万行的数据计算时间。

问题不在运算能力,也不在存储能力,也不在你节约时间,而是你一开始的架构和设计就是错。
我的blog:http://szhaitao.blog.hexun.com & http://www.hoolee.com/user/haitao
--以上均为泛泛之谈--
不尽牛人滚滚来,无边硬伤纷纷现 人在江湖(出来的),哪能不挨刀(总归是要的)
网络对话,歧义纷生;你以为明白了对方的话,其实呢?

您所在的IP暂时不能使用低版本的QQ,请到:http://im.qq.com/下载安装最新版的QQ,感谢您对QQ的支持和使用

相关信息:


欢迎光临本社区,您还没有登录,不能发贴子。请在 这里登录