數(shù)倉(八)從0到1簡單搭建加載數(shù)倉DWD層(用戶行為日志數(shù)據(jù)解析)
一、DWD層用戶行為日志解析結(jié)構(gòu)
DWD層是對用戶的日志行為進行解析,以及對業(yè)務數(shù)據(jù)采用維度模型的方式重新建模(維度退化)。本節(jié)我們先來回顧一下用戶行為日志的結(jié)構(gòu)。
1、前端埋點日志信息
前端埋點日志信息都是JSON格式形式,主要包括兩方面:
(1)啟動日志;(2)事件日志;
我們之前已經(jīng)把前端埋點的日志信息,寫到ODS層ods_log表了,傳入的參數(shù)是一個String類型字符串即一條日志信息一個String類型字符串。


二、DWD層-啟動日志表
1、啟動日志表結(jié)構(gòu)
分區(qū):
????????dt = 2020-06-14
過濾條件:
????????利用get_json_object函數(shù),解析start內(nèi)容不為空說明是啟動日志信息;
范圍
????????包括:公共信息common、啟動信息start、啟動app時間ts;

2、創(chuàng)建表結(jié)構(gòu)
DROP TABLE IF EXISTS dwd_start_log;CREATE EXTERNAL TABLE dwd_start_log(`area_code` STRING COMMENT '地區(qū)編碼',`brand` STRING COMMENT '手機品牌',`channel` STRING COMMENT '渠道',`is_new` STRING COMMENT '是否首次啟動',`model` STRING COMMENT '手機型號',`mid_id` STRING COMMENT '設備id',`os` STRING COMMENT '操作系統(tǒng)',`user_id` STRING COMMENT '會員id',`version_code` STRING COMMENT 'app版本號',`entry` STRING COMMENT 'icon手機圖標 notice 通知 install 安裝后啟動',`loading_time` BIGINT COMMENT '啟動加載時間',`open_ad_id` STRING COMMENT '廣告頁ID ',`open_ad_ms` BIGINT COMMENT '廣告總共播放時間',`open_ad_skip_ms` BIGINT COMMENT '用戶跳過廣告時點',`ts` BIGINT COMMENT '時間') COMMENT '啟動日志表'PARTITIONED BY (`dt` STRING) -- 按照時間創(chuàng)建分區(qū)STORED AS PARQUET -- 采用parquet列式存儲LOCATION '/warehouse/gmall/dwd/dwd_start_log' -- 指定在HDFS上存儲位置TBLPROPERTIES('parquet.compression'='lzo')?--?采用LZO壓縮;
3、裝載數(shù)據(jù)
首日和每日加載數(shù)據(jù)分區(qū)都是一樣的策略,每天DWD層從ODS層獲取數(shù)據(jù)后加載。

insert?overwrite?table?dwd_start_log?partition(dt='2020-06-14')selectget_json_object(line,'$.common.ar'),get_json_object(line,'$.common.ba'),get_json_object(line,'$.common.ch'),get_json_object(line,'$.common.is_new'),get_json_object(line,'$.common.md'),get_json_object(line,'$.common.mid'),get_json_object(line,'$.common.os'),get_json_object(line,'$.common.uid'),get_json_object(line,'$.common.vc'),get_json_object(line,'$.start.entry'),get_json_object(line,'$.start.loading_time'),get_json_object(line,'$.start.open_ad_id'),get_json_object(line,'$.start.open_ad_ms'),get_json_object(line,'$.start.open_ad_skip_ms'),get_json_object(line,'$.ts')from ods_logwhere dt='2020-06-14'and?get_json_object(line,'$.start')?is?not?null;
SET hive.input.format=org.apache.hadoop.hive.ql.io.HiveInputFormat;三、DWD層-頁面日志表
分區(qū):
????????dt = 2020-06-14
過濾條件:
????????利用get_json_object函數(shù),解析page內(nèi)容不為空說明是頁面日志信息;
范圍
????????包括:公共信息common、頁面信息page、啟動app時間ts;

3、創(chuàng)建表結(jié)構(gòu)
drop table if exists dwd_page_log;CREATE EXTERNAL TABLE dwd_page_log(`area_code` string COMMENT '地區(qū)編碼',`brand` string COMMENT '手機品牌',`channel` string COMMENT '渠道',`model` string COMMENT '手機型號',`mid_id` string COMMENT '設備id',`os` string COMMENT '操作系統(tǒng)',`user_id` string COMMENT '會員id',`version_code` string COMMENT 'app版本號',`during_time` bigint COMMENT '持續(xù)時間毫秒',`page_item` string COMMENT '目標id ',`page_item_type` string COMMENT '目標類型',`last_page_id` string COMMENT '上頁類型',`page_id` string COMMENT '頁面ID ',`source_type` string COMMENT '來源類型',`ts` bigint) COMMENT '頁面日志表'PARTITIONED BY (dt string)stored as parquetLOCATION '/warehouse/gmall/dwd/dwd_page_log'TBLPROPERTIES('parquet.compression'='lzo');
insert overwrite table dwd_page_log partition(dt='2020-06-14')selectget_json_object(line,'$.common.ar'),get_json_object(line,'$.common.ba'),get_json_object(line,'$.common.ch'),get_json_object(line,'$.common.md'),get_json_object(line,'$.common.mid'),get_json_object(line,'$.common.os'),get_json_object(line,'$.common.uid'),get_json_object(line,'$.common.vc'),get_json_object(line,'$.page.during_time'),get_json_object(line,'$.page.item'),get_json_object(line,'$.page.item_type'),get_json_object(line,'$.page.last_page_id'),get_json_object(line,'$.page.page_id'),get_json_object(line,'$.page.sourceType'),get_json_object(line,'$.ts')from ods_logwhere dt='2020-06-14'and?get_json_object(line,'$.page')?is?not?null;
四、DWD層-動作日志表

根據(jù)圖示我們可以看到:
(1)需要自定義創(chuàng)建UDTF函數(shù)
來完成對actions動作數(shù)組的“炸裂”,實現(xiàn)“一行輸入,多行輸出”的需求。即輸入JSON數(shù)組字符串a(chǎn)ctions,輸出每一個JSON數(shù)組元素action。
(2)然后通過get_json_object(action,$action元素字段)獲取信息;
分區(qū):
????????dt = 2020-06-14
過濾條件:
? ? ? 利用get_json_object函數(shù),解析actions內(nèi)容不為空;
范圍
????????包括:公共信息common、頁面信息page、動作信息、啟動app時間ts;
2、創(chuàng)建表結(jié)構(gòu)
drop table if exists dwd_action_log;CREATE EXTERNAL TABLE dwd_action_log(`area_code` string COMMENT '地區(qū)編碼',`brand` string COMMENT '手機品牌',`channel` string COMMENT '渠道',`model` string COMMENT '手機型號',`mid_id` string COMMENT '設備id',`os` string COMMENT '操作系統(tǒng)',`user_id` string COMMENT '會員id',`version_code` string COMMENT 'app版本號',`during_time` bigint COMMENT '持續(xù)時間毫秒',`page_item` string COMMENT '目標id ',`page_item_type` string COMMENT '目標類型',`last_page_id` string COMMENT '上頁類型',`page_id` string COMMENT '頁面id ',`source_type` string COMMENT '來源類型',`action_id` string COMMENT '動作id',`item` string COMMENT '目標id ',`item_type` string COMMENT '目標類型',`ts` bigint COMMENT '時間') COMMENT '動作日志表'PARTITIONED BY (dt string)stored as parquetLOCATION '/warehouse/gmall/dwd/dwd_action_log'TBLPROPERTIES('parquet.compression'='lzo');
org.apache.hive hive-exec 3.1.2

package com.qiusheng.hive.udtf;import org.apache.hadoop.hive.ql.exec.MapredContext;import org.apache.hadoop.hive.ql.exec.UDFArgumentException;import org.apache.hadoop.hive.ql.metadata.HiveException;import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;import org.json.JSONArray;import java.util.ArrayList;import java.util.List;/*** @author qiusheng?*?@date?2021年11月04日**/public class ExplodeJSONArray extends GenericUDTF{/*** 1、第一步需要自定義的類繼承GenericUDTF類* 并且重寫initialize方法;* 返回StructObjectInspector對象** @qiusheng* @param argOIs* @return* @throws UDFArgumentException*/@Overridepublic StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException{//1、判斷參數(shù)的合法性argOI實際就是actions//(1)判斷參數(shù)的個數(shù)(需要傳遞一個參數(shù))//如果傳遞的參數(shù)個數(shù)不是1個,則拋異常;if(argOIs.getAllStructFieldRefs().size() != 1){throw new UDFArgumentException("參數(shù)個數(shù)錯誤!只需要1個參數(shù)......");}//(2)判斷傳遞的參數(shù)類型,必須是String類型//如果不是string類型,則拋異常;if(!"String".equals(argOIs.getAllStructFieldRefs().get(0).getFieldObjectInspector().getTypeName())){throw new UDFArgumentException("參數(shù)類型不對!應該是String類型......");}//2、返回StructObjectInspector對象//第一個參數(shù):變量名,類型是List數(shù)組; //第二個參數(shù):檢驗變量,類型是List; //定義返回值名稱ListfieldNames = new ArrayList (); //定義返回值類型ListfieldOIs = new ArrayList (); //把items添加到返回值名稱fieldNames.add("items");//調(diào)用檢驗方法對itemsfieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);//3、返回類型return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames,fieldOIs);????}/*** 2、第二步重寫process方法** function:炸裂功能* @author qiusheng* @param objects* @throws HiveException*/@Overridepublic void process(Object[] objects) throws HiveException{//1、獲取傳入的數(shù)據(jù)就是actionsString jsonArray = objects.toString();//2、把傳入的數(shù)據(jù)(string)類型轉(zhuǎn)化為JSON數(shù)組JSONArray類型JSONArray actions = new JSONArray(jsonArray);//3、循環(huán)取出JSONArray對象中的JSON,并且寫出來for (int i = 0;i < actions.length();i++){//定義一個字符串數(shù)組,長度就是1String[] result = new String[1];//getString(i)把元素取出來,添加到這個數(shù)組中result[0] = actions.getString(i);//寫到String數(shù)組里面forward(result);}}/*** @author qiusheng* @throws HiveException*/@Overridepublic void close() throws HiveException{}}
3.2、通過maven打成jar包

3.3、上傳jar包到hadoop集群路徑下
(1)先上傳jar包到CentOS集群的node1節(jié)點

[qiusheng@node01 module]$ hadoop fs -mkdir -p /functions/hive/jars
上傳jar包到這個目錄/functions/hive/jars

hive (gmall)>create?function?explode_json_array?as?'com.qiusheng.hive.udtf.ExplodeJSONArray'?using?jar?'hdfs://mode01:8020/functions/hive/jars/HiveDWDActionLog-1.0-SNAPSHOT.jar';
insert overwrite table dwd_action_log partition(dt='2020-06-14')selectget_json_object(line,'$.common.ar'),get_json_object(line,'$.common.ba'),get_json_object(line,'$.common.ch'),get_json_object(line,'$.common.md'),get_json_object(line,'$.common.mid'),get_json_object(line,'$.common.os'),get_json_object(line,'$.common.uid'),get_json_object(line,'$.common.vc'),get_json_object(line,'$.page.during_time'),get_json_object(line,'$.page.item'),get_json_object(line,'$.page.item_type'),get_json_object(line,'$.page.last_page_id'),get_json_object(line,'$.page.page_id'),get_json_object(line,'$.page.sourceType'),get_json_object(action,'$.action_id'),get_json_object(action,'$.item'),get_json_object(action,'$.item_type'),get_json_object(action,'$.ts')from ods_log lateral view explode_json_array(get_json_object(line,'$.actions')) tmp as actionwhere dt='2020-06-14'and?get_json_object(line,'$.actions')?is?not?null;
from?ods_log?lateral?view?explode_json_array(get_json_object(line,'$.actions'))?tmp?as?action五、DWD層-曝光日志表

六、DWD層-錯誤日志表

drop table if exists dwd_error_log;CREATE EXTERNAL TABLE dwd_error_log(`area_code` string COMMENT '地區(qū)編碼',`brand` string COMMENT '手機品牌',`channel` string COMMENT '渠道',`model` string COMMENT '手機型號',`mid_id` string COMMENT '設備id',`os` string COMMENT '操作系統(tǒng)',`user_id` string COMMENT '會員id',`version_code` string COMMENT 'app版本號',`page_item` string COMMENT '目標id ',`page_item_type` string COMMENT '目標類型',`last_page_id` string COMMENT '上頁類型',`page_id` string COMMENT '頁面ID ',`source_type` string COMMENT '來源類型',`entry` string COMMENT ' icon手機圖標 notice 通知 install 安裝后啟動',`loading_time` string COMMENT '啟動加載時間',`open_ad_id` string COMMENT '廣告頁ID ',`open_ad_ms` string COMMENT '廣告總共播放時間',`open_ad_skip_ms` string COMMENT '用戶跳過廣告時點',`actions` string COMMENT '動作',`displays` string COMMENT '曝光',`ts` string COMMENT '時間',`error_code` string COMMENT '錯誤碼',`msg` string COMMENT '錯誤信息') COMMENT '錯誤日志表'PARTITIONED BY (dt string)stored as parquetLOCATION '/warehouse/gmall/dwd/dwd_error_log'TBLPROPERTIES('parquet.compression'='lzo');
insert overwrite table dwd_error_log partition(dt='2020-06-14')selectget_json_object(line,'$.common.ar'),get_json_object(line,'$.common.ba'),get_json_object(line,'$.common.ch'),get_json_object(line,'$.common.md'),get_json_object(line,'$.common.mid'),get_json_object(line,'$.common.os'),get_json_object(line,'$.common.uid'),get_json_object(line,'$.common.vc'),get_json_object(line,'$.page.item'),get_json_object(line,'$.page.item_type'),get_json_object(line,'$.page.last_page_id'),get_json_object(line,'$.page.page_id'),get_json_object(line,'$.page.sourceType'),get_json_object(line,'$.start.entry'),get_json_object(line,'$.start.loading_time'),get_json_object(line,'$.start.open_ad_id'),get_json_object(line,'$.start.open_ad_ms'),get_json_object(line,'$.start.open_ad_skip_ms'),get_json_object(line,'$.actions'),get_json_object(line,'$.displays'),get_json_object(line,'$.ts'),get_json_object(line,'$.err.error_code'),get_json_object(line,'$.err.msg')from ods_logwhere dt='2020-06-14'and?get_json_object(line,'$.err')?is?not?null;
