開發的系統中出現大量資料庫sleep狀態的空串連,於此同時通過Log發現系統中通過php的curl請求第三方API介面的反饋出現大量異常,不由得把2者聯絡起來分析原因。日誌反應第三方介面響應緩慢,並且結果為空白,原因不明,但是能想象到php發出curl請求後一直等待串連返回,等待過程中資料庫連接開始sleep,直到curl逾時,進程執行完畢資料庫連結得以釋放。
1. php+mysql+memcache實戰型技術測試
出兩個變態的題目,題目很變態,但是都是實戰中遇到的真實案例,
1:我寫一個程式,既要使用mysql也要使用memcache,
第一行是 mysql_connect,第二行是memcache_connect
換過來寫,第一行是memcache_connect,第二行是mysql_connect
caoz發現實踐中這兩種寫法有很大的區別,區別在哪裡?
2:我寫一個程式,使用了mysql,產生了一個頁面最後用 echo $html; 輸出
一種寫法是
mysql_close();
echo $html;
另一種是
echo $html;
mysql_close();
caoz發現實踐中這兩種區別很大,區別在哪裡?
兩個全是實踐中發現並調整的案例。
caoz寫程式不是追求BT的人,caoz經常和工程師強調,不追求極端的技術體現或者技術炫耀,所以,如果讀者認為這裡的題目是為了所謂的諸如某個寫法比某個寫法資源開銷小一點或者其他什麼,那麼這真不是caoz的本意。
這兩道題都是真實運營環境中遇到的典型案例,典型在哪裡呢?就是當你遇到一個系統故障的時候,你怎麼分析,怎麼思考,怎麼判斷多種關聯因素的影響,所以這個題目答出答不出並不重要,重要的是怎麼思考系統彼此的關係。
一個典型的系統故障是, mysql 串連過多,或者說too many connections,這個問題困擾了我們很久,如果這是因為索引導致的,或者因為資料並發請求導致的,定位到原因並不複雜,但是當上述問題解決後,詭異的現象發生了,資料庫幾乎沒有壓力,沒有阻塞進程,沒有慢查詢,但是mysql串連很多,而且都是sleep串連。此時,webserver的連結也很多,換句話說,因為php執行阻塞,導致mysql連結無法迅速釋放,那麼,php為什麼會阻塞? 逐個斷點分析發現,原來echo耽誤了最多時間。
這個事情讓caoz漲了點經驗,之前從來不會認為echo是一個時間阻塞點(如果你在本機測試,你會認為其時間延遲幾乎是0),但是執行個體跟蹤發現,echo實際上在我們的工作環境中,代表的網路傳輸的過程,換言之,會因為路由,頻寬的因素而等待,而此時,mysql的連結還在那裡沒有釋放,是的,看到題目每個人都會想到,mysql_close要放在echo後面,但是為什麼echo會耽誤時間,很少人會想到。當然,這個也與工作環境有關,caoz只知道我們配置的webserver會有這種情況,是不是存在其他的配置模式,caoz沒有實測,不敢亂說,但是這裡的經驗是,mysql_close放到echo前面,大量的sleep連結會迅速減少。
echo並不耗費太多系統資源,但是會等待網路傳輸,在高並發的網路環境下,注意這一點對資料庫很有好處。
這個問題解決後,mysql健康了很多,但是偶然還會出現連結過多的問題,又困擾了很久,直到有一天,根據使用者反饋的一些錯誤資訊,發現memcached伺服器有不穩定因素,原來是memcached流量過高產生阻塞,php進程等待連結,導致mysql連結大量等待,這是第一個題目的由來,其實這個題目本身沒有標準答案,但是應該有一個意識,當你在一個指令碼中同時啟動A,B兩個連結,那麼如果你不能保證這兩個連結是必然可靠的(通常是無法保障的),那麼後者一旦阻塞,就會導致前者大量連結等待,而前者阻塞,通常不會影響後者。所以,這個答案取決於,哪個連結對你的應用更重要,以及哪個連結有更大的並發支撐性。
兩道題說到底就是一個意思,當遇到系統問題和故障的時候,多想想一些關聯的因素影響,多思考整個架構響應先後過程的邏輯,資料庫連接過多,不一定是資料庫造成的,web連結過多,也不一定非要去最佳化webserver,關聯因素可能才是根源,解決了根源,表象才會徹底解決。
2. 減少MySQL的Sleep進程有效方法
1,通常來說,MySQL出現大量Sleep進程是因為採用的PHP的MySQL長連結資料庫方式,即使用了mysql_pconnect來開啟連結資料庫,解決辦法就是使用“短”連結,即mysql_connect函數。
2,在使用mysql_connect短連結方式開啟資料庫,每個頁面在開啟資料庫後,執行SQL完成,當頁面指令碼結束的時候,這個MySQL串連會自動關閉並且釋放記憶體。但仍然出現大量Sleep進程,可以看看網站是否存在以下幾個方面的問題。
A,硬碟上存在大量的靜態檔案,或者WEB伺服器負荷太重,在處理HTTP請求響應變得太慢,這樣也有可能導致出現大量Sleep進程,解決方案適當調整WEB服務參數和檔案,一味的靜態或者緩衝化網頁內容並不是靈丹妙藥。
B,在網頁指令碼中,有些計算和應用可能非常耗時,比如在0秒的時候開啟資料庫執行完一段SQL代碼後,網頁指令碼隨即花了20秒鐘進行一段複雜的運算,或者是require了一個龐大的PHP檔案(比如含有幾千個違規關鍵字的過濾函數),哪麼這個時候在MySQL後台看到的進程中,這個20秒的過程 MySQL並沒有做任何事情了,一直處於Sleep狀態,直到這個頁面執行完畢或者達到wait_timeout值(被強行關閉),最佳化網頁指令碼,盡量讓程式快速運行,或者在執行這段耗時的運行過程中,執行mysql_close把當前MySQL連結強行關閉。
C,在採集站中,MySQL中大量的Sleep進程這類現象尤其明顯(比如很多網友問道DeDeCMS的MySQL中出現大量Sleep),因為大部的採集器頁面在運行過程中,事先開啟了一個MySQL連結(可能是為了驗證使用者權限等),然後開始使用file_get_contents之類的操作去擷取一個遠端網頁內容,如果這個遠端網站訪問速度太慢,比如花了10秒時間才把網頁取回,哪麼當前採集指令碼程式就一直阻塞在這裡,並且MySQL啥事也沒幹,一直處於Sleep狀態。解決方案同上,在發出file_get_contents採集遠程網頁的時候,使用mysql_close強行關閉 MySQL的串連,等採集完成在適當需要的時候再重新mysql_connect即可。