從一個電商平臺的庫存同步談性能優化和方案落地

下面的案例來自筆者的實際工作經歷,涉及到的系統是筆者負責開發和維護的,一個國外的電商平臺。

如果你對電商系統有所瞭解,將有助於你理解下面提到的業務。

如果你沒有相關的知識背景,也沒有關係,我會儘可能簡化地將業務講給你,並且只要求你理解關鍵概念即可。

背景

事情的起因是平臺的某位高級主管的一封郵件,其中提到商品全量庫存的實時性太低,需要各個部門的人協力解決。

庫存同步相關概念

先介紹一下電商平臺的一些基本概念。

庫存就是倉庫中某個SKU(最小庫存單元)在倉庫中實際有量。

比如某型號灰色8核16G內存的筆記本電腦就是一個SKU,在倉庫中這個SKU有100臺,那麼它的庫存量就是100。

  • 全量和增量庫存

倉庫每天都會把自己實際的庫存量統計出來,這就是全量庫存,倉庫把庫存量發送給各個銷售終端,這就是全量庫存同步

同時,爲了保證庫存的實時性,防止超賣(賣出比實際庫存量更多的商品,倉庫無法發出貨品,有可能導致客訴)和倉庫有貨但客戶買不到的情況,倉庫會把庫存的變化量也實時分發到各個終端,這個庫存的變化量就是增量庫存

舉例來說,上面的那個SKU筆記本電腦有一臺送到攝影棚去拍照了,那麼這臺就無法銷售了,倉庫就會推送一個-1的增量庫存到銷售終端;而如果它收到了消費者的退貨,退貨入庫以後,將會推送一個+1的增量庫存。

  • 多店鋪與分盤

電商平臺一般都會有多個店鋪入駐,例如3C這個分類下面,可能有蘋果、華爲、三星、小米等店鋪。

不同店鋪的庫存是獨立的。

有時候一個SKU在多家店鋪都有售,iPhone X/太空灰色/256GBXXX蘋果平臺旗艦店XXX手機大世界店XX蘋果折扣店 就是三個不同的庫存記錄。

這就是多店鋪庫存

作爲分銷商,它的倉庫中放着不同平臺、不同品牌的商品。例如上面的手機,在深圳、廣州、上海三個地區倉庫都有貨,並且是分別賣給天貓和京東的,那麼它的庫存記錄就有6條,分別是:

No. 倉庫 渠道
1 深圳 天貓
2 深圳 京東
3 廣州 天貓
4 廣州 京東
5 上海 天貓
6 上海 京東

這就是分盤庫存

  • 庫存清點時間和最後更新時間

在實際操作中,爲了保證數據的準確性,平臺會對庫存的時間進行校驗。

例如,倉庫在凌晨 01:00 清點出某SKU庫存量爲100,則這條庫存記錄的庫存清點時間就是01:00。

倉庫在01:00清點完以後,在02:00收到了一個退貨,那麼就會推送一個+1的增量到平臺。

一般情況下,全量先發出,平臺應該先收到全量100,再收到增量+1,最後爲101。

但如果由於某個中間環節出了問題,先收到增量+1,在收到全量100,那麼最終的庫存量將是100。全量庫存會直接覆蓋平臺現在的庫存量。

因此,如果有一個最後更新時間,記錄是02:00收到的增量,那麼當01:00的全量過來的時候,由於比增量時間要早,將被平臺視爲作廢。

庫存流轉過程

實際的庫存數據流轉過程往往不是 「倉庫——>平臺」 這麼短的鏈路,實際鏈路總是很長的:

InventoryDataSystemFlow

不同系統的性能不同,實現方式不同,越長的鏈路時延問題就越嚴重。

方案

問題分析

想要解決問題,首先要分析問題。作爲平臺技術負責人,我先統計了平臺最近一個月的庫存同步時間,大約是150分鐘,並且每隔幾天會延長幾分鐘。

然後我統計了最近一段時間全量庫存的數據變化量,僅僅10天就增加了5w。

InventoryDataAmount

  • 問題定義:
    目前看來,從平臺角度來講,隨着庫存數據量的增加,處理時間不斷延長,再加上整個鏈路很長,造成全量庫存數據的實時性很差。

頭腦風暴

分析完問題,我立即召開了團隊的人員討論解決方案,經過大家討論,可以優化的環節是下面幾個:

  1. 提升硬件配置

當你拼命練跑步避免遲到的時候,也許給你一輛車就解決問題了。

部門服務的資源緊張,配置極低。

  1. 修改消息處理邏輯

目前庫存數據拆分粒度很細(分店鋪分倉庫分門店),加上網絡時延,會造成處理時間延長。

  1. 優化消息處理的邏輯

庫存數據由消息中心統一處理,消息中心會處理訂單、商品、價格、會員、庫存等等多種類型的數據,效率不高。

  1. 優化全量庫存同步

從平臺處理數據的代碼流程着手優化。

確定方案

對於方案1需要金主批錢,方案2需要多個系統修改,這些不好談;方案3需要改動整體架構,工作量巨大。

對於解決燃眉之急,方案4的可行性最高,改動量和影響範圍最小。

細化方案

方案4優化全量庫存同步,具體細化爲下面三個方面

  1. 業務精簡和標準化
  2. 數據處理高性能
  3. 隊列操作高性能

下面在實施的時候一一詳細說明。

實施

業務精簡和標準化

業務精簡和標準化分爲下面幾個方面:

  1. 全增隔離

目前全量和增量庫存同步使用同一個隊列名,通過字段判斷是全量還是增量。
這樣增加了代碼的複雜度,而且原子性不好。
全量庫存單獨隊列,與增量同步隔離。

  1. 剝離日誌

修改庫存以後需要記錄詳細變更日誌,日誌的實時性要求不高,將改操作剝離爲單獨的隊列進行處理。

  1. 剝離新建

目前同步庫存之前先判斷該商品是否存在,如果存在再判斷該商品在庫存表是否有記錄,如果沒有則新建記錄,有則更新庫存。

由於隨着數據量的增加,新建的記錄(每天1k到3k之間)所佔的比重越來越小,因此將新建的操作也單獨剝離爲一個隊列進行處理。

優化消息處理的邏輯

平臺爲分佈式系統,消息處理需要從註冊中心調用遠程Dubbo服務,首先將數據處理移動到Dubbo服務中完成,避免了頻繁調用Dubbo服務,另外使用多線程處理消息,最大限度利用多核心的優勢。

關於線程池的使用,可以參考這篇文章:使用ThreadPoolExecutor構造線程池

//構造線程池
private static ExecutorService executorService = new ThreadPoolExecutor(
16,
32,
10L,
TimeUnit.MINUTES,
new LinkedBlockingQueue<Runnable>(
                2048),new ThreadFactoryBuilder()
                     .setNameFormat("BatchSyncFullInventory-Pool-%d").build(),
                     new ThreadPoolExecutor.CallerRunsPolicy());

經過上面的優化,目前處理的時間有了大幅度降低:

InventoryDataAmount

隊列操作高性能

經過上面的優化,發現每處理2k條消息,處理時間在1s以內,但出隊時間接近15s。

因此,下面的優化重點是提高隊列的操作性能。

由於Redis頻繁的操作,會造成RTT(網絡時延)不斷延長,可以使用管道來降低RTT。

下面是Spring Data Redis使用管道的方式:

//從隊列中循環取出消息, 使用管道, 減少網絡傳輸時間
List<Object> msgList = redisTemplate.executePipelined(new RedisCallback<Object>() {
    @Override
    public Object doInRedis(RedisConnection connection) throws DataAccessException {
        for (int i = 0; i < batchSize; i++) {
            connection.rPop(getQuenueName().getBytes());
        }
        return null;
    }
});

理論上是這樣的,需要有實際的數據支撐,因此需要通過做實驗來驗證方案的效果。

首先,在測試環境對比了三種不同的出隊方式的性能,分別是單線程循環出隊、多線程循環出隊和單線程管道出隊。

測試發現使用管道出隊兩千次,只需要70毫秒左右。

最終,使用了 管道+多線程,庫存消息的處理時間降到了30分鐘左右:

InventoryDataAmount

關於管道的使用,可以參考這篇文章:Redis管道技術

CPU使用過高

雖然發佈到生產以後,處理時間有了大幅度降低,但是經過監控發現,Redis的CPU使用率一直居高不下。

對於監聽隊列的場景,一個簡單的做法是當發現隊列返回的內容爲空的時候,就讓線程休眠幾秒鐘,等隊列中累積了一定量數據以後再通過管道去取,這樣就既能享受管道帶來的高性能,又避免了CPU使用率過高的風險。

//如果消息的內容爲空, 則休眠[10]秒鐘以後再繼續取數據,防止大批量地讀取redis造成CPU消耗過高
if (CollectionUtils.isEmpty(messageList)) {
    Thread.currentThread().sleep(10 * 1000);
    continue;
}

總結

  • 方案設計:頭腦風暴與可行性評估
  • 邏輯精簡化 : 剝離不必要的操作
  • 流程標準化 : 梳理統一業務的流程
  • 線程池實現高性能併發 : Executor Service
  • 管道實現高性能隊列 : Redis Pipelining

作爲一個工程師,要知道自己能力的邊界在哪裏,利用有限的資源讓方案落地。

這裏優化的經歷,是想讓大家對電商相關的業務有所瞭解,另外對處理問題的解決思路有所借鑑。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章