商城首頁卡爆了!!!

碼農談IT發表於2023-12-26

來源:蘇三說技術

前言

最近我們的商城系統出現了一個線上問題,使用者訪問商城首頁的時候要差不多20秒,才返回資料,可以說卡爆了。

到底怎麼回事呢?

1.案發現場

上週四晚上,我們有一個正常的迭代版本按照預期的時候上線。

本次迭代,我所涉及的功能,很快上線,並且測試透過了。

但沒法下班,因為專案組其他同事,還有線上問題在緊急處理。

我過去了解了一下情況,使用者訪問商城首頁的時候響應太慢了,要20秒才返回,有使用者投訴過來了。

進一步瞭解之後發現,造成這個問題的根本原因是redis伺服器掛了。

為什麼會掛呢?

是因為一次性往redis中儲存的資料太多了,導致記憶體不足。

這個商城系統部署到了阿里雲上,當時購買了1G的記憶體空間。

但由於這次上線,有個新功能,需要在商城首頁上,按不同的地區,推薦不同的商品。商品還要按不同的分類做區分。

原本商品只有幾十萬其實不多,但是按地區和分類做區分之後,儲存的資料量乘以了幾百倍,一下子佔用了大量的記憶體。

redis掛了為什麼會導致首頁慢呢?

答:因為程式碼中有業務邏輯,如果從redis中沒有獲取到資料,或者訪問redis失敗了,會從資料庫中獲取。雖說當時是晚上,使用者併發量不大,但是直接訪問資料庫,響應時間一下子下降了很多。商城首頁卡爆了!!!

2.如何快速解決問題?

目前的這套方案,先從redis中獲取資料,如果失敗了,再從資料庫中獲取。

現在的問題是:redis記憶體不足,臨時解決問題,只能加記憶體資源了。

因為加記憶體是最快的,直接加到了4G。如果要改程式碼,這個功能今天晚上可能沒法上線,之前購買的1G的資源確實有點小。

在阿里雲上redis加了記憶體之後,這個問題很快解決了,首頁訪問速度一下子提升。

但這不是問題的本質。

3.覆盤

第二天,我們開始覆盤問題。

發現之前的方案有點問題:

  1. 這次新增的推薦商品功能,儲存到redis的資料量太大了,把有些為null值的欄位,或者前端用不到的欄位也儲存到redis中了,資料結構設計不合理。
  2. redis出現問題之後的兜底方案有點問題,如果redis掛了,就直接訪問了資料庫,導致了使用者訪問慢的問題。如果是白天使用者併發量上來,可能會直接導致資料庫掛掉。

那麼,如何最佳化呢?

4.如何最佳化?

資料結構不合理的問題,可以透過調整資料結構解決,非常容易。

但如果redis掛了該如何處理呢?

4.1 頁面靜態化

其實對於商城首頁,最好的方案是做頁面靜態化處理。

但由於目前商城的使用者併發量,還不算很大,而且如果改成頁面靜態化,前後端的改動都太大了。

因此,這個方案最先被我們否定了。

4.2 加本地快取

為了防止後面再次出現商城首頁訪問慢的問題,可以在應用服務增加本地快取。

這樣不管redis以後能否正常執行,都不影響商城首頁的功能。

但需要考慮一個事情:應用服務的記憶體是否夠用?

顯然如果將所有推薦的商品資料,都儲存到應用服務的本地記憶體中,同樣可能會導致應用服務的記憶體不足的問題。

因此,直接加本地記憶體是不行的。

4.3 改成MongoDB

使用MongoDB替代Redis儲存資料。

Redis:資料全部存在記憶體,定期寫入磁碟,當記憶體不夠時,可以選擇指定的 LRU 演算法刪除資料。

MongoDB:資料存在記憶體,由 linux系統 mmap 實現,當記憶體不夠時,只將熱點資料放入記憶體,其他資料存在磁碟。

顯然MongoDB更適合儲存大批次的結構化的文件資料。

由於我們之前在做其他功能時,使用過MongoDB,它的效能也是挺不錯的。

但如果直接改成從MongoDB中獲取資料,商城首頁的訪問速度可能會有所下降。

4.4 本地快取 + MongoDB

上面說到過的加本地快取,和使用MongoDB都有各自的優缺點。

為什麼不把兩種方案結合一下呢?

在本地快取中儲存熱點資料,每隔5分鐘更新一次。

商城首頁卡爆了!!!使用者的請求過來,先從本地快取中獲取推薦商品資料,如果有則直接返回。

如果沒有,則從MongoDB獲取資料。

這樣可以解決效能的問題,也可以解決儲存大量的資料。

5.兜底方案

上面的說的本地快取 + MongoDB,基本可以解決redis掛了的問題。

但如果MongoDB掛了該怎麼辦呢?

這就需要有一套更好的兜底方案。

5.1 使用Apollo配置

如果MongoDB掛了,則直接返回Apollo配置中預設資料,預設是北京市東城區的推薦商品資料。

該配置由於在Apollo中,我們可以根據實際情況動態調整。

我們都知道Apollo可以配置成叢集模式,是高可用的,一般不容易掛掉。

但它有一個硬傷,就是如果資料並更了,需要人手動調整資料。

沒法保證資料的實時性。

5.2 再從資料庫訪問資料

如果從MongoDB中獲取資料失敗了,則直接從資料庫中獲取資料。

該方案從業務的角度來說,確實沒有問題。

但萬一真的出現這種情況,同樣會出現商城首頁訪問很慢的問題。

5.3 再從redis訪問資料

如果從MongoDB中獲取資料失敗了,則直接從redis中獲取資料。

Redis中只保留熱點商品資料。

這也是一種方案,不過要維護兩份資料:MongoDB一份,Redis一份。

可能會存在資料不一致的問題。

5.4 再加一個本地快取

在從資料庫獲取資料之後,再加一個本地快取,儲存預設的資料,即:北京市東城區的推薦商品資料。

這個本地快取,只有在第一次訪問資料庫時寫入,並且有效期是24小時。

相當於在MongoDB和資料庫之間,再加了一層預設的本地快取。

這樣就能解決資料庫訪問慢的問題。

6.最終方案

經過激烈討論之後,我們最終選擇的方案是:本地快取+MongoDB+本地預設快取+資料庫。商城首頁卡爆了!!!有時候選擇的某一個技術方案,是根據當前的業務發展,或者公司現狀,資金,資源,人手,技術能力等多方面考慮的。

很多技術問題都沒有最完美的解決方案,只有最適合的方案。

來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70024924/viewspace-3001512/,如需轉載,請註明出處,否則將追究法律責任。

相關文章