? 海底总动员1海报:聊城網站建設分享MYSQL系統分析實戰 - 海底总动员国语版高清
當前位置:海底总动员国语版高清» 網站建設 » 聊城網站建設分享MYSQL系統分析實戰
聊城網站建設分享MYSQL系統分析實戰
發布日期:2017-01-13 作者:聊城恒創網絡公司提供聊城網站建設,網站設計制作,聊城網站推廣優化,聊城微營銷,聊城網站托管

上班了上班了,干活了干活了。

 

那個,其實,我很多年沒碰過技術了,但還是覺得有必要把之前一些處理過的技術問題拿出來,其實每個問題,都是很小的問題,如果單獨說原因和答案都非常簡單,但關鍵是,遇到問題的思考方式和分析方法。

 

依然是,高手請無視,針對一些初入技術職場的童鞋,希望能對各位遇到問題時候的思考方式有所幫助。

 

案例1:詭異的鏈接過多

 

當時情況是這樣,突然有一天,數據庫出現鏈接過多錯誤,導致網站報錯。 熟悉mysql并操作過高并發系統的朋友知道,數據連接過多屬于很常見的問題。但當時的情況是,訪問量并不在高峰,按理說不應該有這樣的問題。

 

看了一下數據庫服務器的負載,很低,并不存在cpu或內存跑滿的問題。

慢查詢日志沒有異常的SQL,更沒有鎖表。

 

于是就進入數據庫做一下 show processlist的查詢。

有些朋友可能會問,鏈接過多你還能看show processlist么,那個,mysql里root比普通用戶多一個鏈接許可,所以,記得程序切忌用root鏈接,保留一個給系統分析師用。

 

意外發現,幾乎所有的SQL停留在sleep狀態,而且很多鏈接都持續了好幾秒,甚至十幾秒。

 

這里說明一下,如果是用數據中間件鏈接池來操作,從中間件到數據庫存在固定數字的sleep鏈接是正常的,但從程序端到中間件,除非你是長連接,并且需要保持數據庫頻繁操作的應用,否則,通常不建議數據庫保持連接,也就是不應該出現太多sleep操作。

 

我們的場景就是普通的web應用,php程序而已,都是短鏈接,按理說,程序執行完就應該釋放的,所以這個問題就有點意外。

 

當然,這個和代碼的設計也有關系,因為系統用的開源軟件改寫的,涉及數據庫操作還是蠻多的,一般情況下,數據庫操作完應該及時關閉,但由于一般認為php代碼執行時間很短,所以在代碼架構有點復雜的情況下,很多都是默認整個程序執行完再關閉。那么現在問題來了,到底php發生了什么問題。

 

我們去web服務器,看日志,發現訪問量并沒有異常,也沒有針對我們的攻擊行為,但確實很多php程序執行時間較長,web連接數也明顯多于異常,即便是數據庫重啟,問題依然會重現,那么這時候,我們工程師就在最常用的php代碼里設置斷點,去看代碼到底卡在哪個環節上執行時間很長,結果,發現是我們的一個非常重要的常識盲點。原來執行時間最長的,是在最后代碼數據都執行完,輸出執行 echo 的環節。

 

在本地做性能測試,壓力測試的時候,我們知道echo 這種語句是基本沒有開銷的,也不太可能成為一種負載的來源,但這下我們明白了,echo原來不僅僅是php執行輸出,也包含了網絡傳輸的時間開銷。只有客戶端接收到傳輸內容后,echo執行才結束。

 

而那天的問題,其實是因為同機房有其他公司服務器被Ddos,導致機房出口擁堵,按理說這只是websever的問題,但因為webserver本身有輪詢機制,而且設置的連接數較大,雖然訪問較慢,但沒有崩潰,而因為php代碼里mysql鏈接沒有及時釋放,在php執行echo的時間等待較長,導致mysql鏈接過多崩潰。

 

知道這個問題,解決就簡單了,因為開源系統封裝了輸出template的對象,我們就在這個對象執行的時候,先執行mysql_close(); 這樣只改了一行代碼,問題就解決了。

 

但后來發現出了bug,bug的理由很無厘頭,居然部分template 的偽碼里有數據庫操作,但這個問題解決也簡單,因為畢竟這樣的場景很少, 而且mysql對象也被封裝了,我們就在query方法里加了一行代碼,如果沒有數據庫連接,就重建一個。 這樣,這個重建過程只出現在極少數template里有mysql操作的場景,對整體系統基本沒有性能干擾。

 

這個案例說來挺簡單,就是數據庫連接沒有及時釋放造成的,但因為觸動了一個思維盲區,所以印象深刻。

 

線上的程序做斷點日志分析是最常用的分析詭異問題的方法?;詼系閎罩痙治?,我們可以通過類似二分法,逐步遞進直到精確定位具體到每一行代碼的執行時間開銷。

 

這里還要提醒一個常見問題,線上環境很多問題是在測試環境里很難重現的,所以遇到詭異問題,應該可以在線上做一些日志分析和代碼的調試,當然這樣可能會有一定的風險,但很多公司的流程和規范,開發工程師只能在線下測試性能和壓力承受能力,針對線上很多現實的問題沒有辦法完整實測。

 

大公司可能會把測試環境做的更好更規范,以及有更有經驗的工程師和分析師來解決問題,但創業公司,我建議要給程序員和分析人員一些線上應急處理的權限,否則真的會束手無策,經驗值都是靠犯錯和解決問題來積累的。

 

案例2:看似正常的負載過高

 

當時有個新業務數據增長很快,該業務的數據庫服務器每天處理數百萬次數據查詢請求,uptime比較高,經常在5-6的樣子,cpu負荷較重,運維負責人就發郵件,申請更換更好的服務器,增加資源。

 

按理說,這是個合理請求,負載也確實很高,業務也確實增長,但我這個人天性財迷摳門,總覺得這個數字不應該是極限,就登錄到數據庫服務器看了一下,很簡單,我的方法就是先刷show processlist,連續刷幾遍,看數據庫都在執行啥,開銷都集中在什么狀態,這一看還真就發現問題了,居然經??吹接行﹎ysql進程停留在 storing result to query cache 上。

 

這事我就納悶了,因為按常規,這個狀態應該是基本沒有時間開銷的,也就是show processlist看到是小概率事件的。

 

所以就要驗證一下,執行 set profiling=1,然后從show processlist復制一條執行一次,然后執行 show profiles for query 1; 結果意外發現,常規來說執行開銷最大的sending data (這個開銷可不是輸出數據哦,其實是io尋址)只有0.002秒,而 storing result to query cache 卻執行了 0.005秒的樣子,千分之五秒,一般人可能就無視了吧,但整個SQL執行不到0.01秒,這個開銷比例蠻大的了。

 

那個,其實這個問題的責任者呢,是我自己,我覺得query cache是個好東西啊,所以開始配置服務器的時候,還是我自己做的配置,因為服務器內存夠大,我就把query cache設置的比較大,結果SQL的反饋結果內容較多的情況下,就出現了query cache的碎片化比較嚴重,反而導致了query cache存儲額外的開銷,我在數據庫里直接操作將query cache內容重置的命令,再執行這個SQL,用profiling去分析,發現這個開銷就沒有了,負載瞬間顯著下降了60%左右。

 

然后我跟運維負責人說,半夜沒人的時候把數據庫的啟動參數,query cache那塊設置回默認值,重啟一下數據庫,于是就沒再追加預算和服務器投入。

 

這個案例本身是我自己的烏龍,因為沒有明確理解query cache的讀取和存儲邏輯,自以為是的調高了參數,在SQL返回值較大的情況下,導致了嚴重碎片化,帶來了額外的開銷,雖然每次開銷都極其微小,但由于系統的請求頻次非常高,所以系統不必要的負載就比較大。

 

那么這個案例里,需要分享的方法是,showprocesslist+一定的敏感度,再配合用set profiling去分析具體的開銷,是非常重要的一種分析查詢性能的方法。

 

案例3:io性能的優化案例

 

這個案例又是我的錯,唉,我發現我犯的錯誤還是蠻多的,不過我們工程師解決方案非常經典,所以也列在這里以供參考。

 

還是一個非常高并發的業務場景,最開始呢,為了達到查詢的最優化,數據結構還是我設計的,使用了復合索引,確保每次查詢的索引命中率極高,但這個業務場景有一個問題,就是除了查詢請求很高之外,數據的插入請求基本上是同頻次的。(大部分場景都是數據插入后隨之查詢,個別會有單獨查詢場景),所以插入請求巨大,數據庫的io壓力特別大。

 

結果我們工程師也是受到我的影響吧,摳門的很,也是盡可能在有限資源下挖潛。結果怎么做的呢? 說來簡單,索引降級,把兩個字段的復合索引降到單鍵索引了。

 

單純從查詢而言,這一降級其實是犧牲了效率,但是犧牲的并不大,但從更新而言,從復合索引降級到單鍵索引,索引更新的io負載就有了明顯的降低,由于查詢的負載開銷遠低于更新的負載開銷,所以這一降級,在查詢與更新同頻的場景下,就變得效果特別好。

 

這個案例需要分享的經驗是,索引的建立,不但要考慮查詢的語句,更新的語句,也要考慮業務場景中相關的頻次,在更新頻次遠低于查詢頻次時,和更新頻次與查詢頻次相當時,同樣的數據結構,同樣的SQL語句,可能索引的設計方案會有重大的調整和改變。

 

 

 

 

Mysql也有了很多的版本迭代,很多之前遇到的問題和瓶頸也許現在已經在系統中順暢解決,但我覺得,一些思路和方法依然值得分享。

 

當然,這些都不是什么高大上的技術和解決方案,都是實戰中,屌絲創業團隊面臨一些實際問題的響應和處理能力。很多草根創業團隊,其實都是栽倒類似這樣的問題上的。

?