
實時數據倉庫相比較離線數倉,實時性更高,這就要求數據流盡量短,層次相對簡化,相比較離線,這里的ods和明細表就可以合并等于從業務庫實時同步數據寫入到kafka中的一個topic。關于數據采集這里不做討論,部門是部署腳本監控業務庫的DML語句,將create、update、delete的數據實時同步寫入到kafka。那么我們要實現多個數據流實時關聯合并組成寬記錄,
這里有幾點要求
實時性
延遲要低,分鐘級、秒級
一致性
保證數據的準確一致性,不能丟數據,寧可多算不可漏掉數據
易用性
實時數據有三種狀態增、刪、改,對于下游使用這個寬表要做到易用,能抽象通用的匯總模型
思路
1.如何解決誰先到誰后到誰遲遲不到的問題?storm的例子中有兩個數據流的join的案例,是使用內存來存儲先到的數據,設置過期時間,這樣就有兩個問題,過期就表明會丟數據,由于實時數據特點有增有改有刪,即使關聯上已經往下發送,但是考慮到update、delete,那內存中數據不能立即clear,內存消耗也是個問題,很容易吃滿內存,溢出。如果放到內存不合適,那就借助存儲數據庫,比如hbase、redis,mysql等,這里第一版是使用的hbase,如果后期性能跟不上可升級為redis。2.如何解決更新、刪除數據對之前合并結果的影響這里定義了一種操作叫做逆操作。比如金額最早為5元,后續變成3元,那這條記錄我會把前后都傳遞給下游,3|5,下游如果有匯總,那就 - 5 + 3,如果只是合并不做計算,那就只取 3就可以了。
整體的結構圖

如圖上所示,除了消費各自topic的數據寫法不一致外,其他節點都是將左邊和右邊合并,然后繼續往下發送,所以這些點都是可以抽象成一個通用的bolt來處理。
GeneralMerge
Map<String, String> left = (Map<String, String>)tuple.getValueByField("left");Map<String, String> right = (Map<String, String>)tuple.getValueByField("right");if(left != null && right!=null) { left.putAll(right);}獲取左邊右邊,然后merge起來 圖里邊最小的單元如下圖,都是2個流關聯組成寬記錄往下emit,然后再跟右邊的結果合并。一直到最后的節點

仔細觀察,整個拓撲結構都是由一個個這樣的最小單元組成的,那么最小單元是否可以繼續抽象成通用的模型呢。拆分下這個單元,就是由2個數據流、關聯關系、流到哪、數據流數據要存儲到什么表中抽象出來就是左邊的表,右表的表,表和表之間的關系【leftjoin、innerjoin】這些信息抽象成對象
Param leftParam = new Params(hb_table1,hb_table1_index,"L|R");Param rightParam = new Params(hb_table2,hb_table2_index,"L&R");
hb_table{1,2}代表數據存儲的表名hb_table{1,2}_index代表數據自己的主鍵和另一方關聯鍵的索引表,比如不都是主鍵之間進行joinL|R代表 左關聯,代表左邊的數據到了,會先去找右邊的數據,即使右邊的數據還未到,發現關聯關系為L|R,左關聯,左邊也往下發送。最小單元的處理過程就是 先定義數據流的信息,讀取topic,保存數據、索引表,找對方然后發送,如下代碼
//獲取數據流信息Map<String, String> leftMap = preParseProcess.process(tuple, leftParam); leftMap = save(tuple, leftMap); //保存emit(tuple,collector, leftMap); //找對方然后發送
數據傳遞
新增類型,封裝到map中往下傳遞
更新類型,在更新的字段上標記新值、舊值,比如 new_value + split + old_value,下游使用時,已經告訴了你更新后的值、更新后的值,自己根據業務處理即可
刪除類型,在每個字段后標記刪除標簽,如 old_value + split + DEL
優缺點
中間結果多、表多、操作hbase頻繁對hbase有一定的壓力、拓撲結構比較多,運維比較麻煩
將多個數據流匯總成一個通用寬表,下游再有這主題的任何實時需求都可以很方便的使用和編寫。提高工作效率,減少驗證數據成本。
結論
方案可行
延遲在分鐘內
除了運單等變化的字段其他字段可以與離線數據吻合
提高實時開發的效率
待續....困的受不鳥了。。。。