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

一、前言

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

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

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

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

二、商品系統演進

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

商品系統架構圖如下:

圖片

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

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

2.1 商品活動、贈品剝離

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

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

2.2 秒殺獨立

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

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

2.3 代銷系統成立

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

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

2.4 庫存剝離

庫存管理的痛點:

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

三、挑戰

作爲最底層的系統,最主要的挑戰就是具備穩定性,高性能,數據一致性的能力。

3.1 穩定性

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% 多,而一些常規的擴容是沒有效果的。

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

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

後來替換成我們自己研發的熱點緩存組件,支持熱點動態探測,熱點上報,集羣廣播等功能。

3.3 數據一致性

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

對於讀請求採用先讀緩存,命中直接返回,未命中讀數據庫再緩存。對於寫請求採用先操作數據庫,再刪除緩存。

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

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

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

四、總結

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


作者:Ju Changjiang

來源:vivo 官網商城開發團隊

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/uHGymO2FLOKQgz4D1K_jug