京東APP百億級的車與商品關係資料檢索實踐

語言: CN / TW / HK

本文主要講解了京東百億級商品車型適配資料儲存結構設計以及怎樣實現適配介面的高效能查詢。通過京東百億級資料快取架構設計實踐案例,簡單剖析了jimdb的點陣圖(bitmap)函式和lua指令碼應用在高效能場景。希望通過本文,讀者可以對快取的內部結構知識有一定了解,並且能夠以最小的記憶體使用代價將點陣圖(bitmap)靈活應用到各個高效能實際場景。

名詞解釋:

jimdb-基於redis改造升級,京東中介軟體團隊自研的一個高效能key-value資料庫,多數功能與redis一致。

整個汽車行業特殊性,對於零配件有一個很強的對口特性,不同車使用的零配件(例如:輪胎、機油、三濾、雨刮、火花塞等)規格型號不一樣。在售賣汽車零配件的時候,不能像3C家電、服飾,需要結合使用者具體車輛資訊,推薦適合的配件商品。基於此原因,京東自建人車檔案模型並且利用演算法清洗出百億級的車型-零配件的適配關係資料,最終形成“ 人->車-〉貨 ”關係鏈路,解決“ 人不識貨 ”的問題。具體使用場景如下圖:

圖1.1京東商詳推薦商品     

圖1.2京東加購彈窗推薦商品

資料模型

人-> 車->貨 ”關係的核心鏈路是由人(京東使用者)、乘用車和SKU這三部分組成。

首先,使用者在京東APP的商搜頁、商詳頁多個位置都可以選擇自己的車型資訊進行繫結(例如: 圖2.1 ,京東商詳綁車入口位置“+新增愛車”按鈕),建立“人車檔案”資料。

圖2.1.京東商詳綁車入口位置

圖2.2.京東商搜綁車入口位置

其次,運營在後臺管理系統中將商品與車型進行繫結,建立“商品與車型關係”資料(商品與車型的關係資料量級在百億級別)。

最終,購買商品的時候,京東推薦系統可以通過使用者自己繫結的車型推薦出適合該車型的商品。具體商品適配車型資料模型,見 圖2.3

名詞解釋:

SKU-Stock Keeping Unit(庫存量單位),KU是指一款商品,每款都有出現一個SKU,便於電商品牌識別商品。例如:商品詳情頁的每個型號/規格都會對應一個SKU。

乘用車-涵蓋了轎車、微型客車以及不超過9座的輕型客車。乘用車下細分為轎車、MPV和SUV等。

圖2.3京東商品適配車型資料模型

快取結構設計

基於前面兩個部分的介紹,我們可以瞭解到整個商品搜尋適配推薦存在兩個最核心問題。第一、百億級商品適配車型資料的儲存結構設計,儘可能的佔用資源成本最小;第二、商詳通過使用者車型來搜尋適配商品時,必須保證介面效能的TP999位於毫秒級。最終技術選型的時候,採用了jimdb的點陣圖(bitmap)函式來進行資料儲存。

名詞解釋:

TP999-單位時間內,99.9%請求介面響應時間小於等於該值。例如:1秒鐘內1000個請求,對這1000個請求按照響應時間由低到高排名,第999位的響應時間是20ms。最終TP999就是20ms。

3.1

點陣圖(bitmap)結構

點陣圖(bitmap)是通過最小的單位bit來進行0或者1的設定,表示某個元素對應的值或者狀態。一個bit的值是0或者1;也就是說一個bit能儲存的最多資訊是2。

  • 位(bit):計算機內部資料儲存的最小單位,例如:11001100是一個八位二進位制數。

  • 位元組(byte):計算機中資料處理的基本單位,習慣上用大寫B來表示,1B(byte,位元組)=8bit。

圖3.1點陣圖(bitmap)內部結構

3.2

點陣圖(bitmap)資料寫流程

點陣圖(bitmap)是基於jimdb的SDS(簡單動態字串)型別的一系列位操作,遵循jimdb的SDS特性,例如:點陣圖(bitmap)最大長度512M,最大可以儲存232位。以下是“big”字串的SDS結構示例:

圖3.2.1

SDS(簡單動態字串)為了保證效能採用了空間預分配的策略:空間預分配用於優化SDS的字串增長操作。SDS的API對一個SDS進行修改並且需要對SDS進行空間擴充套件的時候,程式不僅會為SDS分配修改所必須要的空間,還會為SDS分配額外的未使用空。具體預分配流程圖如下:

圖3.2.2

1)建立SDS簡單字串預分配空間為:偏移量/8+1。

2)剩餘空間不足時,預分配空間流程。

3.3

壓縮商品與車關係快取

商品適配車型關係(百億級資料量)

商品與車關係快取儲存過程中,採用了商品SKU作為KEY,全量車型ID的偏移量(採用偏移量是為降低記憶體消耗)作為VALUE值來進行儲存。

全量車型ID大約有幾十萬的資料量,極限情況下一個商品SKU可以適配幾十萬輛車,很容易造成快取大KEY的問題,為此我們進行了偏移量(全量車型ID對應的自增ID)的分段處理。具體是按照:SKU作為快取KEY的基礎上,追加一個分段標記數字作為新KEY,每個偏移量都會按照分段範圍對應一個分段標記數字。例如:偏移量1〜50000,對應快取KEY為SKU+0;偏移量50001〜100000,對應快取KEY為SKU+1,其它偏移量以此類推,這樣就保證了一個SKU即使適配所有車輛也不會出現快取大KEY的情況。

BitMap快取結構底層使用SDS簡單字串,為了保證效能採用了預分配空間的策略(如圖3.2.2,“快取BitMap內部儲存流程圖”的2)中虛線框圈選),這樣在快取商品與車關係的時候浪費了大量的快取空間。為此我們調整了偏移量儲存順序,首先獲取到需要快取的車型內最大的偏移量,保證同一個快取KEY第1次建立SDS簡單字串(如圖3.2.2,“快取BitMap內部儲存流程圖”的1)中虛線框圈選)後,不再進行第2次空間擴容,這樣來最大限度的提升快取利用率,起到壓縮空間目的。快取資料關係流程如下:

3)設定分段最大的偏移量,保證後續新增偏移量不再擴容空間。

4)設定分段較小的偏移量。

全量車型ID是定長7位的數字,如果用它作為偏移量將消耗記憶體巨大,所以採用對應自增ID作為偏移量。最終在bitmap快取的商品SKU與車的適配關係快取結構如下圖:

3.3商品與車快取結構圖

5)spuId用{}括起來表示快取路由(Lua指令碼中同一次請求,資料必須在快取同一個分片上,否則會丟失資料)。POP商品spuId是SKU的產品ID,自營商品spuId是SKU的MainSkuId。

備註:

1、自營商品MainSkuId可能發生變化,所以我們接入了商品變化MQ訊息,實時調整SKU與車適配關係的儲存位置。

2、京東商詳頁面中每個不同的規格/型號分別對應不同的SKU,但是它們都對應同一個SpuId或者MainSkuId。

快取架構設計

商品與車的關係資料量每天都在不斷增長,要求快取架構設計,需要支援叢集橫向/縱向擴容和來滿足業務發展以及高可用性。整個快取架構體系主要有前端、京東養車商品與車關係層和儲存三部分組成。

“商品與車關係快取架構”層核心包括:1、“叢集路由”層,實現了叢集橫向擴容,保證資料量增長的時候,快取容量也能跟上。2、“分片路由”層,保證搜尋的底層資料的分片相同,避免資料丟失。

“儲存”層核心包括:1、實現了快取壓縮,參見3.3壓縮商品與車關係快取。2、單元化實現跨區域災備,保障大促系統穩定性。

具體商品與車關係快取架構如下:

商品與車關係快取架構圖

6)叢集路由,通過商品型別或者商品編號(POP商品)路由到不同快取叢集,便於橫向擴充套件,每個叢集單分片限制,解決分片超過限制問題。

7)分片路由,保障Lua指令碼搜尋資料的底層資料叢集分片相同,避免資料丟失。其中自營商品和POP商品的路由分別是main_sku_id和product_id。

8)自營商品快取叢集,單元化實現跨區域災備,採用自研DRC(Data Replication Center)資料同步機制。

9)POP商品快取叢集,通過商家編號拆分為兩個子叢集。

高效能搜尋

基於BitMap(點陣圖)快取的商品與車關係資料,商詳呼叫介面的內部實現採用了Lua指令碼來降低網路開銷,保障整個介面的效能。以下是搜尋介面的流程圖:

5.1

商詳搜尋商品與車適配關係流程圖

10)商詳呼叫介面的時候,要傳兩個引數。第1個引數是全量車型ID列表,大約5個全量車型ID。第2個引數是商品SKU列表,SKU的數量極限超過200個。最後全量車型ID與商品SKU組合為上千個商品與車的關係後,再到百億級適配關係去搜索看是否匹配的。如果不匹配返回適配商品,反之則返回不適配。

5.2

商詳搜尋商品與車適配關係Lua程式碼

Lua指令碼減少了應用伺服器與快取伺服器的互動,降低了網路開銷的時間,達到提升搜尋服務的效能。以下是Lua指令碼具體程式碼:

5.3

商詳搜尋商品與車適配關係介面效能

基於以上快取設計和Lua指令碼的使用,整個介面T999小於13ms。具體的介面效能監控如下圖:

整個快取結構設計的時候,使用BitMap(點陣圖)來儲存資料。解析SDS的內部儲存流程,通過儲存流程機制避開預分配空間節點,最大限度的利用快取空間,避免資源浪費。採用Lua指令碼來實現資料的適配搜尋,降低網路開銷,進一步提升介面的效能。希望此文對大家後續設計類似場景有一定的幫助和啟發。