節約記憶體是一種罪惡?
回福州幾天了,有好多東西想寫,可是由於一些個人問題而中斷了,那怎麼辦呢?今天來個重量級別高一點的內容吧。這可是我花了一兩年時間才明白的東西哪。。。是的,一兩年。
似乎從我的第一位程式語言老師開始,任何人都這麼告訴我:「寫出來的程式,要跑得又快佔的記憶體又少」,不是嗎?(第一位程式老師?噢,陳老師您好!不,可能還有更早的?是的80年代的basic教程。呵呵,別說我是80年代出生的,但是我的的確確很認真的讀過80年代的basic教程,雖然那時我還小)。所以現在的人們看到這種說法似乎覺得合情合理,是啊,跑得又快佔的資源又少。可是這往往是做不到的,你想讓馬跑得又快,吃得又少或者像以前的地主們總是希望長工吃得少幹得好這可能嗎?有可能,不過有乙個度,我們只能盡可能的把握這個度,讓程式跑得足夠快,又使用合理的系統資源,不是嗎?但是在這硬體技術快速發展的今天,我們應該認真考慮一下是否為了節省10m的記憶體占用而去花費兩個小時時間來優化程式。這麼做似乎損失的不只是你的兩個小時,還有系統的速度和穩定性,您看得沒錯還有「穩定性」。讓我們先看看下面這則故事(如果覺得相當無趣,直接看後面的說明吧)。
記得遙遠的公元2023年,程式設計師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到公司查一下確實資料不是同步的,實際上同步程式還沒執行完成,經過幾天的觀察同步程式不是在上班時間前跑不完成就是在更新過程中由於資料庫(長時間連線)異常退出了。。。。
呵呵,程式還是那麼的小,占用那麼少的資源,只是跑多個程式,這已經足以讓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]系統的資源利用率總是很高嗎?),忘記了老師教給你的「吃得又少跑得又快」的馬吧。
因為浪費系統資源,節約記憶體是一種罪惡。
中來,那時候我也罩不了你的。
為保證讀性,文中故事內容進行了部分誇大。。。呵呵,哈哈。
文中使用的例子用的是delphi,而讓我明白上面道理的工作與delphi沒有任何關係。c是一門讓你改變很多思想的語言。(不要跟我爭論哪種語言好,各人所處環境和階段不同罷了)
md,打字打了我兩三個小時,實在打累了後半段的論述實在不是很滿意改天再說吧,呵呵,改天?改天我想說的就是:《系統引數使用方法》了,不過換了gvim的確不會出現ue那種自動換行亂碼的情況,乙個字:好爽,歡迎拍磚:[email protected]。
h.j.leochen 2005.10.20 fuzhou fujian.
順便罵一下上海f1,md什麼東西出問題不好,跑道出問題了,真是丟人。
順便罵一下東航,md什麼時候你能明白,我們要的不是多好的服務,多好的機上點心,要的只是你能準點,安全的把我送到目的地。分清主營業務是什麼再搞你的亂開八糟的服務好不好?不是一次兩次了,為什麼每次坐你們航班都搞這種飛機?
順便贊一下中國聯通,沒想到拿了一張鑽石卡(a類大客戶,哈哈想當年我也是做大客戶系統的.),去機場竟然可以到頭等倉休息室(我是窮人,以前沒享受過.),讓我爽得不行。。。呵呵。
ZT 成熟是一種明亮而不刺眼的光輝
成熟是一種明亮而不刺眼的光輝,一種圓潤而不膩耳的音響,一種不再需要對別人察言觀色的,一種終於停止向周圍申訴求告的大氣,一種不理會哄鬧的微笑,一種洗刷了偏激的冷漠,一種無需聲張的厚實,一種能夠看得很遠卻又並不陡峭的高度。不要因為害怕被別人誤會而等待理解,現在生活各自獨立,永珍共存。東家的柳樹矮一點兒,...
mysql是一種 mysql是一種 系統
填空題 資料庫系統是由 構成的 單選題 服務企業的內部營銷包括 單選題 角 填空題 資料庫是指 資料集合 填空題 are you going to buy 其它 繪製第4題。填空題 i promise i 填空題 資料模型的三要素是 單選題 原稿 多選題 在服務企業的內部運作層面,進行內部營銷系統構...
快樂是一種品位,一種氣質。
這兩天很不爽,2月初,應該是寒冷的,可是杭州現在最高溫度達到了24度,確切的說今天的氣溫是13 24度,大太陽。暖和了,卻惶恐了。如果沒了季節之分,冬不冬,春不春,夏不夏,秋不秋,將多麼可怕。開啟qq氣象台,發現北方很冷,就杭州一帶很古怪。突然羨慕北方的人,享受著冬天。我覺得我都該裸奔了,熱啊!去年...