從CPU100%高危故障到穩定在10%:一個月的最佳化之旅,成功上線!

努力的小雨發表於2024-02-04

引言

經過三個月的開發,專案透過了所有測試並上線,然而,我們發現專案的首頁幾乎無法開啟,後臺一直髮生超時錯誤,導致CPU過度負荷。在這次專案開發過程中,我制定了一份詳細的技術最佳化方案。考慮到客戶無法提供機器硬體配置,我們只能從軟體方面尋找解決方案,以滿足客戶的預期。同時,我還準備了一個簡單的專案覆盤,如果你對此感興趣,也可以一起檢視。

image

初期最佳化

在進行第一次最佳化時,我們發現SQL的基本書寫存在問題。透過使用pinpoint工具,我們成功抓取了所有的SQL語句。然後,我們請一位對業務非常熟悉的人對所有的SQL進行了審查,主要是最佳化SQL書寫中的基本錯誤。由於開發人員的疏忽,導致了資料庫的全表查詢,但是由於測試資料庫的資料量不足,測試環境並沒有發現潛在的基礎SQL問題。經過第一輪SQL最佳化,現在所有的SQL語句已經得到了正確的修正。

經過壓力測試後,我們發現資料庫的CPU已經超負荷執行。為了進一步最佳化效能,我們決定進行SQL的詳細最佳化。在查詢表關聯的過程中,我們發現有很多欄位實際上已經在業務表中冗餘存在,因此無需再去關聯另外一張表。透過減少表的關聯操作,我們可以有效提高SQL的執行效率。

中期最佳化

由於測試資料量不足且業務關聯性較差,我們需要申請將正式資料庫中涉及的表資料遷移至測試資料庫,以模擬正式環境進行壓力測試。

經過初步討論,我們認為業務表的資料量很大可能導致了SQL查詢的緩慢和CPU效能的佔用。為了最佳化這個問題,我們決定根據所有報表涉及的表及其欄位,掃描業務增量資料並抽離出一個小表用於報表業務查詢。

在進行資料遷移的過程中,我們要注意的是,除非資料量很小的表,不要直接將其抽離到小表中。相反,我們需要引入一箇中間表來提前緩衝資料。如果直接去除中間表,可能會導致對業務表的鎖定,從而降低業務操作的效能。

注意點:在上線之前,務必確保準備好初始化表資料和相應的表索引。丟失一個都會導致生產資料庫的CPU跑滿。

image

後期最佳化

由於專案需要進行時間範圍查詢,當選擇以月為單位時,首頁在壓測併發下未能達到預期要求。因此,我們進行了方案升級,引入了結果表的概念。具體而言,我們按照月份的最小統計範圍提前生成結果統計表。也有人提出在此階段涉及到的業務介面中加入一步更新結果表中的某一個指標欄位的操作。然而,這樣做會導致業務之間的耦合性非常嚴重,可能隨時影響正常業務的操作。

在中期之前,我們已經想到了使用中間表->小表的方式來進行資料處理。因此,我們能夠準確地瞭解當前資料的變化情況,並根據變動的欄位值進行相應的操作,對相關的指標欄位進行增加或減少的操作。

為了解決由於多個小表同時更新統計表欄位而導致的行鎖問題,我們採取了進一步的最佳化措施,將原統計表拆分為兩張表,但仍保持主鍵一致。這樣做的好處是減少了不同小表同時更新同一行資料的情況,但需要注意的是指標欄位是有所不同的。

雖然此時也滿足了壓測要求,但是仍然需要考慮到此專案報表的風險會影響到業務資料庫的正常操作,所以將其切換到另外一個小型資料庫來專門提供其查詢。就算出現問題也不會影響到業務端的正常操作。

注意點:在進行當月上線之前,需要提前對統計表的各個指標進行初始化。這樣可以確保資料的準確性和完整性。

上線

經過上一期的系統最佳化,我們成功滿足了預期的壓力測試結果,所有按鈕和報表功能均可以正常點選和檢視。值得一提的是,資料庫的CPU效能一直保持在10%以下,顯示了良好的穩定性。

image

說明:有毛刺現象是因為這個小型資料庫在進行同步大表資料導致的。其實,關於結果集這種方法,在前期已經有過提出,但是我當時選擇了放棄,因為存在許多不確定性因素,可能導致指標值和實際值有一定的差異。然而,目前來看,至少可以滿足壓測和使用的要求。

專案覆盤

以上只是我在技術方面的參與和最佳化過程的簡要描述。接下來,我將作為一個非程式設計師身份來對整個專案進行一個簡單覆盤,從一開始的專案參與到需求統計指標的制定,我一直都在其中參與,並且對這個模組非常熟悉。當第三方提出無法實現或者實現上存在困難的問題時,我通常會舉例說明,因為第三方並不會像程式設計師一樣從技術角度考慮問題。在我看來,到目前為止,一切都沒有問題,因為我們已經仔細審查了每個指標,確定了哪些問題需要第三方進一步研究。

下一步就有問題了,整個專案的需求我是最瞭解的,但是由於我並不參與專案的開發,導致專案經理對於某一個小點根本不瞭解。因此,其他人經常來找我諮詢,並且直到專案接近尾聲時,當意識到無法滿足客戶的要求,才急忙找我參與開發。這是其中一個存在的問題。

讓我來詳細說明一下我的問題。在整個開發過程中,我的問題也逐漸顯露出來,那就是為什麼只有我一個人瞭解這個問題,而其他人卻一無所知呢?原因是因為我一直在負責這個模組的開發,但是我並沒有將相關的細節文件化,導致所有的細節都只存在於我的腦海中。因此,其他人只能不斷地向我請教,才有可能瞭解這些細節。然而,這種依賴性是需要避免的,儘管我自己也很不喜歡寫文件。

在專案中期時,我聽到最多的抱怨之一是關於指標統計的問題。有些指標統計了多一個數,而其他指標則統計了少一個數。儘管開發工作基本上已經完成,但在核對各個數字時卻出現了問題。然而,直到後期才發現這些問題其實是由於SQL編寫不當所導致的。在對資料庫進行操作時,我們可能沒有考慮到一些邊界情況或者特殊情況,導致了資料統計的不準確性。另外,還有可能是測試資料庫中的資料基本都是髒資料,也就是不符合我們預期的資料,這也對統計結果造成了一定的影響。

image

在專案的後期階段,由於排期已經到達客戶可接受的最低底線,大家都非常著急地制定最佳化方案。這時候我才被真正拉過來參與,整個過程耗時將近一個月,沒有休息的時間。出方案的人也開始費盡心思地想辦法,但前提是他們對業務一無所知。他們也沒有花時間靜下心來好好思考,只管出方案,而我們這邊需要再評估一下。我們花了整整一天(24小時)的時間來進行評估,具體方案和技術最佳化的細節我就不詳細介紹了,總之最終的方案就是我之前提到的各種技術最佳化。

其實,我在這個過程中發現了一個規律,前期一直在進行指標分析,而後期的重點則是確保系統能夠滿足壓力測試的要求,即客戶的預期。如果下次你有類似的需求,一定要確保在系統正常執行的基礎上,再去最佳化指標的準確性,就像這次專案,當初上線時連頁面都無法開啟,資料庫的CPU直接達到了極限,根本沒有時間去核對報表指標了。簡而言之,我們需要先滿足最低要求,然後逐步進行最佳化。

相關文章