1.業務背景
榜單在經歷了供給量迅速增長及C端分發場景多樣化等迭代,數據量及峰值流量呈十倍百倍增長,這必然帶來數據庫的極大存儲壓力和C端查詢性能降低。為滿足未來各類復雜定制化規則和億萬級數據甄選,綜合引導消費者的購物決策,得物商品榜單生產遷移及B/C端數據存儲隔離應運而生。
2.概述
得物榜單作為C端核心導購場景之一,通過建立豐富的規則矩陣,多維度提供用戶購買參考建議,幫助用戶快速決策并完成商詳轉化。目前涵蓋六種類型包括熱銷榜、新品榜、趨勢榜、種草榜、好評榜及回購榜,主要入口包括商詳、品牌主頁、分類tab、瀑布流、會場、頻道等。
2.1 “圈選”+“排序”是核心:
通過圈品條件圈選出一系列商品,再根據排序規則排名后取TOP20商品入選榜單。圈品范圍包括類目、品牌、系列、標簽等,通過【6類模型、N項指標因子】綜合計算排序后的總分代表商品的綜合競爭力,分值越大,代表該商品表現越好。得物榜單基于上述算法模型嚴格把控上榜商品品質,從而幫助用戶根據自身訴求快速決策。
2.2 是否存在更優的排序?
通過對榜單排序邏輯優化可針對性提升榜單承接效率,探索一套最佳排序規則公式需要通過不斷的嘗試,因此整個實驗周期較長,在設計上就需要支持實驗的快速推進。
來看下目前如何實現榜單的創建及生產。
涉及表:
- 基礎表:記錄榜單基礎信息;
- 圈品條件表:記錄榜單圈品維度信息包括類目id、品牌id、系列id、標簽id、商品ids集合等;
- 商品集合表:記錄榜單下關聯的top20商品id,當前榜單B/C端流量都是走了這個表的查詢;
如何綁定圈品范圍?
- 人工榜單通過后臺新增榜單時綁定撈月規則或人工直接配置商品ids集合,并同步寫榜單圈品條件表;
- 半自動化榜單通過后臺新增榜單時綁定類目id、品牌id、系列id等維度規則,并同步寫榜單圈品條件表;
- 自動化榜單通過預先設定的規則批量寫榜單圈品條件表;
數據流轉?
商品后臺新增榜單基礎信息至基礎表,將圈品范圍信息(類目、品牌、系列、標簽、spuIds等)保存至圈品條件表,搜索每兩小時定時從庫中撈取圈品條件表數據,獲取最新數據刷到商品集合表中。
- 搜索數倉H+1/T+1 dump:搜索離線數倉通過離線計算排序因子數據,生成離線寬表;
- 搜索離線圈品排序引擎:對照組榜單由搜索生產。每2小時定時調度,掃描圈品條件表全量數據,從寬表中篩選商品并進行排序,排序結果通過DTS數據同步回流榜單商品集合表;
- 搜索離線圈品排序引擎:實驗組榜單由商品圈品排序引擎生成,底層實現和搜索大致相同。
3.系統缺陷及解決思路
3.1 鏈路強耦合
商品/搜索存在雙寫榜單商品表場景,由于搜索通過dts數據同步方式回流數據,導致數據相互覆蓋甚至主鍵沖突,通過id隔離的方式可以暫時解決。
3.2 重復造輪子
目前榜單商品生產鏈路強依賴搜索,由搜索實現商品圈選及排序,搜索榜單商品生產方式單一,無法滿足榜單圈選/排序規則定制化供給。而「撈月」作為得物核心選品投放平臺,已經具備強大的圈品排序能力。
3.2.1 撈月指標體系
海量選品指標維度(商品基礎信息,活動信息,價格與庫存,流量與轉化等)支撐各業務,分鐘級別選品實時指標數據;
- odps離線指標數據:商品/交易指標進行計算產出離線寬表。
- 實時交易指標:如活動期間GMV,買家數等交易數據。
- 離線DUMP:離線數倉通過離線計算指標對應值經由datawork同步任務將指標值同步至撈月B端ES。
- 實時DUMP:業務系統/實時數倉或其他對接模式準實時的通過商品Feature(一些業務邏輯性強的指標)、DB BinLog或其他方式將數據通知到撈月指標中心,再由撈月指標中心將數據落到撈月B端ES。
3.2.2 撈月實時選品引擎
分鐘級別執行引擎更新選品結果。
3.2.3 撈月排序中心
支持個性化、統計字段、自定義權重配比等多維度復雜升降序排序規則;也支持用戶特征,進行推薦算法個性化排序。
具體細節不在本文展開,重點關注撈月指標體系、選品及排序能力在本次遷移中的應用。
針對第一個和第二個問題,我們提出榜單遷移撈月——通過復用撈月現有圈選/排序能力,完成榜單商品生產能力搭建,將榜單生產從搜索側遷移至商品側,移除榜單底層能力搭建對搜索的依賴。
3.3 無法支撐海量數據的高并發讀
未來榜單需支撐類目下沉場景,“類目下沉”即針對目前以類目為維度的榜單,再按品牌、系列、標簽等維度進行榜單延伸。如:跑步鞋熱賣榜下,再細分出耐克跑步鞋、入門跑步鞋、透氣跑步鞋等榜單,更多維度擴充包括人群、風格等。按照笛卡爾積生成方式將任意維度與類目進行兩兩組合,將產生百萬級甚至千萬級別海量數據,造成極大的存儲壓力。同時,榜單從生產到C端分發,經歷榜單商品圈定、審批流及其他狀態控制,最終能夠在C端成功分發的榜單數量有限,每次查詢都會觸發有效數據的實時過濾。隨著榜單數量的快速增長,必然帶來C端查詢性能降低,如大key、索引失效等,存在性能隱患。榜單在商詳分發,必然伴隨著高并發讀。
針對這個問題,雖然可以盡量地從優化 sql、優化索引、緩存等等方面進行優化,但總會有到達極限的時候。關于海量數據的存儲選型已經有非常廣泛的案例,該如何進行存儲選型?存儲選型的目的還是為了我們的使用場景和用戶服務,因此在選型前需要回答一些業務指標&技術指標方面的問題,以便于我們清楚存儲選型的應用環境:
- 數據量及日增數據量:數據量在可控范圍內日增穩定;
- 讀寫偏好:榜單狀態及上榜商品不頻繁變更,讀多寫少;
- 運行性能要求:并發量峰值商詳、首頁,低谷像一些二級頁面;
- 查詢復雜度:復雜條件查詢、聚合查詢、join查詢;
- 其他性能要求:實時性要求不高;
結合榜單業務特征以及海量數據和高并發的特點,可能的解決方案不限于
- 使用緩存的方式通過程序代碼將數據直接保存到內存中,如ConcurrentHashMap、Caffeine等;或使用緩存框架如Redis等;
- 數據庫優化:數據庫優化的方式很多,常見的可以分為:數據庫表結構優化、SQL語句優化、分區、分表、索引優化、使用存儲過程代替直接操作等;
- 使用NoSql技術:HBASE、MongoDB等;
- 使用搜索引擎技術:ElasticSearch等;
設計實踐中,要基于需求、業務驅動架構,無論選用 DB/NoSQL, 一定是以需求為導向,最終數據存儲方案必然是各種權衡的綜合性設計:
- 分庫分表:垂直切分適用于表中存在業務耦合,且拆分后單表數據量依舊很大;水平切分的關聯查詢性能差;
- HBASE:列存儲分布式數據庫,適合TB級別數據的實時入庫和快速隨機訪問場景,缺點是查詢僅能通過rowkey和range檢索,不支持復雜查詢;
- MongoDB:文檔型NoSql,適合非結構化數據存儲,表結構可隨意變更,因此插入效率高,同樣的也是不支持復雜查詢如多表查詢等;
- ElasticSearch:寫入性能低,實時性低,但是通過為所有字段添加索引可支持復雜的聚合查詢和條件查詢。
綜合考慮,B/C端數據存儲隔離成為當下性價比最高的解決方案。
以下將闡述本次實踐的詳細實施點。
4.技術實施點
整體改造將分為兩個階段進行:首先完成鏈路改造,即榜單生產遷移撈月,待數據驗證通過進行第二階段的存儲改造,即B/C端數據存儲隔離。
4.1 鏈路改造
4.1.1 撈月指標創建
撈月需支持榜單實驗能力,因此在榜單對照組通用指標的基礎上新增實驗組通用指標,同時預留對應的實驗組排序規則??芍С滞愋桶駟瓮瑫r段進行一項實驗,實驗結束后根據實驗結果修改通用算法模型。實驗指標可在多次實驗復用,不會造成撈月指標數量的遞增。
- 對照組熱銷榜分數指標(best_seller_score) :根據7日內銷量與成交金額計算綜合排序
- 對照組新品榜分數指標(new_product_score) :根據上架時間和收藏人數等指標綜合計算排序
- 對照組趨勢榜榜分數指標(soare_score) :根據近7日銷量和收藏人數等指標綜合計算排序
- 對照組種草榜分數指標(collect_score) :根據當日新增商品銷量等指標綜合計算排序
- 對照組好評榜分數指標(favorite_score) :根據商品好評率和好評數等指標綜合計算排序
- 對照組回購榜分數指標(rebuy_score) :根據商品年度累計回購人數等指標綜合計算排序
- 實驗組熱銷榜分數指標(best_seller_score_test):根據累計付款人數和收藏人數等指標綜合計算排序
- 實驗組新品榜分數指標(new_product_score_test) :根據上架時間、點擊、收藏、銷量等指標計算綜合排序
- 實驗組趨勢榜榜分數指標(soare_score_test) :根據近7日銷量和收藏人數等指標綜合計算排序
- 實驗組種草榜分數指標(collect_score_test) :根據當日新增商品銷量等指標綜合計算排序
- 實驗組組好評榜分數指標(favorite_score_test) :根據商品好評率和好評數等指標綜合計算排序
- 實驗組組回購榜分數指標(rebuy_score_test) :根據商品年度累計回購人數等指標綜合計算排序
4.1.2 排序規則創建
- 對照組熱銷榜排序規則
(sort_best_seller)=best_seller_score * 100% desc - 對照組新品榜排序規則
(sort_new_product)=new_product_score * 100% desc - 對照組趨勢榜排序規則
sort_soare)=soare_score * 100% desc - 對照組種草榜排序規則
(sort_collect)=collect_score * 100% desc - 對照組好評榜排序規則
(sort_favorite)=favorite_score * 100% desc - 對照組回購榜排序規則
(sort_rebuy)=rebuy_score * 100% desc - 實驗組熱銷榜排序規則
(sort_best_seller_test)=best_seller_score_test * 100% desc - 實驗組新品榜排序規則
(sort_new_product_test)=new_product_score_test * 100% desc - 實驗組趨勢榜排序規則
(sort_soare_test)=soare_score_test * 100% desc - 實驗組種草榜排序規則
(sort_collect_test)=collect_score_test * 100% desc - 實驗組好評榜排序規則
(sort_favorite_test)=favorite_score_test * 100% desc - 實驗組回購榜排序規則
(sort_rebuy_test)=rebuy_score_test * 100% desc
4.1.3 指標、排序規則同步ES供后續選品及排序
改造后全鏈路數據流轉如圖所示:
- 榜單創建:圈品條件寫入撈月選品規則數據表;
- 排序規則:超時中心調用排序引擎更新每個商品的排序分同步撈月B端es;
- 實時選品:圈品引擎分鐘級別掃描待執行的選品規則數據,從底表中篩選符合條件的商品并生成選品集id同步撈月B端es;
- 完成綁定:撈月es將選品結果回流到榜單商品表,并將選品集id回流到榜單基礎表,榜單id和選品集id綁定完成;
- 商品更新:監聽撈月選品集結果變更消息。
4.2 存儲改造
4.2.1 B端數據源-撈月B端ES
在鏈路改造環節我們已經完成了榜單和撈月集的綁定,因此查榜單下的商品就等同于查撈月集下的商品。
具體邏輯如下:
那么是否完全可以復用撈月存儲結構,釋放榜單商品集獨立存儲的空間?首先根據B/C查詢場景劃分為正向鏈路和反向鏈路。正向鏈路,也就是從榜單id獲取到撈月集id,從撈月es根據撈月集id獲取撈月商品結果集,B端查詢方式均為正向鏈路,因此可以復用撈月存儲結構。反向鏈路,即從撈月es根據商品id獲取其所在的撈月集ids,從撈月集ids查詢榜單ids。由于撈月集id暫無場景打標,只能遍歷撈月集ids判斷是否屬于某一個榜單,查詢成本極高,榜單在C端的核心分發場景商詳便是滿足了這個鏈路特征,當然我們可以通過建立撈月集場景打標體系、構建榜單商品ES大寬表的方式來解決問題,有沒有性價比更高的解決方式?
4.2.2 C端數據源-榜單集合mysql表
針對當前業務規模及C端查詢復雜度,考慮采用B/C端查詢隔離的方式進行實現。撈月選品結果變更、榜單顯示/隱藏、審核通過/駁回、生效/失效都會影響榜單的分發狀態。最終能在C端分發的榜單十分有限,為避免在C端做大量數據的實時過濾,降低索引失效和大key風險,原B/C端公用數據源榜單集合表僅存儲可分發榜單商品數據供C端查詢,實現方式及其簡單:在狀態變更及撈月結果集變更時觸發實時更新/刪除榜單集合表即可,整個改造過程無需改動C端代碼,僅通過幾行業務代碼,就將DB存儲數據量量級降至原來的40%,可支撐未來兩至三年數據量的穩定增長。
4.3 灰度設計
相對于功能完整性,如何實現平滑穩定的切流是整個項目中比較重要的一環。
為了實現用戶無感切換,降低切換過程中可能出現的故障對系統的影響,通過多個灰度讀寫開關保證切流過程的平滑和穩定性,整個過程可,做到“隨切隨?!?。整體灰度分階段逐步推進,采用:
- “增量數據維護-數據驗證-全量數據刷數-數據驗證-灰度切流-數據驗證”的方式
- 各個階段均有補償策略或回滾方案,風險可控性較高
即采用先進行增量數據維護,待增量數據check+fix通過后,進行全量數據刷數,待全量數據check+fix完成后,實施灰度讀切流,并進行雙讀check,一旦出現問題則開關關閉;寫切流采用先寫臨時表,臨時表數據驗證通過后切換寫主表,一旦出現故障或臟數據,預案啟動,可保證數據一小時內回滾。具體來說,我們關注以下四點:
4.3.1 數據維護
首先進行風險評估,涉及榜單需綁定撈月集數量為N,以每個撈月集限制M個SPU為準,選品結果集總數量可達N*M,寫入高流量將造成ES實例整體性能急劇下降。另一方面,數據修復成本極高,綁定錯誤只能重新綁定新的撈月集,需全量刪除舊撈月集后再綁定新撈月集,兩次寫入成本極高。因此先進行增量數據維護,待增量數據check+fix通過后進行全量數據分批刷數。
4.3.2 讀/寫切流
- 讀切流:讀切流的核心是將B端數據源切換到撈月ES。這一步執行的節點十分關鍵,需放在鏈路改造完成,存儲改造開始之前進行。原因是如果先進行了存儲改造再執行切流,由于存儲改造完成后rank_list表僅存儲可分發榜單,那么切流后一旦出現問題應立刻切換回老鏈路,原B/C端公用數據源rank_list表數據已經不完整,完全不能滿足B端使用,無法快速止血,只能快速定位問題并修復上線。確定執行時間節點后,寫好開關并進行雙讀check,發現問題切換回原鏈路即可。
- 寫切流:搜索更新榜單商品開關關閉后,撈月選品結果全量寫入榜單集合表的風險較大,通過建立榜單集合臨時表,待臨時表數據驗證通過后切換主表。
4.3.3 數據校驗
雙讀check、不定時全量數據check+fix 、增量數據check+fix 、應用監控、日志告警埋點等;
4.3.4 數據訂正
針對灰度過程可能出現的所有數據錯誤預備對應數據訂正接口。多想一步,如果在寫切流過程中切換寫主表后出現未識別到的數據錯誤,如何快速止血?在遷移的過程中我們暫時保留搜索更新榜單商品的能力作為數據修復預案,可在一小時內完成數據修復。
具體灰度切流推進流程如下:
經歷兩個星期的灰度,已移除對搜索的依賴實現全鏈路閉環,依照灰度方案通過切流開關及預案等手段確?!半S切隨?!?,上線期間零故障。
5.總結
綜上所述,榜單通過生產遷移徹底解決了一直以來榜單底層能力支撐不足的痛點。完成鏈路合并后,借助撈月圈品排序能力降低未來各類復雜定制化供給場景的維護成本,功能上線后已提升榜單各類業務迭代效率提升50%以上。并通過B/C端數據存儲隔離,以極低的改造成本降低表存儲成本60%。
在這個基礎上,思考是否有更多的發力點:結合當前系統現狀和未來的可能性,結合業務規劃,圍繞用戶對平臺榜單預期,未來將演變出多維度(內容、品牌、sku、spu等)榜單通用生產引擎,在供給充足基礎上實現場景化個性化分發。
以上就是我們在得物商品榜單生產改造探索實踐中的一些經驗和總結,分享出來希望對閱讀本文的你有一些幫助!