最近處理的性能優(yōu)化總結(jié)思考

按照常理,性能優(yōu)化應(yīng)該是屬于比較高級(jí),處于項(xiàng)目中后期的工作了套腹,但是如果實(shí)現(xiàn)不給力,在項(xiàng)目初期就可以遇到了资铡。

很多人都嫌棄Python慢电禀,個(gè)人認(rèn)為他們之中90%都沒有資格這么說,一方面笤休,需要高性能的地方并不是每個(gè)項(xiàng)目都需要尖飞,另一方面,他們自己寫的代碼爛的要死店雅,才是罪魁禍?zhǔn)住?/p>

Python的代碼可讀性非常好政基,利于開發(fā)和維護(hù),是對(duì)開發(fā)者友好的語(yǔ)言闹啦。但如果代碼寫成一團(tuán)糟沮明,沒有揚(yáng)長(zhǎng)避短,導(dǎo)致維護(hù)困難窍奋,開發(fā)新功能無處下手珊擂,性能又遇到瓶頸,這個(gè)時(shí)候又怪罪起Python或是框架來费变,可以說是愚蠢至極摧扇。

我為什么要提上面這段呢?因?yàn)槲以诠ぷ髦胁粩嗟赜∽C了之前聽聞的一個(gè)說法挚歧。那就是扛稽,當(dāng)一個(gè)項(xiàng)目重構(gòu)后,代碼量縮減滑负,性能提升n倍在张,往往會(huì)被人們歸功于使用了新語(yǔ)言或是新框架。但這樣的理解是不對(duì)的矮慕,就算是使用同一種語(yǔ)言帮匾,同一種框架,在重構(gòu)時(shí)痴鳄,由于已經(jīng)有了之前的積累瘟斜,這些積累包括需求的深刻認(rèn)識(shí),弊端和bug的提前了解,以此為鑒螺句,才能夠在之后的重構(gòu)時(shí)虽惭,搭建起較好的架構(gòu)實(shí)現(xiàn)。
如若不然蛇尚,該挖的坑還會(huì)再挖一遍芽唇,該跳的坑還得再跳一次。不從以往吸取教訓(xùn)取劫,總結(jié)經(jīng)驗(yàn)匆笤,那么永遠(yuǎn)都不會(huì)有出頭之日。

最近我就做了一些性能優(yōu)化工作谱邪,也正好是同一語(yǔ)言同一框架的優(yōu)化疚膊。有些東西,雖然我們聽說了虾标,記住了寓盗,但是如果沒有經(jīng)歷過的話,總感覺會(huì)缺少些東西璧函。在這次的優(yōu)化工作中傀蚌,一些東西不斷地從實(shí)際當(dāng)中總結(jié)出來,又不斷地和以往的知識(shí)相互驗(yàn)證蘸吓,讓我感覺受益匪淺善炫,有點(diǎn)融會(huì)貫通的感覺,包括上文和下文提到的東西库继,我都是深有感悟奥嵋铡!這個(gè)時(shí)候一定要記錄下來宪萄,因?yàn)楦行缘恼J(rèn)識(shí)艺谆,和最終能夠形成文字表達(dá)出來的認(rèn)識(shí)也有不同,后者明顯對(duì)知識(shí)的掌握更深拜英。類比做題容易静汤,但是如果要給別人講清楚如何做題,那對(duì)人的要求就要更高了居凶。
綜上啊虫给,這就是經(jīng)驗(yàn)啊,只有見的多了侠碧,才算是身經(jīng)百戰(zhàn)抹估。。弄兜。

下面談?wù)勗敿?xì)情況药蜻。

有些東西是可以立即采用的瓷式,比如異步、cache谷暮。

異步的作用無疑是非常大的。首頁(yè)加載慢盛垦?運(yùn)營(yíng)人員抱怨后臺(tái)大批量操作時(shí)頁(yè)面卡资摇?那這個(gè)時(shí)候就要考慮處理流程里面都必須要用戶等在那里嗎腾夯,用戶需要立刻就看到結(jié)果嗎颊埃?在書上,我們經(jīng)车悖看到發(fā)郵件這種操作會(huì)被作者舉例班利,說是非常適合異步。同樣榨呆,進(jìn)首頁(yè)的時(shí)候判斷用戶參與了哪些活動(dòng)罗标,是否需要發(fā)紅包,運(yùn)營(yíng)審批一些后臺(tái)數(shù)據(jù)积蜻,這些操作都可以異步實(shí)現(xiàn)闯割,對(duì)結(jié)果沒影響,還顯著提升頁(yè)面加載和后臺(tái)審批速度竿拆。
并且宙拉,異步處理可以使用多個(gè)worker,進(jìn)一步減少處理時(shí)間丙笋。
目前我們公司異步處理很復(fù)雜谢澈,大類可分為Redis隊(duì)列和Celery處理,其中內(nèi)部還有細(xì)分御板,我認(rèn)為這部分能夠重構(gòu)一下锥忿,拋棄Redis,統(tǒng)一使用Celery怠肋。

cache缎谷,譯作緩存,也譯作快取灶似,實(shí)際上這是它的一體兩面列林。什么東西很長(zhǎng)時(shí)間不會(huì)變,什么東西不需要立即對(duì)數(shù)據(jù)庫(kù)進(jìn)行寫入酪惭,這些東西統(tǒng)統(tǒng)可以使用cache希痴。
cache不單單指使用NoSql數(shù)據(jù)庫(kù)。其實(shí)在代碼里面就可以實(shí)現(xiàn)某些東西春感,比如有一個(gè)值是從數(shù)據(jù)庫(kù)中讀取的砌创,但是基本不會(huì)改變虏缸,那這個(gè)時(shí)候可以使用@cache_property來將這個(gè)值作為類的屬性緩存起來。這樣的話嫩实,當(dāng)程序啟動(dòng)后(有些場(chǎng)景是第一次訪問請(qǐng)求時(shí)刽辙,比如使用odoo會(huì)在第一個(gè)訪問請(qǐng)求到來時(shí)建立url_mapping,會(huì)將對(duì)應(yīng)的endipoint存儲(chǔ)甲献,endpoint中即是處理對(duì)應(yīng)url的類的實(shí)例)宰缤,就會(huì)緩存到內(nèi)存中(和NoSql同樣是內(nèi)存!)晃洒,缺點(diǎn)是萬一要是值有改變的話慨灭,得重啟才行。
還有的球及,當(dāng)然就是使用NoSql數(shù)據(jù)庫(kù)了氧骤,我們這邊用的是Redis。最近我看了一些《Redis應(yīng)用實(shí)踐》吃引,發(fā)現(xiàn)其中對(duì)Redis的使用非常主動(dòng):并不是所有數(shù)據(jù)都要存放到關(guān)系型數(shù)據(jù)庫(kù)中筹陵,NoSql只是一種輔助,而是將非常多的數(shù)據(jù)镊尺,比如說是涉及到經(jīng)常改變的數(shù)據(jù)惶翻,直接放到Redis中進(jìn)行處理,而永久化數(shù)據(jù)并沒有提及鹅心。非常遺憾的是吕粗,我們公司目前還沒有這種做法或是想法。
我們公司已經(jīng)實(shí)現(xiàn)了兩套緩存機(jī)制旭愧,但由于odoo的ORM寫入機(jī)制颅筋,保存到Redis中的數(shù)據(jù)還沒怎么使用呢,就經(jīng)常被重寫输枯,結(jié)果導(dǎo)致了非常嚴(yán)重的死鎖议泵,所以都廢棄掉了。
目前的話我們只是在代碼里零星地使用Redis進(jìn)行緩存桃熄,可以說不怎么正常先口。做優(yōu)化時(shí),我的代碼實(shí)現(xiàn)還非常土瞳收,同樣的語(yǔ)句寫了好幾遍碉京,被CTO和一位架構(gòu)師同時(shí)吐槽,但目前也沒有時(shí)間去優(yōu)雅處理螟深。谐宙。。
以前一直沒感覺NoSql有多神界弧,直到最近我才深深地理解了什么叫做“保存在內(nèi)存中”的含義——硬盤就算是SSD凡蜻,關(guān)系型數(shù)據(jù)庫(kù)也還是慢搭综,對(duì)放在內(nèi)存里的數(shù)據(jù)實(shí)在是太快了。

以上這兩點(diǎn)的話划栓,只要能夠理清楚實(shí)現(xiàn)邏輯就可以動(dòng)手做改變了兑巾,但接下來的一些優(yōu)化需要有能夠定量的指標(biāo)才行。

關(guān)于Python代碼的執(zhí)行時(shí)間忠荞,我推薦使用line_profiler蒋歌,它可以按行標(biāo)注執(zhí)行時(shí)間,比較好用钻洒。其他的很多都是按函數(shù)來標(biāo)注奋姿,不太符合我們的需求锄开。
按照line_profiler提供的每行執(zhí)行時(shí)間素标,以及每行調(diào)用次數(shù),可以很輕松的找出瓶頸萍悴。無奈的是我司代碼瓶頸太多了头遭,我一般是抓大放小,先從最嚴(yán)重的開始癣诱。

一些比較好找的點(diǎn)是计维,ORM的性能,比如odoo中filtered語(yǔ)句就比search語(yǔ)句執(zhí)行的慢撕予,而search語(yǔ)句中鲫惶,若使用a.b.c這種格式,那么ORM在翻譯search語(yǔ)句至sql語(yǔ)句時(shí)实抡,就會(huì)變成join語(yǔ)句欠母,這樣執(zhí)行的也慢。再比如吆寨,有些功能有更好的實(shí)現(xiàn)赏淌,比如說是優(yōu)秀的第三方庫(kù),那么對(duì)原先的代碼進(jìn)行替換即可啄清。

剩下的都是需要深入場(chǎng)景六水,深入代碼邏輯中去判斷是否能夠優(yōu)化。一般而言辣卒,如果一行代碼執(zhí)行時(shí)間過長(zhǎng)掷贾,那么就要進(jìn)行思考,大體上荣茫,首先要想想是否需要這么計(jì)算胯盯,再考慮怎么優(yōu)化。如果一行代碼執(zhí)行次數(shù)過多计露,那就要考慮是否有多次重復(fù)調(diào)用了博脑。我在這次優(yōu)化中憎乙,發(fā)現(xiàn)有一處調(diào)用了300多次,按照?qǐng)鼍爸恍枰?0次即可叉趣,后來發(fā)現(xiàn)在代碼邏輯中有一處是從上往下進(jìn)行循環(huán)泞边,在循環(huán)中調(diào)用了一個(gè)方法,這個(gè)方法又進(jìn)行從下往上的遞歸疗杉,結(jié)果就往返執(zhí)行了300多次阵谚。優(yōu)化好之后,發(fā)現(xiàn)又有一個(gè)地方執(zhí)行了3000多次烟具,我真是百思不得其解啊梢什,后來反復(fù)查找,才發(fā)現(xiàn)是有一個(gè)模型的方法中有一個(gè)自動(dòng)觸發(fā)的方法朝聋,當(dāng)有些屬性改變時(shí)就會(huì)進(jìn)行調(diào)用嗡午。真可謂是一步一坑啊冀痕!

說了這么多荔睹,其實(shí)我們也沒有涉及到很高級(jí)的技術(shù),都是一些基礎(chǔ)言蛇。但是僻他,從性能優(yōu)化的工作中,也能夠看出一些問題腊尚,也就是所謂的根因:一是Model設(shè)計(jì)不合理吨拗,Model設(shè)計(jì)的好不好沒有標(biāo)準(zhǔn),是有一些比較教條的規(guī)則婿斥,但更應(yīng)該看的是Model的設(shè)計(jì)是不是非常符合實(shí)際的應(yīng)用場(chǎng)景劝篷,我司的設(shè)計(jì)估計(jì)剛開始時(shí)還是符合的,但目前已經(jīng)非常不匹配了受扳。
二是代碼質(zhì)量不過關(guān)携龟,我在上文所舉的那個(gè)例子,就是很好的證明勘高,對(duì)于這點(diǎn)我只能說要看個(gè)人的自覺了峡蟋,也沒有想到或是聽說過有很好的方法,code review這種東西在我司執(zhí)行效果不佳华望。
三是極端測(cè)試不充分蕊蝗,否則也不會(huì)等到線上流量暴增之后,才發(fā)現(xiàn)這么多性能問題赖舟。
四是性能未量化蓬戚,當(dāng)初在寫代碼時(shí),并沒有考慮到哪些地方對(duì)性能有要求宾抓,只是把功能簡(jiǎn)單的實(shí)現(xiàn)而已子漩。這就好比總分是100分豫喧,考了60分已經(jīng)及格,但是總分突然提升到1000分時(shí)幢泼,60分就是個(gè)垃圾紧显。雖然過早的優(yōu)化是不對(duì)的,但是如果總是低要求的話缕棵,那就是挖坑不止孵班,問題永遠(yuǎn)都解決不完。

上面提到的都是通過技術(shù)解決招驴。下面談?wù)劷鉀Q問題的思路篙程,當(dāng)然是個(gè)人見解。實(shí)際上遇到問題的時(shí)候别厘,我們首要的考慮是虱饿,出現(xiàn)問題的場(chǎng)景是真需求還是偽需求,也就是說丹允,問題并不需要都通過技術(shù)手段來解決郭厌,而且技術(shù)手段也不應(yīng)該是首選袋倔。這個(gè)過程應(yīng)該是一步步來雕蔽,不斷地對(duì)各種取巧方法進(jìn)行否定,直到無法可解宾娜,才去選擇技術(shù)來解決批狐。
而當(dāng)選取技術(shù)時(shí),也并不是說直接硬上了前塔,像是采用異步嚣艇、cache這種討巧的技術(shù)來解決問題其實(shí)是非常漂亮的。這次有個(gè)需要優(yōu)化的地方執(zhí)行了20s左右华弓,原因是會(huì)對(duì)4000+個(gè)浮點(diǎn)數(shù)進(jìn)行加運(yùn)算食零,在實(shí)際當(dāng)中,它是一個(gè)偽需求寂屏,我們通過一個(gè)tag來規(guī)避它贰谣,而通過一個(gè)定時(shí)任務(wù)來執(zhí)行計(jì)算,而計(jì)算本身我也只是引入numpy的sum來替換python的內(nèi)置sum迁霎。如果它真的是一個(gè)真需求的話吱抚,我才可能會(huì)去考慮多進(jìn)程/線程,Map-Reduce考廉,C代碼等等其他技術(shù)秘豹。
這樣可能不太符合一個(gè)技術(shù)人員的定位,面對(duì)問題應(yīng)該正面強(qiáng)上的啊昌粤,這樣才能證明自己啊……但我恰恰喜歡這種比較“取巧”既绕,“暴力”的方法啄刹,而且認(rèn)為這樣才足夠優(yōu)雅。

最后說點(diǎn)其他的凄贩,關(guān)于死鎖鸵膏。之前我是認(rèn)為死鎖這種高大上的東西得非常大的數(shù)據(jù)才會(huì)出現(xiàn),沒想到啊沒想到怎炊。歸根結(jié)底谭企,一部分歸咎于ORM的實(shí)現(xiàn),一部分則是代碼實(shí)現(xiàn)太爛评肆。如果執(zhí)行夠快的話债查,行級(jí)鎖是很難觸發(fā)死鎖的。但是按照我司一個(gè)事務(wù)能執(zhí)行30min+的尿性瓜挽,不出死鎖才怪盹廷。目前我們把一些長(zhǎng)事務(wù)給分拆,變成一個(gè)個(gè)小事務(wù)對(duì)數(shù)據(jù)庫(kù)進(jìn)行commit久橙,來規(guī)避死鎖的觸發(fā)俄占。

總之,寫代碼之前好好理清楚需求淆衷,好好設(shè)計(jì)架構(gòu)缸榄,寫代碼的時(shí)候要對(duì)人友好,要寫得優(yōu)雅祝拯,寫完后要進(jìn)行測(cè)試甚带,要對(duì)各種可能出現(xiàn)的情況提前做好判斷,是否對(duì)性能有要求也要提前想好佳头,并對(duì)實(shí)現(xiàn)進(jìn)行驗(yàn)證鹰贵。

軟件工程,軟件工程康嘉,做工程一定要考慮成本碉输,做工程一定有前有后有各種要求標(biāo)準(zhǔn),軟件工程永遠(yuǎn)都不是代碼羅列亭珍。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末敷钾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子块蚌,更是在濱河造成了極大的恐慌闰非,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件峭范,死亡現(xiàn)場(chǎng)離奇詭異财松,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門辆毡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來菜秦,“玉大人,你說我怎么就攤上這事舶掖∏蜃颍” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵眨攘,是天一觀的道長(zhǎng)主慰。 經(jīng)常有香客問我,道長(zhǎng)鲫售,這世上最難降的妖魔是什么共螺? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮情竹,結(jié)果婚禮上藐不,老公的妹妹穿的比我還像新娘。我一直安慰自己秦效,他們只是感情好雏蛮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著阱州,像睡著了一般挑秉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贡耽,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天衷模,我揣著相機(jī)與錄音鹊汛,去河邊找鬼蒲赂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛刁憋,可吹牛的內(nèi)容都是我干的滥嘴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼至耻,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼若皱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起尘颓,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤走触,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后疤苹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體互广,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了惫皱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片像樊。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖旅敷,靈堂內(nèi)的尸體忽然破棺而出生棍,到底是詐尸還是另有隱情,我是刑警寧澤媳谁,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布涂滴,位于F島的核電站,受9級(jí)特大地震影響晴音,放射性物質(zhì)發(fā)生泄漏氢妈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一段多、第九天 我趴在偏房一處隱蔽的房頂上張望首量。 院中可真熱鬧,春花似錦进苍、人聲如沸加缘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)拣宏。三九已至,卻和暖如春杠人,著一層夾襖步出監(jiān)牢的瞬間勋乾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工嗡善, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辑莫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓罩引,卻偏偏與公主長(zhǎng)得像各吨,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子袁铐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Zookeeper用于集群主備切換揭蜒。 YARN讓集群具備更好的擴(kuò)展性。 Spark沒有存儲(chǔ)能力剔桨。 Spark的Ma...
    Yobhel閱讀 7,269評(píng)論 0 34
  • Nosql概述 在介紹Redis之前屉更,首先先要介紹Nosql的概念。 互聯(lián)網(wǎng)架構(gòu)發(fā)展 在90年代的時(shí)候洒缀,計(jì)算機(jī)訪問...
    COKIDCC閱讀 689評(píng)論 0 1
  • 本文主要根據(jù)美團(tuán)的技術(shù)博客《常見性能優(yōu)化策略的總結(jié)》整理而來瑰谜。 代碼 之所以把代碼放到第一位,是因?yàn)檫@一點(diǎn)最容易引...
    你是妖怪吧閱讀 4,127評(píng)論 0 15
  • 今天看到一位朋友寫的mysql筆記總結(jié)芥玉,覺得寫的很詳細(xì)很用心恬试,這里轉(zhuǎn)載一下,供大家參考下抡草,也希望大家能關(guān)注他原文地...
    信仰與初衷閱讀 4,730評(píng)論 0 30
  • 代碼 之所以把代碼放到第一位砚哗,是因?yàn)檫@一點(diǎn)最容易引起技術(shù)人員的忽視龙助。很多技術(shù)人員拿到一個(gè)性能優(yōu)化的需求以后,言必稱...
    劉臻楓閱讀 491評(píng)論 0 7