本文由作者王改革授權網易雲社群釋出。
一、背景和實作目标
在開發嚴選資料産品(大麥商品資料營運平台和移動資料工作台VIPAPP)的時候,最多的業務場景就是對實時和離線資料模型中查詢、處理、統一資料結構傳回給前端。是以在開發的同時也一直在思考如何将這些相似的資料處理流程統一起來,更關注資料名額本身。
開發中經常遇到的幾個問題是:
- 資料查詢連接配接管理分散
- 模型查詢結果緩存分散
- 對于模型資料查詢結果缺少統一的資料變換子產品支援,每日産出的實時資料名額以及離線資料名額經過後端邏輯做接口傳回的時候,會有大量的get、set操作,如果同時需要計算名額同比、環比、占比、對比值等複合名額時,就會充斥大量的重複髒代碼。
- 依賴的資料服務對存儲在MySQL、GP、Kylin、HBase等存儲引擎的資料模型暫時沒有多模型的連接配接支援。
是以針對以上問題,我們希望能夠設計出能夠在資料産品中使用的通用名額查詢計算子產品(DPRequestManager),主要實作如下目标:
- 管理資料模型查詢,封裝對于 統一查詢服務(DQS)、MySQL等查詢請求,提供查詢連接配接池。
- 提供靈活的資料變換能力
- 能夠通過配置對相應名額(包括名額值、環比、占比、同比等)自動計算,減少過多的備援代碼。
- 支援資料對象映射,減少頻繁的取值和指派操作
- 支援查詢級别的緩存(可以根據系統需求自定義緩存時間、可以設定緩存條件),減少對依賴服務的查詢壓力。
二、通用名額查詢計算子產品(DPRequestManager)組織結構
- 紅色部分的并發查詢器負責管理資料産品與底層資料查詢存儲引擎的連接配接,統一管理各種資料查詢。DPRequestManager會作為資料産品模型查詢的統一入口,封裝底層資料模型存儲引擎,為資料查詢提供線程池服務。同時此部分還提供模型退化的能力。
- 統一緩存子產品負責對資料查詢做條件緩存,對于每日資料未産出或者其他情況可以對查詢結果約定一些必要條件來決定是否緩存查詢結果(緩存級别為請求級别)。
- 橙色部分的資料變換子產品可以對DQS擷取的多表資料進行靈活資料變換
- 配置子產品可以對資料關系映射以及相應的複合名額計算做相應配置,統一生成結果,減少備援代碼
三、資料變換子產品支援操作
對于從模型存儲引擎查詢結果處理成List<Map<String, Object>>結構的,并提供提供以下資料變換操作。
命名規則:子產品内部将使用資料dataKey存放查詢處理結果,dateKey對應的list結果可以類比excel行清單,其中的列名對應Map結構的key值。
現在主要提供如下資料操作:
1. compose(多個查詢集合進行組合,做記憶體連接配接使用,對于多個資料中存在同名列的情況,可選擇按照FIFO進行是否覆寫)
compose({"group_id", "group_name"}, "result_A", "resultB")
2. rename(将資料某一列列名重新命名)group(基于中繼資料行進行分組)
rename ("dk", {"sale_amount_day", "sale_qty_day"}, {"sale_amount, sale_qty"})
3. group(基于資料行進行分組)
group(row -> new StringJoiner("_").add(row.get("week").add(row.get("group_id")))
4. aggregation(基于資料行進行分組, 然後對度量名額字段進行聚合)
aggregation({"week", "group_id", "group_name"}, {sum("sale_amount"), sum("sale_qty")})
5. shape(資料變形,将多列資料變成寬表資料,平坦化透視表結構)
6. intersection(多個資料集合取交集)
7. top(根據有序度量取top集合)
四、名額計算配置以及對象關系映射
這個地方主要解決兩個問題:
- 模型資料查詢結果到資料對象的映射,減少頻繁的取值和指派,現在通用的ORM架構都能解決這個問題,DPRequestManager為了配合複合名額計算通過反射來實作資料對象映射部分。
- 映射過程中的名額計算(名額表達式計算、環比、同比、占比等複合名額的計算)
DPRequestManager主要是通過兩個配置來配合解決複合名額計算的問題。
第一個配置主要是定義資料對象映射的資料篩選規則(目标資料集合、環比資料集合、占比資料集合等)
{
"clazz": StockDTO.class,
"useDate": "2018-12-07", // 指定目标值日期
"hbDate": "2018-12-06", // 指定環比日期
"hbKey": "group_id", // 計算環比使用
"filterKey": "", // 過濾器
"filterValue": ""
}
第二個配置是資料對象DTO的配置(通過注解對複合名額計算進行配置定義)
DTO配置主要使用了三個注解,@FromDO,@FunctionDO,@IgnoreAssign
@From 定義簡單按key取值
@FunctionDO定義複合名額計算規則(hb,tb,zb,avg,sum等)
@IgnoreAssign 對象映射是字段忽略
同時支援定義的複合名額對象的名額計算指派。
@Datapublic class StockSingleVO extends BaseVO { // 簡單取值,預設駝峰轉下劃線取字段group_id
private Long groupId; @FromDO("'商品組:'+group_name") private String groupName; // 簡單取值,直接在庫量
@FromDO("stock_cnt_zhuzhan_1d") private Number stockCnt; // 環比,對在庫量字段計算環比
@FromDO("stock_cnt_zhuzhan_1d") @FunctionDO(types = FunctionTypeEnum.HB) private Number stockCntHB; // 簡單取值,計算在庫+在途量
@FromDO(value = "stock_cnt_zhuzhan_1d+onway_cnt_zhuzhan_1d") private Number stockAndOnwayCnt; // 環比計算,計算在庫+在途的環比
@FromDO(value = "stock_cnt_zhuzhan_1d+onway_cnt_zhuzhan_1d") @FunctionDO(types = FunctionTypeEnum.HB) private Number stockAndOnwayCntHB; // 占比計算
@FromDO(value = "stock_cnt_zhuzhan_1d+onway_cnt_zhuzhan_1d") @FunctionDO(types = FunctionTypeEnum.ZB) private Number stockAndOnwayCntZB;
}
資料過濾規則配置和資料對象中定義的複合名額計算配置一起支援資料對象映射,這樣可以減少大量重複指派取值以及手動計算複合名額的工作。同時配合使用資料變換子產品和資料對象映射能夠釋放更大的靈活性,将資料變換子產品、資料對象映射、複合名額計算子產品解耦。
子產品查詢代碼示例:
/**
* condition1,condition2為構造的名額查詢條件
*/EngineRequest request1 = new EngineRequest(EngineType.DQS, condition1, EngineResultTypeEnum.LIST); // 構造查詢請求1EngineRequest request2 = new EngineRequest(EngineType.DQS, condition2, EngineResultTypeEnum.LIST); // 構造查詢請求2// 構造配置1(資料過濾配置 -> 配置目标日期、環比日期、計算環比目标分組key)EngineConvertConfig config = new EngineConvertConfig<>(StockSingleVO.class, "2018-12-07", "group_id", null, null);
config.setHbDate("2018-12-06");
Listlist = requestEngineManager.initThreadLocal()
.addRequest(dk1, request1)
.addRequest(dk2, request2)
.execute()
.compose(true, Arrays.asList("date_id", "group_id"), dk_new, dk1, dk2) // 請求結果連接配接
.converToList(dk_new, config); // 資料對象映射// 前端資料結構組裝SmartQueryResult result = BaseVO.assembleResult(list, StockSingleVO.class);
五、總結
資料産品中很多通用的部分可以抽出來作為單獨子產品或者服務。文中介紹的複合名額查詢子產品已經在大麥商品資料營運平台中實踐,它把資料産品名額查詢、計算以及對象映射等公共部分提取出來,有效的提高開發效率并能夠降低開發成本。
相關文章:
【推薦】 git submodule