天天看點

聊一聊方案中心性能優化中做的緩存設計

申懷 阿裡開發者 2023-07-26 09:02 發表于浙江

聊一聊方案中心性能優化中做的緩存設計

阿裡妹導讀

本篇文章主要是對方案性能優化2.0中,所做的緩存設計的過程、方案、結果做一個總結。

一、前言

對于方案中心,核心業務場景之一是物流場景下的物流費用計算。而部分業務場景下,對于物流費用計算的性能有較高要求,如ICBU網站運費模闆鍊路,通方案中心計算快遞、海拼物流費用。在接入新的流量場景的背景下(ICBU商品搜尋接入運費展示、菜鳥經營中台快遞運力線回遷方案中心),方案中心将會面對更高的性能壓力。

此前預估如需要支援運費模闆計算核心20國運費,方案中心叢集qps需要達到6600,計算全量220國運費,叢集qps則需要達到35600。但是優化前方案中心叢集qps性能僅僅在1600左右,遠遠達不到要求,此前已經做了1.0的優化版本,qps提升40%,但是還達不到支撐計算20國運費的要求。2.0優化目标就是要達到足以支撐計算核心20國運費的要求。

本篇文章主要是對2.0的優化中,所做的緩存設計的過程、方案、結果做一個總結。

二、優化2.0面臨什麼問題

2.1 1.0優化做了什麼

在1.0 的性能優化中,我們制定了降低CPU資源消耗、合理使用緩存資源等措施,整體叢集性能提升在40%左右,qps叢集壓測能到2300,但這與目标6600還有差距。是以,針對複雜的查價流程,需要進一步在代碼架構層面進行優化。

1.0 已做優化措施

  • 降低CPU資源消耗
  • 規則引擎表達式預先編譯并且緩存
  • 減少大對象深度複制,快遞場景可以完全不複制
  • 避免通過JSON序列化,再反序列化實作建立對象并且指派
  • 底層中繼資料查詢方法,避免使用第三方封裝的校驗架構
  • 日志列印優化,debug添加isDebug判斷
  • tair使用優化
  • mget替代tair get方法使用,降低網絡資源消耗

2.2 計費流程的核心問題--嵌套循環查詢

聊一聊方案中心性能優化中做的緩存設計

圖 2.2-1 簡化的計費流程

  • 比對運力線方案 1<=n<=10,查DB or localCache
  • 計算物流方案 1<=n<=?,視服務商報價而定,如有多條方案,則計算多條。
  • 計算銷售價/計算成本價,分别計算兩個報價。
  • 比對報價版本,比對目前生效的報價版本。查DB 或 tair
  • 比對費用項報價1<=n<=30+,視不同運力線而定。查DB 或 localCache

以上隻是簡化流程,依然有很深的嵌套循環。

本質上可以把DB、Tair、LocalCache定義成資料存儲層,在多重嵌套循環,一次HSF請求,與資料存儲層互動的次數将會極多,這是一個非常不确定的資源消耗,存在這樣的不确定性,無法滿足穩定的QPS性能要求。

看到一句有意思的話,高QPS的問題,都可以用“走緩存,加機器”來解決。一句很精辟的話,但是想要要走緩存,加機器,也不是簡單的事情。走緩存舊得深入業務場景分析緩存模型、讀寫政策,加機器得考慮叢集其他單點資源的瓶頸,涉及各種問題。

雖然涉及各種問題,不過這次優化2.0總體的方針還是背靠這句話“走緩存,加機器”。

2.3 計費資料涉及模型簡述

聊一聊方案中心性能優化中做的緩存設計

圖2.3-1 簡化詢價的資料模型表達

太詳細的資料模型就不展開了,聊聊簡化版的結構模型。快遞詢價流程為例,資料流以運力線(sku)為起始,需要依賴上圖中其他方塊中的資料資訊完成整個計費流程,每個方塊部分涉及的資料表至少2張以上,有的方塊甚至需要5張表才能擷取到完整的目标資料。

了解到目前計費流程關聯的資料模型有哪些,繼續看優化前方案中心的緩存模型。

2.4 目前緩存模型

聊一聊方案中心性能優化中做的緩存設計

圖2.4-1 2.0優化前資料讀取模型

2.4.1 Tair緩存

2.0 優化前,方案詢價流程最核心使用了Tair緩存的隻有一個,就是圖2.3-1中查詢目前報價配置這裡的資料查詢。在周遊sku的過程,1.0優化之前會多次RPC請求,從Tair查詢sku或者資源的目前生效的報價配置, 1.0優化之後,通過mget,提前批量查詢來稍作優化。

這裡分析下曆史原因,為什麼隻在這裡用了Tair緩存。

  • 為什麼緩存?要擷取 目前報價配置,從DB查詢擷取,查詢涉及表較多,不做緩存,那肯定頂不住。
  • 為什麼用Tair?現在看來,是因為擷取 目前報價配置 模型的查詢複雜性,通過分布式緩存,盡可能降級查詢DB次數。

優點:

  • 使用Tair緩存目前報價配置,批量讀取的方式,可以一定程度緩解DB查詢壓力

缺點:

  • 目前Tair緩存模型設計,沒有把最核心、查詢量級最大的方案和報價進行緩存,沒解決真正痛點;
  • 在計費過程中仍需要根據每個方案+費用項構造相應的緩存key,需要費用項多多情況下,仍然需要多次查詢tair;

2.4.2 本地緩存

2.0 優化前,本地緩存模型的構成方式比較簡單。大部分以mapper查詢接口界别 查詢條件 + 結果 構成帶過期時間本地緩存。

這種模型的本地緩存,優點缺點都很明顯。

優點:

  • 實作簡單,不用對資料做新的聚合設計,調mapper接口級别緩存。于前期臨時、快速解決性能問題的本地緩存方案;

缺點:

  • 大面積、細粒度使用本地緩存,叢集機器本地緩存資料還不一緻,易造成客戶體驗割裂問題(測試有時候都搞不清是bug還是緩存)
  • 粒度太細,計費流程與資料存儲層的互動還是嵌套分散在深層次循環流程的内部,當緩存失效,依然會有大量DB查詢(特别是循環嵌套最深的報價查詢)
  • 不太能支援水準擴容(嘗試過,DB扛不住)
  • 緩存資料無法預熱,面對大流量場景,程式重新開機易出現成功率下跌(優化前每次釋出基本都會發生)

圖2.4-2 報價緩存失效後的查詢高峰

*注:每10分鐘就有一批DB查詢高峰,主要因為報價的本地緩存key的構成,包含了目前時間的 整十分 字元串,如10,20,30),是以每10分鐘就會需要加載新的緩存

三、新的緩存

3.1 為什麼需要新的緩存設計

基于以上背景,大緻對目前的緩存模型有一定認知,深層的嵌套循環,無法滿足一系列要求的緩存模型(叢集機器過多,緩存失效DB扛不住壓力),想要做到靠 “走緩存,加機器”來滿足高QPS要求,以現在的流程、緩存設計是無法做到的。

那麼要怎麼取做新的緩存設計?我的了解是先有成熟穩定的業務代碼流程,再來談緩存設計,在有問題的流程上做緩存設計,無疑是無用功。

但是優化前的詢價代碼架構,不能滿足要求,是以第一步,是詢價流程做重新定義,明确優化後的代碼架構,把資料讀取和計算邏輯分隔管理,基于此再做緩存設計。

3.2 對詢價計費流程的重新定義

這裡的重新定義,應該了解成是技術解決方案層面的重新定義,問題空間層面的定義是不變的,詢價流程,在業務流程上還是這樣基本定義。

聊一聊方案中心性能優化中做的緩存設計

圖3.2-1 詢價流程基本定義

但是在技術解決方案定義上,原來的流程資料擷取與使用耦合,分散在嵌套循環的資料使用過程中。需要把資料擷取與使用的過程做隔離,資料擷取子產品的内部,擷取資料操作做聚合(減少次數,如最高頻的比對方案和比對報價),聚合的次元是緩存設計的工作。

聊一聊方案中心性能優化中做的緩存設計

圖3.2-2 解決方案詢價流程重新定義

如上圖3.2-2所示,新的流程基本構思。

1.首先,所有DB查詢,都盡可能有相應的緩存資料;

2.其次,針對要緩存的資料,按照量級劃分,報價和方案資料屬于大量資料,需要用Tair緩存;sku相關、資源相關、spu相關的資料,理論上都是方案中心,對服務定義、服務表達所進行的配置,相對穩定不多變,整體占用記憶體相對小,是以可用本地緩存存儲。

新的流程,在嵌套循環定價計費前,應當能批量比對完方案和報價,從Tair擷取。過程中,需要的服務定義和表達的資料,從LocalCache擷取。

整體方案設計如下:

  • 前置費率比對:小二啟用運力線新報價的時候,通過精衛監聽報價表變更,為每個物流方案提前比對費用項報價,并且比對結果儲存在清洗出來的産品線路費率表中。客戶側查價時,根據from-to線路資訊即可擷取到該線路所有的費用項的報價,不需要在運作時逐個費用項取比對費率,大幅減少查價運作時比對報價的tair請求以及邏輯運算。
  • 減少DB通路:配置型資料通過方案中心本地緩存架構通路擷取,資料量大的費率模型資料,從tair緩存擷取。通過此方案,可以大幅減少DB通路。
  • 減少網絡開銷:在費用計算前組裝好計費所需的資料上下文,通過批量tair查詢讀取費率,避免在核心流程循環通路擷取費率資料,減少RPC請求次數。
聊一聊方案中心性能優化中做的緩存設計

圖3.2-3 報價緩存失效後的查詢高峰

3.3 緩存模型

3.3.1 Tair緩存模型

Tair緩存模型,這裡指的是方案和報價的緩存。這裡的Tair緩存模型的設計,需要滿足兩個關鍵點:可批量查和高速查。貼合業務場景來進行設計,提高比對方案報價的效率以及命中率。

聊一聊方案中心性能優化中做的緩存設計

圖3.3-1 Tair緩存模型設計

prefixGets

之前考慮的使用mget可能會受限于key分片問題導緻查詢緩存性能不穩定。對于方案/報價查詢,改用prefixPut,prefixGets進行批量緩存存取,一個主key,多個子key的情況下,以獲得更好的批量讀取性能。通過prefixGets,可以高效批量擷取一次查價中多個sku的方案的緩存資料 或者 報價的緩存資料。

prefixPut

prefixPut發生在查詢不到報價、或者方案的時候,進行惰性加載

緩存key設計

  • 主key:對于方案/報價,都可以用SPU次元區分主key,如上圖所示。
  • 方案查詢子key:
  • 對于國際快遞,比對線路方案的核心條件,隻有一個destinationCountry,以destinationCountry作為緩存key,可以緩存每次對應目的國的方案查詢結果。
  • 同時在查方案的時候,已經指定了sku_id和生效的報價version_id,是以設計查詢方案緩存key由 sku_id+version_id+destinationCountry組成。如上圖所示。另外需要注意使用version_id作為緩存key一部分,可以對資料做較長時間的緩存,避免了頻繁失效要重新查詢方案資料,并且将Tair緩存的實時性控制,轉移為對version_id的實時性控制。

每次需要更換緩存,隻需有構造新的version_id的緩存key來查詢即可。

  • 報價查詢子key:
  • 查價流程,先根據destinationCountry查詢方案緩存, 得到方案線路清單,一條線路資訊包含【destinationCountry、warehouseCode】。比對報價的查詢條件是warehouseCode + destinationCountry,相似地,設計查詢報價的緩存key由 sku_id+version_id+destinationCountry+warehouseCode組成。
  • 為什麼不直接用destinationCountry?報價資料對象相對而言比較大,受限于業務場景與value大小限制,緩存粒度拆分相對需要更小.
  • 同樣地,可以對資料做較長時間的緩存,避免了頻繁失效要重新查詢方案資料,并且将Tair緩存的實時性控制,轉移為對version_id的實時性控制。

value設計

  • 方案value:沒啥特殊的,結構比較簡單
聊一聊方案中心性能優化中做的緩存設計

圖3.3-2 方案緩存模型value

  • 報價value:結構類似方案,核心是多了報價資訊result【JSON結構資料】
聊一聊方案中心性能優化中做的緩存設計

圖3.3-3 報價緩存模型value

  • 報價記錄value結構

當小二啟用運力線新報價的時候,通過精衛監聽報價表變更,為每個物流線路提前比對每個費用項報價,并且比對結果儲存在清洗出來的産品線路費率表中。客戶側查價時,根據from-to線路資訊查詢産品線路費率表,即可擷取到該線路所有的費用項的報價,不需要在運作時逐個費用項取比對費率,并且在此時把查詢結果,作為報價記錄的value緩存起來,後續查詢命中緩存即可擷取到該線路的所有的報價資訊。

聊一聊方案中心性能優化中做的緩存設計

3.3.2 本地緩存模型

詢價計費時依賴的配置型資料,分成3大類,按照sku、resource、spu做聚合緩存。

key設計

key以 類型+ 大類的主鍵構成:sku+sku_id, resource+resource_id, spu+spu_id

value設計

聊一聊方案中心性能優化中做的緩存設計

圖3.3-4 本地緩存模型value

通過這樣的聚合模型設計,詢價過程中,通過用skuID可以在本地緩存中檢索到任意想要的服務表達定義相關的資料。另外這裡緩存的實時性和寫邏輯控制,在後面展開。

聚合模型每個子屬性更新,需要更新整個模型的資料,這裡為什麼考慮要做聚合,而不采用每個表的資料都單獨一個key緩存的實作方式呢?

  • 每個表的單獨key,緩存結構零散,需要管理更多的Key。
  • 每個表的單獨key,緩存結構零散,在讀取緩存的業務層,不同業務場景訴求下,需要實作比較複雜的關聯組裝邏輯。
  • 配置資料并不多變,少更新,聚合模型更新讀取DB的次數有限,較少。

綜上,相關的配置資料聚合管理的好處大于缺點。

四、緩存讀寫

4.1 本地緩存

4.1.1 緩存預熱

本地緩存預熱,程式啟動時,根據程式内置邏輯定義的本地緩存Key集合,提前加載緩存到應用記憶體,保證提供服務時,緩存已經加載。

4.1.2 緩存更新

簡單來說就是通過監聽精衛,借助廣播能力,通知叢集更新本地緩存。這裡的緩存是一直存在于堆記憶體,不會失效,隻會廣播重新整理。每次重新整理緩存,按照圖3.3-4 本地緩存模型value描述的聚合模型,每次更新最小粒度為一個ConfigDTO。

4.1.3 解決了什麼

  • 解決應用伺服器本地緩存方案緩存實時性問題,實作應用伺服器叢集本地緩存方案的準實時重新整理。
  • 通過廣播資料實體變更,觸發本地緩存重新整理,解決應用伺服器叢集多節點本地緩存不一緻的問題。例如之前經常出現,因為本地緩存問題,查詢方案多次不一緻的問題。
  • 資料啟動時預熱,解決了之前每次釋出,程式重新開機都會出現服務成功率下跌的問題。
  • 對于已緩存資料,在資料使用的業務流程中,可完全屏蔽資料庫查詢,對水準擴容友好。可以解決擴容時,DB瓶頸問題。

4.2 tair緩存

查詢沒啥輸的,按照 tair緩存模型設計 的key-value,進行prefixGets,prefixPut。需要注意的是,key設計的粒度、報價value大小限制。

4.2.1 緩存預熱

這裡需要做預熱的場景,基本就隻有新運力線上線了,一般日常還是沒問題的。新運力上線,目前要求是分批灰階,等Tair緩存命中率上去了,繼續開啟灰階,這是比較保守的做法。

4.1.2 緩存更新

前面有提到,Tair緩存資料的實時性控制,是依靠version_id的實時性控制,方案或者報價的version_id通過本地緩存準實時更新,能夠保證version_id的準實時性,進而保證每次查詢Tair緩存資料的實時正确性。因為每次擷取到的version_id是最新的,拼接出來的Key自然也是查詢最新的緩存的Key

4.3 緩存讀寫總結

聊一聊方案中心性能優化中做的緩存設計

圖4.3-1 緩存讀寫總結

  • 配置型資料:穩定,量不大,查價計費時需要經常讀取的資料。例如運力線配置、運力線資源、報價版本、費用項、運力資源關聯關系等。
  • 方案報價型資料:量大,無法本地緩存,具備版本特性,可以長時間存儲在tair。

五、緩存髒資料處理

5.1 本地緩存

盡管精衛很強大,但也不是100%保證沒有意外,為避免髒資料産生,是以會采用定時任務重新整理的方式來定時更新本地緩存。

5.2 tair緩存

前面的設計有提到,目前的方案/報價緩存子key,是帶版本号的,隻要版本号正确,就不存在緩存髒資料的問題,而版本号資料實時性,依賴于本方案中的本地緩存實作,二者互相結合,保證查詢Tair緩存資料的正确性。另外使用版本号作為緩存key還可以對資料做較長時間的緩存,避免了頻繁失效要重新查詢報價資料。

六、單點資源瓶頸

6.1 Tair瓶頸

對整個應用叢集來說,支撐更大的流量,繞不開單點資源瓶頸,水準擴容更加繞不開單點資源瓶頸。不巧,最近在接入更大的流量場景的時候,就遇到Tair瓶頸問題

聊一聊方案中心性能優化中做的緩存設計

圖6.1-1 切流Tair出現瓶頸監控視圖

聊一聊方案中心性能優化中做的緩存設計

圖6.1-2 緩存穿透後DB的QPS視圖

可以看到出現大量的Tair限流,解決處理方向有幾種,簡單說一下

方向1

很簡單,如果是原本Tair的限流門檻值很低,那麼可以申請擴容。需要注意的是,申請擴容的容量評估,需要結合我們查詢緩存方式來評估,鷹眼上看的僅僅是對Tair發起RPC請求的統計,服務端限流統計是按照真正的key個數統計的。例如使用到prefixGet,那麼就按Skey個數統計。

方向2

如果擴容不能滿足,那麼就需要回到代碼中,看看有沒有什麼不必要的Tair查詢,進行優化。

方向3

針對熱點Key做一層本地緩存,如果應用伺服器的熱點本地緩存中包含key,那麼就不需要查詢Tair了,可以直接傳回結果,降低對Tair的壓力。熱點key的識别可依賴Tair内嵌的LocalCache功能,或者我們自己實作,動态配置熱點Key。

方向4

使用RDB。對持久化有需求,并且緩存QPS确實很高,如果目前使用的是LDB,那麼可以考慮使用RDB,LDB成本比較高,沒那麼多資源。RDB成本相對較低,可以有更多資源。

方案中心怎麼做

這次遇到Tair瓶頸,方案中心是先從簡單的方向1、2入手。

首先申請擴容,一開始評估預計QPS,按照的鷹眼平台展示的來估,因為方案中心使用了prefixGet,是以估少了,擴容完成後發現還是限流。

無奈,但也沒繼續申請擴容,而是到業務、代碼中,分析可以減少的查詢Tair的點。

最後發現,起始每次查買家側快遞查價(大流量場景),除了指定目的國以外,還會指定方案的倉庫。這樣第一次隻根據目的國查詢方案的時候,可能會包含多個倉庫的方案(一般方案是 倉庫-目的國),做了很多無效tair查詢。可以先根據指定倉庫篩選方案,然後再拿剩餘的方案,構造key,批量查Tair報價,可以大大減少key數倍的個數,最後穩定支援切流。這種就是基于業務場景做優化的措施。

七、總結

7.1 資料

通過本地緩存配置型資料 + tair緩存方案報價型資料的組合,緩存命中的場景下,查價計費鍊路已經可以實作無DB查詢。目前線上穩定支援水準擴容,按照壓測資料預估,單機支援180QPS,單叢集50台機器支撐9000+qps

結合新的緩存組合,代碼路徑實作調整如下:

聊一聊方案中心性能優化中做的緩存設計
  • 擷取N個運力線方案版本+報價版本:查詢本地緩存
  • 批量擷取N個運力線方案:查詢tair or DB
  • 批量擷取N個方案的報價:查詢tair or DB

繼續閱讀