最近所開發(fā)的系統(tǒng)中出現(xiàn)大量數(shù)據(jù)庫sleep狀態(tài)的空連接,于此同時通過Log發(fā)現(xiàn)系統(tǒng)中通過php的curl請求第三方API接口的反饋出現(xiàn)大量異常,不由得把2者聯(lián)系起來分析原因。日志反應第三方接口響應緩慢,并且結(jié)果為空,原因不明,但是能想象到php發(fā)出curl請求后一直等待連接返回,等待過程中數(shù)據(jù)庫連接開始sleep,直到curl超時,進程執(zhí)行完畢數(shù)據(jù)庫鏈接得以釋放。
轉(zhuǎn)載2個與php進程阻塞有關(guān)的文章
php+mysql+memcache實戰(zhàn)型技術(shù)測試(答案公布)
出兩個變態(tài)的題目,題目很變態(tài),但是都是實戰(zhàn)中遇到的真實案例,
1:我寫一個程序,既要使用mysql也要使用memcache,
第一行是 mysql_connect,第二行是memcache_connect
換過來寫,第一行是memcache_connect,第二行是mysql_connect
caoz發(fā)現(xiàn)實踐中這兩種寫法有很大的區(qū)別,區(qū)別在哪里?
2:我寫一個程序,使用了mysql,生成了一個頁面最后用 echo $html; 輸出
一種寫法是
mysql_close();
echo $html;
另一種是
echo $html;
mysql_close();
caoz發(fā)現(xiàn)實踐中這兩種區(qū)別很大,區(qū)別在哪里?
兩個全是實踐中發(fā)現(xiàn)并調(diào)整的案例。
---------------------------------------------半夜黑眼圈更新博客的分割線--------------------------------
caoz寫程序不是追求BT的人,caoz經(jīng)常和工程師強調(diào),不追求極端的技術(shù)體現(xiàn)或者技術(shù)炫耀,所以,如果讀者認為這里的題目是為了所謂的諸如某個寫法比某個寫法資源開銷小一點或者其他什么,那么這真不是caoz的本意。
這兩道題都是真實運營環(huán)境中遇到的典型案例,典型在哪里呢?就是當你遇到一個系統(tǒng)故障的時候,你怎么分析,怎么思考,怎么判斷多種關(guān)聯(lián)因素的影響,所以這個題目答出答不出并不重要,重要的是怎么思考系統(tǒng)彼此的關(guān)系。
一個典型的系統(tǒng)故障是, mysql 連接過多,或者說too many
connections,這個問題困擾了我們很久,如果這是因為索引導致的,或者因為數(shù)據(jù)并發(fā)請求導致的,定位到原因并不復雜,但是當上述問題解決后,詭異的現(xiàn)象發(fā)生了,數(shù)據(jù)庫幾乎沒有壓力,沒有阻塞進程,沒有慢查詢,但是mysql連接很多,而且都是sleep連接。此時,webserver的鏈接也很多,換句話說,因為php執(zhí)行阻塞,導致mysql鏈接無法迅速釋放,那么,php為什么會阻塞?
逐個斷點分析發(fā)現(xiàn),原來echo耽誤了最多時間。
這個事情讓caoz漲了點經(jīng)驗,之前從來不會認為echo是一個時間阻塞點(如果你在本機測試,你會認為其時間延遲幾乎是0),但是實例跟蹤發(fā)現(xiàn),echo實際上在我們的工作環(huán)境中,代表的網(wǎng)絡(luò)傳輸?shù)倪^程,換言之,會因為路由,帶寬的因素而等待,而此時,mysql的鏈接還在那里沒有釋放,是的,看到題目每個人都會想到,mysql_close要放在echo后面,但是為什么echo會耽誤時間,很少人會想到。當然,這個也與工作環(huán)境有關(guān),caoz只知道我們配置的webserver會有這種情況,是不是存在其他的配置模式,caoz沒有實測,不敢亂說,但是這里的經(jīng)驗是,mysql_close放到echo前面,大量的sleep鏈接會迅速減少。
echo并不耗費太多系統(tǒng)資源,但是會等待網(wǎng)絡(luò)傳輸,在高并發(fā)的網(wǎng)絡(luò)環(huán)境下,注意這一點對數(shù)據(jù)庫很有好處。
這個問題解決后,mysql健康了很多,但是偶然還會出現(xiàn)鏈接過多的問題,又困擾了很久,直到有一天,根據(jù)用戶反饋的一些錯誤信息,發(fā)現(xiàn)memcached服務器有不穩(wěn)定因素,原來是memcached流量過高產(chǎn)生阻塞,php進程等待鏈接,導致mysql鏈接大量等待,這是第一個題目的由來,其實這個題目本身沒有標準答案,但是應該有一個意識,當你在一個腳本中同時啟動A,B兩個鏈接,那么如果你不能保證這兩個鏈接是必然可靠的(通常是無法保障的),那么后者一旦阻塞,就會導致前者大量鏈接等待,而前者阻塞,通常不會影響后者。所以,這個答案取決于,哪個鏈接對你的應用更重要,以及哪個鏈接有更大的并發(fā)支撐性。
兩道題說到底就是一個意思,當遇到系統(tǒng)問題和故障的時候,多想想一些關(guān)聯(lián)的因素影響,多思考整個架構(gòu)響應先后過程的邏輯,數(shù)據(jù)庫連接過多,不一定是數(shù)據(jù)庫造成的,web鏈接過多,也不一定非要去優(yōu)化webserver,關(guān)聯(lián)因素可能才是根源,解決了根源,表象才會徹底解決。
------------------------------------------------------------------------------------------------------------------------------
文章二:【減少MySQL的Sleep進程有效方法】
經(jīng)常遇到很多朋友問到,他的MySQL中有很多Sleep進程,嚴重占用MySQL的資源,現(xiàn)在分析一下出現(xiàn)這種現(xiàn)象的原因和解決辦法:
1,通常來說,MySQL出現(xiàn)大量Sleep進程是因為采用的PHP的MySQL長鏈接數(shù)據(jù)庫方式,即使用了mysql_pconnect來打開鏈接數(shù)據(jù)庫,解決辦法就是使用“短”鏈接,即mysql_connect函數(shù)。
2,在使用mysql_connect短鏈接方式打開數(shù)據(jù)庫,每個頁面在打開數(shù)據(jù)庫后,執(zhí)行SQL完成,當頁面腳本結(jié)束的時候,這個MySQL連接會自動關(guān)閉并且釋放內(nèi)存。但仍然出現(xiàn)大量Sleep進程,可以看看網(wǎng)站是否存在以下幾個方面的問題。
A,硬盤上存在大量的靜態(tài)文件,或者WEB服務器負荷太重,在處理HTTP請求響應變得太慢,這樣也有可能導致出現(xiàn)大量Sleep進程,解決方法適當調(diào)整WEB服務參數(shù)和文件,一味的靜態(tài)或者緩存化網(wǎng)頁內(nèi)容并不是靈丹妙藥。
B,在網(wǎng)頁腳本中,有些計算和應用可能非常耗時,比如在0秒的時候打開數(shù)據(jù)庫執(zhí)行完一段SQL代碼后,網(wǎng)頁腳本隨即花了20秒鐘進行一段復雜的運算,或者是require了一個龐大的PHP文件(比如含有幾千個違規(guī)關(guān)鍵字的過濾函數(shù)),哪么這個時候在MySQL后臺看到的進程中,這個20秒的過程
MySQL并沒有做任何事情了,一直處于Sleep狀態(tài),直到這個頁面執(zhí)行完畢或者達到wait_timeout值(被強行關(guān)閉),優(yōu)化網(wǎng)頁腳本,盡量讓程序快速運行,或者在執(zhí)行這段耗時的運行過程中,執(zhí)行mysql_close把當前MySQL鏈接強行關(guān)閉。
C,在采集站中,MySQL中大量的Sleep進程這類現(xiàn)象尤其明顯(比如很多網(wǎng)友問道DeDeCMS的MySQL中出現(xiàn)大量Sleep),因為大部的采集器頁面在運行過程中,事先打開了一個MySQL鏈接(可能是為了驗證用戶權(quán)限等),然后開始使用file_get_contents之類的操作去獲取一個遠程的網(wǎng)頁內(nèi)容,如果這個遠程的站點訪問速度太慢,比如花了10秒時間才把網(wǎng)頁取回,哪么當前采集腳本程序就一直阻塞在這里,并且MySQL啥事也沒干,一直處于Sleep狀態(tài)。解決方法同上,在發(fā)出file_get_contents采集遠程網(wǎng)頁的時候,使用mysql_close強行關(guān)閉
MySQL的連接,等采集完成在適當需要的時候再重新mysql_connect即可。