vivo 全球商城:商品系統架構設計與實踐

vivo網際網路技術發表於2021-11-08

一、前言

隨著使用者量級的快速增長,vivo官方商城v1.0的單體架構逐漸暴露出弊端:模組愈發臃腫、開發效率低下、效能出現瓶頸、系統維護困難。

從2017年開始啟動的v2.0架構升級,基於業務模組進行垂直的系統物理拆分,拆分出來業務線各司其職,提供服務化的能力,共同支撐主站業務。

商品模組是整個鏈路的核心,模組的增多嚴重影響系統的效能,服務化改造勢在必行。

本文將介紹vivo商城商品系統建設的過程中遇到的問題和解決方案,分享架構設計經驗。

二、商品系統演進

將商品模組從商城拆分出來,獨立為商品系統,逐漸向底層發展,為商城,搜尋,會員、營銷等提供基礎標準化服務。

商品系統架構圖如下:

前期商品系統比較雜亂,包含業務模組比較多,如商品活動業務、秒殺業務,庫存管理,隨著業務的不斷髮展,商品系統承載更多的業務不利於系統擴充套件和維護。

故思考逐漸將商品業務逐漸下沉並作為最底層、最基礎的業務系統,併為眾多呼叫方提供高效能的服務,下面介紹商品系統的升級歷史。

2.1 商品活動、贈品剝離

隨著商品活動的不斷增多,玩法多樣,同時與活動相關的額外屬性也相應增加,這些都並不是與商品資訊強關聯,更偏向於使用者營銷,不應該與核心商品業務耦合在一起,故將其合併入商城促銷系統。

贈品不僅僅是手機、配件,有可能會是積分、會員等,這些放在商品系統都不合適,也不屬於商品模組的內容,故同步將其合併入商城促銷系統。

2.2 秒殺獨立

眾所周知,秒殺活動的特點是:

  • 限時:時間範圍很短,超過設定的時間就結束了

  • 限量:商品數量很少,遠低於實際庫存

  • 訪問量大:價格低,可以吸引非常多的使用者

基於以上特性,做好一個秒殺活動不是一蹴而就,由於系統資源共享,當突發的大流量衝擊會造成商品系統其他業務拒絕服務,會對核心的交易鏈路造成阻塞的風險,故將其獨立為單獨的秒殺系統,單獨對外提供服務。

2.3 代銷系統成立

我們商城的主要銷售品類還是手機以及手機配件等,商品的品類比較少,為了解決非手機商品品類不豐富的問題,運營考慮與知名電商進行合作,期望引入更多的商品品類。

為了方便後續擴充套件,以及對原有系統的不侵入性,我們經過考慮專門獨立出一個子系統,用於承接代銷業務,最後期望做成一個完備平臺,後續通過提供開放API的方式讓其他電商主動接入我們業務。

2.4 庫存剝離

庫存管理的痛點:

  • 由於我們的庫存都是到商品維度,僅僅一個欄位標識數量,每次編輯商品都需要為商品調整庫存,無法動態實現庫存管理;

  • 同時營銷系統也有自己活動庫存管理機制,入口分散,關聯性較弱;

  • 可售庫存和活動庫存管理的依據都是實際庫存,造成容易配置錯誤。

基於以上痛點,同時為了更方便運營管理庫存,也為未來使用實際庫存進行銷售打下基礎,我們成立庫存中心,並提供以下主要功能:

  • 與ecms實際庫存進行實時同步;

  • 可以根據實際庫存的倉庫分佈情況,計算商品的預計發貨倉庫和發貨時間,從而計算商品預計送達時間;

  • 完成低庫存預警,可以根據可用庫存、平均月銷等進行計算,動態提醒運營訂貨。

三、挑戰

作為最底層的系統,最主要的挑戰就是具備穩定性,高效能,資料一致性的能力。

3.1 穩定性

  • 避免單機瓶頸:根據壓測選擇合適的節點數量,不浪費,同時也能保證溝通,可以應對突發流量。

  • 業務限流降級:對核心介面進行限流,優先保證系統可用,當流量對系統壓力過大時將非核心業務進行降級,優先保證核心業務。

  • 設定合理的超時時間:對Redis、資料庫的訪問設定合理超時時間,不宜過長,避免流量較大時導致應用執行緒被佔滿。

  • 監控&告警:日誌規範化,同時接入公司的日誌監控和告警平臺,做到主動發現問題並及時。

  • 熔斷:外部介面接入熔斷,防止因為外部介面異常導致本系統受到影響。

3.2 高效能

多級快取

為了提升查詢速度,降低資料庫的壓力,我們採用多級快取的方式,介面接入熱點快取元件,動態探測熱點資料,如果是熱點則直接從本地獲取,如果不是熱點則直接從redis獲取。

讀寫分離

資料庫採用讀寫分離架構,主庫進行更新操作,從庫負責查詢操作。

介面限流

接入限流元件, 直接運算元據庫的介面會進行限流,防止因為突發流量、或者不規範呼叫導致資料庫壓力增加,影響其他介面。

不過早期也踩過一些坑:

1、商品列表查詢造成redis key過多,導致redis記憶體不夠的風險

由於是列表查詢,進行快取的時候是對入參進行hash,獲取唯一的key,由於入參商品較多,某些場景下入參是隨時變化的,根據排列組合,會造成基本每次請求都會回源,再快取,可能造成資料庫拒絕服務或者redis記憶體溢位。

方案一:迴圈入參列表,每次從redis獲取資料,然後返回;

這個方案解決了key過多導致記憶體溢位的問題,但是很明顯,它增加了很多的網路互動,如果有幾十個key,可想而知,對效能會有不小的影響,那有什麼其他辦法能減少網路互動呢,下面我們看方案二。

方案二:我們通過對原有的Redis 元件進行增強,由於Redis叢集模式不支援mget,故我們採用pipeline的方式實現,先根據key計算出其所在的slot,然後聚合一次性提交,這樣每個商品資料只需快取一次即可,同時採用mget也大大提升了查詢速度。

這就即解決了key值過多的問題,也解決了方案一中多次網路互動的問題,經過壓測對比,方案二比方案一效能提升50%以上,key越多,效果越明顯。

2、熱點資料,導致redis單機瓶頸

商城經常有新品釋出會,釋出會結束後會直接跳轉到新品商詳頁,此時新品商詳頁就會出現流量特別大且突發、資料單一,這就導致Redis節點負載不平衡,有些10%不到,有些達到90%多,而一些常規的擴容是沒有效果的。

針對熱點問題我們有以下解決方案:

  • key的雜湊,將key分散到不同的節點

  • 採用本地快取的方式

開始我們採用的是基於開源的Caffeine完成本地快取元件,本地自動計算請求量,當達到一定的閥值就快取資料,根據不同的業務場景快取不同的時間,一般不超過15秒,主要解決熱點資料的問題。

後來替換成我們自己研發的熱點快取元件,支援熱點動態探測,熱點上報,叢集廣播等功能。

3.3 資料一致性

1、對於Redis的資料一致性比較好解決,採用“Cache Aside Pattern”:

對於讀請求採用先讀快取,命中直接返回,未命中讀資料庫再快取。對於寫請求採用先運算元據庫,再刪除快取。

2、由於庫存剝離出去,維護入口還是在商品系統,這就導致存在跨庫操作,平常的單庫事務無法解決。

開始我們採用異常捕獲,本地事務回滾的方式,操作麻煩點,但也能解決這個問題。

後來我們通過開源的seata完成分散式事務元件,通過改寫程式碼引入公司的基礎元件,目前已經接入使用。

四、總結

本篇主要介紹商城商品系統如何進行拆分、並慢慢下沉為最基礎的系統,使其職責更加單一,能夠提供高效能的商品服務,並分享在此過程中遇到的技術問題和解決方案,後續會有庫存系統的演進歷史、分散式事務相關內容,敬請期待。

作者:vivo官網商城開發團隊-Ju Changjiang

相關文章