廢話少說颈娜,先看代碼
def add_end(L=[]):
L.append('END')
return L
這段python代碼看起來有什么問題嗎剑逃?看起來是不是也沒啥問題浙宜,L是個(gè)入?yún)ⅲ琹ist形式的蛹磺,不傳的話有個(gè)空list作為默認(rèn)值粟瞬,業(yè)務(wù)體里面對(duì)L進(jìn)行了內(nèi)容追加,并把追加后的L對(duì)象返回萤捆。業(yè)務(wù)很簡單
但就是這樣的一個(gè)看起來人畜無害的代碼讓一個(gè)網(wǎng)站在痛苦中度過了一個(gè)月的時(shí)間裙品,同時(shí)流失了大量的用戶,讓我們來看一下親歷者回憶當(dāng)時(shí)的情況吧
Digg's v4 launch: an optimism born of necessity.
不想看英文的同學(xué)可以全文翻譯或者看下面節(jié)選的關(guān)鍵內(nèi)容:
背景:digg是一個(gè)以科技為主的新聞?wù)军c(diǎn)鳖轰,用戶可提交新聞?dòng)鑔igg清酥,通過digg機(jī)制顯示于digg首頁上。十幾年前web2.0的弄潮兒蕴侣,巔峰時(shí)期月訪問量在3000W以上焰轻,于12年沒落,至今依然保持運(yùn)營昆雀。這個(gè)事情發(fā)生在18年辱志,讓這個(gè)本不富裕的網(wǎng)站雪上加霜
18年Digg到推出V4,從V3.5到V4已經(jīng)持續(xù)開發(fā)了兩年時(shí)間狞膘,整個(gè)公司希望通過V4讓Digg重回互聯(lián)網(wǎng)第一梯隊(duì)揩懒。他們甚至提前準(zhǔn)備了慶祝的香檳(半場(chǎng)開香檳,傳統(tǒng)藝能了)
他們準(zhǔn)備了升級(jí)的方案挽封,但沒有回滾計(jì)劃(這flag立的已球,銘記墨菲定律)
升級(jí)之后的網(wǎng)站并沒有按計(jì)劃展現(xiàn),而是因?yàn)楹笈_(tái)的高負(fù)載導(dǎo)致大多數(shù)頁面都無法加載辅愿。中途Digg團(tuán)隊(duì)觀察到了數(shù)據(jù)庫(Cassandra)和緩存(memcache)負(fù)載高智亮,擴(kuò)容了一次,但還是沒有徹底解決問題点待。特別是V4中最重要的MyNews頁面阔蛉,這是這個(gè)版本中最亮眼的功能,以及用戶的默認(rèn)頁面癞埠,然而這個(gè)頁面卻一直都在報(bào)錯(cuò)
Digg團(tuán)隊(duì)后來把初始頁面改成了TopNews状原,來保證用戶不至于在登錄的時(shí)候就看到錯(cuò)誤頁面,但問題還是沒有得到解決
兩天后Digg團(tuán)隊(duì)把數(shù)據(jù)從Cassandra遷移到了redis中苗踪,提升了數(shù)據(jù)庫的訪問性能颠区。問題得到了緩解,但還是需要每隔4小時(shí)重啟一次服務(wù)
一個(gè)月后通铲,問題終于被查出來了瓦呼,Digg的API服務(wù)是一個(gè)Python服務(wù),類似于網(wǎng)關(guān)層,作為網(wǎng)關(guān)需要查詢用戶信息央串,這個(gè)用戶信息的查詢函數(shù)的參數(shù)形式就是上面這種列表形式(當(dāng)然具體代碼長啥樣子咱也不知道,只知道形式)碗啄,就是這樣的代碼形式導(dǎo)致了這一個(gè)月的問題
OK质和,讓我們執(zhí)行一次上面的函數(shù)
>>> add_end()
['END']
看著沒問題哈,再執(zhí)行個(gè)帶參數(shù)的
>>> add_end([1, 2, 3])
[1, 2, 3, 'END']
看著也沒問題哈稚字,讓我們?cè)賵?zhí)行一次無參的
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']
嗯饲宿?為什么會(huì)這樣?默認(rèn)參數(shù)是[]胆描,但是函數(shù)似乎每次都“記住了”上次添加了'END'后的list
原因解釋如下:
Python函數(shù)在定義的時(shí)候瘫想,默認(rèn)參數(shù)L的值就被計(jì)算出來了,即[]昌讲,因?yàn)槟J(rèn)參數(shù)L也是一個(gè)變量国夜,它指向?qū)ο骩],每次調(diào)用該函數(shù)短绸,如果改變了L的內(nèi)容车吹,則下次調(diào)用時(shí),默認(rèn)參數(shù)的內(nèi)容就變了醋闭,不再是函數(shù)定義時(shí)的[]了
因此在大量用戶訪問Digg的時(shí)候窄驹,L中的默認(rèn)列表會(huì)越來越大,而且伴隨著并發(fā)訪問证逻,這是一個(gè)二次以上多項(xiàng)式級(jí)別的增長乐埠,很快就會(huì)吃滿內(nèi)存,導(dǎo)致服務(wù)不可用
代碼和解釋參考囚企,里面也講了如何解決:廖雪峰:函數(shù)的參數(shù)
這樣的問題的確很隱蔽丈咐,甚至有的時(shí)候反直覺(可能是對(duì)于不熟悉的人來說),這時(shí)候熟練使用一些靜態(tài)代碼檢查的工具就顯得尤為必要洞拨。這些別人已經(jīng)踩過的坑很多已經(jīng)總結(jié)在了這些工具里扯罐,即使他無法確定這一定是個(gè)BUG,也會(huì)給研發(fā)人員一些危險(xiǎn)編碼方式的提示烦衣。