訪問數(shù)據(jù)庫總超時?這份避坑指南請收好
本文轉(zhuǎn)載自微信公眾號「數(shù)倉寶貝庫」,作者李玥 。轉(zhuǎn)載本文請聯(lián)系數(shù)倉寶貝庫公眾號。
01事故排查過程
電商公司大都希望做社交引流,社交公司大都希望做電商,從而將流量變現(xiàn),所以社交電商一直是熱門的創(chuàng)業(yè)方向。這個真實的案例來自某家做社交電商的創(chuàng)業(yè)公司。下面就來一起看下這個典型的數(shù)據(jù)庫超時案例。
從圣誕節(jié)平安夜開始,每天晚上固定十點到十一點這個時間段,該公司的系統(tǒng)會癱瘓一個小時左右的時間,過了這個時間段,系統(tǒng)就會自動恢復(fù)正常。系統(tǒng)癱瘓時,網(wǎng)頁和App都打不開,數(shù)據(jù)庫服務(wù)請求超時。
如圖1所示,該公司的系統(tǒng)架構(gòu)是一個非常典型的小型創(chuàng)業(yè)公司的微服務(wù)架構(gòu)。
圖1 典型的小型創(chuàng)業(yè)公司系統(tǒng)架構(gòu)
該公司將整個系統(tǒng)托管在公有云上,Nginx作為前置網(wǎng)關(guān)承接前端的所有請求。后端根據(jù)業(yè)務(wù),劃分了若干個微服務(wù)分別進行部署。數(shù)據(jù)保存在MySQL數(shù)據(jù)庫中,部分?jǐn)?shù)據(jù)用Memcached做了前置緩存。數(shù)據(jù)并沒有按照微服務(wù)最佳實踐的要求,進行嚴(yán)格的劃分和隔離,而是為了方便,存放在了一起。
這種存儲設(shè)計方式,對于一個業(yè)務(wù)變化極快的創(chuàng)業(yè)公司來說是比較合理的。因為它的每個微服務(wù),隨時都在隨著業(yè)務(wù)需求的變化而發(fā)生改變,如果做了嚴(yán)格的數(shù)據(jù)隔離,反而不利于應(yīng)對需求的變化。
開始分析這個案例時,我首先注意到的一個關(guān)鍵現(xiàn)象是,每天晚上十點到十一點這個時間段,是絕大多數(shù)內(nèi)容類App訪問量的高峰期。因為這個時間段很多人都會躺在床上玩手機,因此我初步判斷,這個故障可能與訪問量有關(guān)。圖2所示的是該系統(tǒng)每天各個時間段的訪問量趨勢圖,正好可以印證我的初步判斷。
圖2 系統(tǒng)訪問量
基于這個判斷,排查問題的重點應(yīng)該放在那些服務(wù)于用戶訪問的功能上,比如,首頁、商品列表頁、內(nèi)容推薦等功能。
在訪問量達到峰值的時候,請求全部超時。而隨著訪問量的減少,系統(tǒng)又能自動恢復(fù),因此基本上可以排除后臺服務(wù)被大量請求沖垮,進程僵死或退出的可能性。因為如果進程出現(xiàn)這種情況,一般是不會自動恢復(fù)的。排查問題的重點應(yīng)該放在MySQL上。
觀察圖3所示的MySQL服務(wù)器各時段的CPU利用率監(jiān)控圖,我們可以發(fā)現(xiàn)其中的問題。從監(jiān)控圖上可以看出,故障時段MySQL的CPU利用率一直是100%。這種情況下,MySQL基本上處于不可用的狀態(tài),執(zhí)行所有的SQL都會超時。在MySQL中,這種CPU利用率高的現(xiàn)象,絕大多數(shù)情況下都是由慢SQL導(dǎo)致的,所以需要優(yōu)先排查慢SQL。MySQL和各大云廠商提供的RDS(關(guān)系型數(shù)據(jù)庫服務(wù))都能提供慢SQL日志,分析慢SQL日志,是查找造成類似問題的原因最有效的方法。
圖3 MySQL服務(wù)器各時段的CPU利用率監(jiān)控圖
一般來說,慢SQL日志中,會包含這樣一些信息:SQL語句、執(zhí)行次數(shù)、執(zhí)行時長。通過分析慢SQL查找問題,并沒有什么標(biāo)準(zhǔn)的方法,主要還是依靠經(jīng)驗。
首先,我們需要知道的一點是,當(dāng)數(shù)據(jù)庫非常忙的時候,任何一個SQL的執(zhí)行都會很慢。所以并不是說,慢SQL日志中記錄的這些慢SQL都是有問題的SQL。大部分情況下,導(dǎo)致問題的SQL只是其中的一條或幾條,不能簡單地依據(jù)執(zhí)行次數(shù)和執(zhí)行時長進行判斷。但是,單次執(zhí)行時間特別長的SQL,仍然是應(yīng)該重點排查的對象。
通過分析這個系統(tǒng)的慢SQL日志,我首先找到了一條特別慢的SQL。以下代碼是這條SQL的完整語句:
- 1select fo.FollowId as vid, count(fo.id) as vcounts
- 2
- 3from follow fo, user_info ui
- 4
- 5where fo.userid = ui.userid
- 6
- 7and fo.CreateTime between
- 8
- 9str_to_date(?, '%Y-%m-%d %H:%i:%s')
- 10
- 11and str_to_date(?, '%Y-%m-%d %H:%i:%s')
- 12
- 13and fo.IsDel = 0
- 14
- 15and ui.UserState = 0
- 16
- 17group by vid
- 18
- 19order by vcounts desc
- 20
- 21limit 0,10
這條SQL支撐的功能是一個“網(wǎng)紅”排行榜,用于排列出“粉絲”數(shù)最多的前10名“網(wǎng)紅”。
請注意,這種排行榜的查詢,一定要做緩存。在上述案例中,排行榜是新上線的功能,由于沒有做緩存,導(dǎo)致訪問量高峰時間段服務(wù)卡死,因此增加緩存應(yīng)該可以有效解決上述問題。
為排行榜增加緩存后,新版本立即上線。本以為問題就此可以得到解決,結(jié)果到了晚高峰時間段,系統(tǒng)仍然出現(xiàn)了各種請求超時,頁面打不開的問題。
再次分析慢SQL日志,我發(fā)現(xiàn)排行榜的慢SQL不見了,說明緩存生效了。日志中的其他慢SQL,查詢次數(shù)和查詢時長的分布都很均勻,找不到明顯有問題的SQL。
于是,我再次查看MySQL服務(wù)器各時段的CPU利用率監(jiān)控圖,如圖4所示。
圖4 系統(tǒng)增加緩存后,MySQL服務(wù)器各時段的CPU利用率
把圖放大后,我又從中發(fā)現(xiàn)了如下兩點規(guī)律。
1)CPU利用率,以20分鐘為周期,非常有規(guī)律地進行波動。
2)總體的趨勢與訪問量正相關(guān)。
那么,我們是不是可以猜測一下,如圖5所示,MySQL服務(wù)器的CPU利用率監(jiān)控圖的波形主要由兩個部分構(gòu)成:參考線以下的部分,是正常處理日常訪問請求的部分,它與訪問量是正相關(guān)的;參考線以上的部分,來自某個以20分鐘為周期的定時任務(wù),與訪問量關(guān)系不大。
圖5 系統(tǒng)增加緩存后,MySQL服務(wù)器各時段的CPU利用率(附帶參考線)
排查整個系統(tǒng),并未發(fā)現(xiàn)有以20分鐘為周期的定時任務(wù),繼續(xù)擴大排查范圍,排查周期小于20分鐘的定時任務(wù),最后終于定位到了問題所在。
該公司App的首頁聚合了大量的內(nèi)容,比如,精選商品、標(biāo)題圖、排行榜、編輯推薦,等等。這些內(nèi)容會涉及大量的數(shù)據(jù)庫查詢操作。該系統(tǒng)在設(shè)計之初,為首頁做了一個整體的緩存,緩存的過期時間是10分鐘。但是隨著需求的不斷變化,首頁上需要查詢的內(nèi)容越來越多,導(dǎo)致查詢首頁的全部內(nèi)容變得越來越慢。
通過檢查日志可以發(fā)現(xiàn),刷新一次緩存的時間竟然長達15分鐘。緩存是每隔10分鐘整點刷新一次,因為10分鐘內(nèi)刷不完,所以下次刷新就推遲到了20分鐘之后,這就導(dǎo)致了圖5中,參考線以上每20分鐘一個周期的規(guī)律波形。由于緩存的刷新比較慢,導(dǎo)致很多請求無法命中緩存,因此大量請求只能穿透緩存直接查詢數(shù)據(jù)庫。圖9-5中參考線以下的部分,包含了很多這類請求占用的CPU利用率。
找到了問題的原因所在,下面就來進行針對性的優(yōu)化,問題很快就得到了解決。新版本上線之后,再也沒有出現(xiàn)過“午夜宕機”的問題。如
圖6所示,對比優(yōu)化前后MySQL服務(wù)器的CPU利用率,可以看出,優(yōu)化的效果非常明顯。
圖6 優(yōu)化前后MySQL服務(wù)器的CPU利用率對比
02如何避免悲劇重演
至此,導(dǎo)致問題的原因找到了,問題也得到了圓滿解決。單從這個案例來看,問題的原因在于,開發(fā)人員在編寫SQL時,沒有考慮數(shù)據(jù)量和執(zhí)行時間,緩存的使用也不合理。最終導(dǎo)致在訪問高峰期時,MySQL服務(wù)器被大量的查詢請求卡死,而無法提供服務(wù)。
作為系統(tǒng)的開發(fā)人員,對于上述問題,我們可以總結(jié)出如下兩點經(jīng)驗。
第一,在編寫SQL的時候,一定要小心謹(jǐn)慎、仔細評估,首先思考如下三個問題。
- SQL所涉及的表,其數(shù)據(jù)規(guī)模是多少?
- SQL可能會遍歷的數(shù)據(jù)量是多少?
- 如何盡量避免寫出慢SQL?
第二,能不能利用緩存減少數(shù)據(jù)庫查詢的次數(shù)?在使用緩存的時候,我們需要特別注意緩存命中率,應(yīng)盡量避免請求因為命中不了緩存,而直接穿透到數(shù)據(jù)庫上。
不過,我們無法保證,整個團隊的所有開發(fā)人員以后都不會再犯這類錯誤。但是,這并不意味著,上述問題就無法避免了,否則大企業(yè)的服務(wù)系統(tǒng)會因為每天上線大量的BUG而無法正常工作。實際情況是,大企業(yè)的系統(tǒng)通常都是比較穩(wěn)定的,基本上不會出現(xiàn)全站無法訪問的問題,這要歸功于其優(yōu)秀的系統(tǒng)架構(gòu)。優(yōu)秀的系統(tǒng)架構(gòu),可以在一定程度上,減輕故障對系統(tǒng)的影響。
針對這次事故,我在系統(tǒng)架構(gòu)層面,為該公司提了兩條改進的建議。
第一條建議是,上線一個定時監(jiān)控和殺掉慢SQL的腳本。這個腳本每分鐘執(zhí)行一次,檢測在上一分鐘內(nèi),有沒有執(zhí)行時間超過一分鐘(這個閾值可以根據(jù)實際情況進行調(diào)整)的慢SQL,如果發(fā)現(xiàn),就直接殺掉這個會話。
這樣可以有效地避免因為一個慢SQL而拖垮整個數(shù)據(jù)庫的悲劇。即使出現(xiàn)慢SQL,數(shù)據(jù)庫也可以在至多1分鐘內(nèi)自動恢復(fù),從而避免出現(xiàn)數(shù)據(jù)庫長時間不可用的問題。不過,這樣做也是有代價的,可能會導(dǎo)致某些功能,之前運行是正常的,在這個腳本上線后卻出現(xiàn)了問題。但是,總體來說,這個代價還是值得付出的,同時也可以反過來督促開發(fā)人員,使其更加小心謹(jǐn)慎,避免寫出慢SQL。
第二條建議是,將首面做成一個簡單的靜態(tài)頁面,作為降級方案,首頁上只要包含商品搜索欄、大的品類和其他頂級功能模塊入口的鏈接就可以了。在Nginx上實現(xiàn)一個策略,如果請求首頁數(shù)據(jù)超時,則直接返回這個靜態(tài)頁面的首頁作為替代。后續(xù)即使首頁再出現(xiàn)任何故障,也可以暫時降級,用靜態(tài)首頁替代,至少不會影響到用戶使用其他功能。
這兩條改進建議的實施都是非常容易的,不需要對系統(tǒng)進行很大的改造,而且效果也是立竿見影的。
當(dāng)然,這個系統(tǒng)的存儲架構(gòu)還有很多可以改進的地方,比如,對數(shù)據(jù)做適當(dāng)?shù)母綦x,改進緩存置換策略,將數(shù)據(jù)庫升級為主從部署,把非業(yè)務(wù)請求的數(shù)據(jù)庫查詢遷移到單獨的從庫上,等等,只是這些改進都需要對系統(tǒng)做出比較大的改動升級,需要從長計議之后再在系統(tǒng)后續(xù)的迭代過程中逐步實施。
03小結(jié)
本文分析了一個由于慢SQL導(dǎo)致網(wǎng)站服務(wù)器訪問故障的案例。在“破案”的過程中,我分享了一些很有用的經(jīng)驗,這些經(jīng)驗對于大家在工作中遇到類似問題時會有很大的參考作用。下面再來梳理一下這些經(jīng)驗。
1)根據(jù)故障時段出現(xiàn)在系統(tǒng)繁忙時這一現(xiàn)象,推斷出故障原因與支持用戶訪問的功能有關(guān)。
2)根據(jù)系統(tǒng)能在流量峰值過后自動恢復(fù)這一現(xiàn)象,排除后臺服務(wù)被大量請求沖垮的可能性。
3)根據(jù)服務(wù)器的CPU利用率曲線的規(guī)律變化,推斷出故障原因可能與定時任務(wù)有關(guān)。
在故障復(fù)盤階段,我們針對故障問題本身的原因,做了針對性的預(yù)防和改進,除此之外,更重要的是,在系統(tǒng)架構(gòu)層面也進行了改進,整個系統(tǒng)變得更加健壯,不至于因為某個小的失誤,就導(dǎo)致出現(xiàn)全站無法訪問的問題。
我為該系統(tǒng)提出的第一個建議是定時自動殺死慢SQL,原因是:系統(tǒng)的關(guān)鍵部分要有自我保護機制,以避免因為外部的錯誤而影響到系統(tǒng)的關(guān)鍵部分。第二個建議是首頁降級,原因是:當(dāng)關(guān)鍵系統(tǒng)出現(xiàn)故障的時候,要有臨時的降級方案,以盡量減少故障造成的不良影響。
這些架構(gòu)上的改進措施,雖然不能完全避免故障,但是可以在很大程度上減小故障的影響范圍,減輕故障帶來的損失,希望大家能夠仔細體會,活學(xué)活用。
關(guān)于作者:李玥,美團基礎(chǔ)技術(shù)部高級技術(shù)專家,極客時間《后端存儲實戰(zhàn)課》《消息隊列高手課》等專欄作者。曾在當(dāng)當(dāng)網(wǎng)、京東零售等公司任職。從事互聯(lián)網(wǎng)電商行業(yè)基礎(chǔ)架構(gòu)領(lǐng)域的架構(gòu)設(shè)計和研發(fā)工作多年,曾多次參與雙十一和618電商大促。專注于分布式存儲、云原生架構(gòu)下的服務(wù)治理、分布式消息和實時計算等技術(shù)領(lǐng)域,致力于推進基礎(chǔ)架構(gòu)技術(shù)的創(chuàng)新與開源。
本文摘編自《電商存儲系統(tǒng)實戰(zhàn):架構(gòu)設(shè)計與海量數(shù)據(jù)處理》,經(jīng)出版方授權(quán)發(fā)布。(ISBN:9787111697411)轉(zhuǎn)載請保留文章出處。