<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          數(shù)倉(八)從0到1簡單搭建加載數(shù)倉DWD層(用戶行為日志數(shù)據(jù)解析)

          共 14026字,需瀏覽 29分鐘

           ·

          2022-01-17 01:56


          數(shù)倉(一)簡介數(shù)倉,OLTP和OLAP
          數(shù)倉(二)關(guān)系建模和維度建模
          數(shù)倉(三)簡析阿里、美團、網(wǎng)易、恒豐銀行、馬蜂窩5家數(shù)倉分層架構(gòu)
          數(shù)倉(四)數(shù)據(jù)倉庫分層
          數(shù)倉(五)元數(shù)據(jù)管理系統(tǒng)解析
          數(shù)倉(六)從0到1簡單搭建數(shù)倉ODS層(埋點日志 + 業(yè)務數(shù)據(jù))
          數(shù)倉(七)從0到1簡單搭建加載數(shù)倉DIM層以及拉鏈表處理
          上一節(jié)我們講解了數(shù)倉DIM維度層的搭建和使用,并且講解了拉鏈表的概念和使用。這節(jié)我們講解DWD層關(guān)于用戶行為日志數(shù)據(jù)的搭建和使用。下一次分享DWD層關(guān)于業(yè)務數(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類型字符串。



          2、日志解析思路
          我們根據(jù)ODS層日志數(shù)據(jù)內(nèi)容來解析到DWD層分為5個表,也可以根據(jù)啟動日志和事件日志來解析到DWD層分為2個表。前者是根據(jù)內(nèi)容抽象來解析,顆粒度更細,一般大公司使用。后者比較簡單就是根據(jù)數(shù)據(jù)的類型來解析。我們這里采用第一種方式,把頁面日志和啟動日志信息的數(shù)據(jù)需要裝載到DWD層里面的五張表。所以DWD層就是解析這五張表。




          我們下面來依次解析每一張表。



          二、DWD層-啟動日志表


          啟動日志表中每一行數(shù)據(jù)對應一個啟動記錄,一個啟動記錄應該包含日志中的公共信息和啟動信息。
          常規(guī)解析思路是:可以先將所有包含start字段的日志過濾出來,然后使用get_json_object函數(shù)解析每個字段。


          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ù)后加載。


          SQL實現(xiàn)
          insert?overwrite?table?dwd_start_log?partition(dt='2020-06-14')select    get_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;
          注意:這里hive需要使用HiveInputFormat,而不是CombineHiveInputFormat
          SET hive.input.format=org.apache.hadoop.hive.ql.io.HiveInputFormat;
          后者不能識別lzo.index的索引文件,會把索引文件當做普通文件來處理,并且導致lzo文件無法切片。而ODS層數(shù)據(jù)我們處理的時候是帶lzo索引文件的。


          三、DWD層-頁面日志表


          頁面日志表和啟動日志表的處理邏輯一樣。頁面日志表中每一行數(shù)據(jù)對應一個頁面的訪問記錄,一個頁面訪問記錄應該包含日志中的公共信息和頁面信息。
          常規(guī)解析思路是:也是先將所有包含page字段的日志過濾出來,然后使用get_json_object函數(shù)解析每個字段。
          1、頁面日志表結(jié)構(gòu)


          • 分區(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');
          4、裝載數(shù)據(jù)
          和啟動日志表一樣,首日和每日加載數(shù)據(jù)分區(qū)都是一樣的策略,每天DWD層從ODS層獲取數(shù)據(jù)后加載。
          insert overwrite table dwd_page_log partition(dt='2020-06-14')select    get_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層-動作日志表


          動作日志表,比前面啟動日志和頁面信息表要復雜的多。動作日志表中每行數(shù)據(jù)對應的是用戶的一個動作記錄,一個動作記錄應當包含公共信息、頁面信息以及動作信息。
          常規(guī)解析思路是:先將包含action字段的日志過濾出來,然后通過UDF、UDTF函數(shù)(用戶定義表生成函數(shù)user-defined table-generating function)將action數(shù)組“炸裂”(類似于explode函數(shù)的效果),然后使用get_json_object函數(shù)解析每個字段。
          1、頁面日志表結(jié)構(gòu)


          根據(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');
          3、創(chuàng)建UDTF函數(shù)
          我們通過java代碼來實現(xiàn)功能,并且通過MAVEN來打成jar。然后上傳到HDFS集群,給hive做關(guān)聯(lián)調(diào)用。當然也可以使用hive自帶的UDTF函數(shù)含來完成解析。但是后者必須是在hive的客戶端完成,必須帶hive庫名,不夠靈活。
          3.1、編寫java代碼實現(xiàn)UDTF功能
          1、pom.xml文件中hive版本依賴
          我這里hive的版本是3.1.2
                          org.apache.hive        hive-exec        3.1.2    
          2、項目工程結(jié)構(gòu)
          這個比較簡單。




          3、ExplodeJSONArray類編寫
          思路是:
          (1)ExplodeJSONArray繼承hive的GenericUDTF類;
          (2)實現(xiàn)initialize、process、close三個方法;
          initialize方法:主要對解析的變量名、變量類型做校驗
          process方法:完成對“JSON數(shù)組炸裂成單一元素”的功能。這里是一行in,多行out,并且forward到元素里面
          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 */ @Override public 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;
          //定義返回值名稱 List fieldNames = new ArrayList();
          //定義返回值類型 List fieldOIs = new ArrayList();
          //把items添加到返回值名稱 fieldNames.add("items");
          //調(diào)用檢驗方法對items fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
          //3、返回類型 return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames,fieldOIs);????}
          /** * 2、第二步重寫process方法 * * function:炸裂功能 * @author qiusheng * @param objects * @throws HiveException */ @Override public void process(Object[] objects) throws HiveException{ //1、獲取傳入的數(shù)據(jù)就是actions String 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ù)組,長度就是1 String[] result = new String[1]; //getString(i)把元素取出來,添加到這個數(shù)組中 result[0] = actions.getString(i); //寫到String數(shù)組里面 forward(result); } }
          /** * @author qiusheng * @throws HiveException */ @Override public void close() throws HiveException{
          }}


          3.2、通過maven打成jar包




          3.3、上傳jar包到hadoop集群路徑下

          (1)先上傳jar包到CentOS集群的node1節(jié)點



          (2)上傳到HDFS集群系統(tǒng)
          先創(chuàng)建目錄functions/hive/jars
          [qiusheng@node01 module]$ hadoop fs -mkdir -p /functions/hive/jars
          檢查查看目錄是否已經(jīng)創(chuàng)建;


          上傳jar包到這個目錄/functions/hive/jars


          3.4、創(chuàng)建UDTF和jar包關(guān)聯(lián)
          在hive客戶端,創(chuàng)建function關(guān)聯(lián)jar包
          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';




          4、裝載數(shù)據(jù)
          所需要的字段都拼接齊全了,我們來寫裝載語句。
          insert overwrite table dwd_action_log partition(dt='2020-06-14')select    get_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;
          這里lateral view意思是:為原始表的每行調(diào)用UDTF,UTDF會把一行拆分成一或者多行,lateral view再把結(jié)果組合,產(chǎn)生一個支持別名表的虛擬表action。
          from?ods_log?lateral?view?explode_json_array(get_json_object(line,'$.actions'))?tmp?as?action
          這樣我們動作日志表就完成了。

          五、DWD層-曝光日志表


          曝光日志表和動作日志表,解析一樣。曝光日志表中每行數(shù)據(jù)對應一個曝光記錄,一個曝光記錄應當包含公共信息、頁面信息以及曝光信息。
          常規(guī)思路:先將包含display字段的日志過濾出來,然后通過UDTF函數(shù),將display數(shù)組“炸開”(類似于explode函數(shù)的效果),然后使用get_json_object函數(shù)解析每個字段。
          1、頁面日志表結(jié)構(gòu)



          后面建表、做UDTF、一系列操作、裝載數(shù)據(jù)和動作日志表一樣。留給讀者自行實踐一下。

          六、DWD層-錯誤日志表


          動作日志表,比前面啟動日志和頁面信息表要復雜一些。錯誤日志表中每行數(shù)據(jù)對應一個錯誤記錄,為方便定位錯誤,一個錯誤記錄應當包含與之對應的公共信息、頁面信息、曝光信息、動作信息、啟動信息以及錯誤信息。先將包含err字段的日志過濾出來,然后使用get_json_object函數(shù)解析所有字段。
          1、頁面日志表結(jié)構(gòu)




          含有2個actions 、displays數(shù)組的UDTF炸裂。
          2、創(chuàng)建表結(jié)構(gòu)
          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');
          注意:
          對動作數(shù)組和曝光數(shù)組做處理,如需分析錯誤與單個動作或曝光的關(guān)聯(lián),可先使用explode_json_array函數(shù)將數(shù)組“炸開”,再使用get_json_object函數(shù)獲取具體字
          3、裝載表數(shù)據(jù)
          insert overwrite table dwd_error_log partition(dt='2020-06-14')select    get_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_log where dt='2020-06-14'and?get_json_object(line,'$.err')?is?not?null;
          這樣我們就完成了最后一張表從ODS層到DWD層的解析

          瀏覽 39
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  草莓视频91 | 性少妇69 | 亚洲爽爽| 孕妇╳ⅹ孕交XXX | 人人看人人撸 |