电商App项目的离线数仓
参考项目网址:(1)https://hub.fastgit.org/HammingMa/gmall-dw
(2)warehouse: 电商数据仓库
相关概念
SKU,SPU
首先通过检索搜索出来的商品列表中,每个商品都是一个SKU。每个SKU都有自己独立的库存数。也就是说每一个商品详情展示都是一个SKU。
比如,咱们购买一台iPhoneX手机,iPhoneX手机就是一个SPU,但是你购买的时候,不可能是以iPhoneX手机为单位买的,商家也不可能以iPhoneX为单位记录库存。必须要以什么颜色什么版本的iPhoneX为单位。比如,你购买的是一台银色、128G内存的、支持联通网络的iPhoneX ,商家也会以这个单位来记录库存数。那这个更细致的单位就叫库存单元(SKU)。
维度建模:
维度建模是一种将数据结构化的逻辑设计方法。首先介绍两个最重要的概念:事实,维度。
维度建模将业务过程分为度量和上下文。度量是由业务过程和支持它们的业务源系统获取的,常常以数值形式体现,我们称它们为“事实”。
事实由大量的文本形式的上下文包围着,这些文本形式的上下文是伴随着事实产生而确定的。这些上下文被直观的分割成多个独立的模块我们将其称作“维”。维度描述了事实的相关信息,比如who,what,when,where,why等等。
例如小明在淘宝上下单100元买了两箱牛奶,100元和2箱就是事实,而小明,牛奶,时间,配送地址等等这些信息就是维
维度模型主要应用于OLAP系统中,因为关系模型虽然冗余少,但是在大规模数据,跨表分析统计查询过程中,会造成多表关联,这会大大降低执行效率。
所以把相关各种表整理成两种:事实表和维度表两种。所有维度表围绕着事实表进行解释。
数据仓库里表的分类
1)实体表:
一个现实存在的业务对象,比如用户,商品,商家,销售员等等。
例如用户表可以包含
id |
姓名 |
生日 |
性别 |
邮箱 |
用户等级 |
创建时间 |
同步策略:数据量比较小,通常可以做每日全量,就是每天存一份完整数据。即每日全量。
2)维度表:
一般是指对应一些业务状态,代码的解释表。也可以称之为码表。
比如地区表,订单状态,支付方式,审批状态,商品分类等等。,
例如订单状态表可以包含
编号 |
订单状态名称 |
同步策略:
针对可能会有变化的状态数据可以存储每日全量。
没变化的客观世界的维度(比如性别,地区,民族,政治成分,鞋子尺码)可以就存一份固定值。
3)事务型事实表,一般指随着业务发生不断产生的数据。特点是一旦发生不会再变化。
一般比如,交易流水,操作日志,出库入库记录等等。
例如交易流水表包含
编号 |
对外业务编号 |
订单编号 |
用户编号 |
支付宝交易流水编号 |
支付金额 |
交易内容 |
支付类型 |
支付时间 |
同步策略:以做成每日增量表,即每日创建一个分区存储。
4)周期型事实表,一般指随着业务发生不断产生的数据。
与事务型不同的是,数据会随着业务周期性的推进而变化。
比如订单,其中订单状态会周期性变化。再比如,请假、贷款申请,随着批复状态在周期性变化。
例如订单表包含:
订单编号 |
订单金额 |
订单状态 |
用户id |
支付方式 |
支付流水号 |
创建时间 |
操作时间 |
同步策略:需要得到每日新增及变化量。为方便的取到某个时间切片的快照数据,用利用每日新增和变化表,制作一张拉链表。
原始数据
从日志中抽取用户行为数据(json格式),包括启动数据和事件行为数据(这里模拟生成),存到指定文件。还有从数据库表里获得的相关数据,也存到指定文件。
(1)启动数据包括 :
//设备id // 用户id // 程序版本号 5,6等 //程序版本名 v1.1.1 // 安卓系统版本 //设置日志类型 start appStart.setEn("start"); // 语言 es,en,pt // 渠道号 从哪个渠道来的 appStart.setSr(getRandomChar(1)); // 区域 // 手机品牌 ba ,手机型号 md,就取2位数字了 // 嵌入sdk的版本 // gmail // 屏幕宽高 hw // 客户端产生日志时间 // 手机网络模式 3G,4G,WIFI // 拉丁美洲 西经34°46′至西经117°09;北纬32°42′至南纬53°54′ // 经度 // 纬度 // 入口 // 开屏广告类型 // 状态 // 加载时长 // 失败码 // 扩展字段
数据例子
{"action":"1","ar":"MX","ba":"HTC","detail":"201","en":"start","entry":"1","extend1":"","g":"RWT7HN31@gmail.com","hw":"750*1134","l":"pt","la":"-9.0","ln":"-54.0","loading_time":"1","md":"HTC-17","mid":"31","nw":"WIFI","open_ad_type":"2","os":"8.2.6","sr":"C","sv":"V2.0.8","t":"1632675539796","uid":"31","vc":"11","vn":"1.3.6"}
(2)事件行为数据分为下面几种:
1.商品点击,展示 2.商品详情页 3. 商品列表页 4.广告 5.消息通知 6.用户前台活跃 7.用户后台活跃 8. 故障日志 9.用户评论 10. 用户收藏 11.用户点赞
数据例子
1632709453025|{"cm":{"ln":"-44.7","sv":"V2.8.4","os":"8.1.1","g":"TTF03B8I@gmail.com","mid":"32","nw":"3G","l":"en","vc":"17","hw":"750*1134","ar":"MX","uid":"32","t":"1632706083536","la":"13.4","md":"sumsung-5","vn":"1.0.3","ba":"Sumsung","sr":"M"},"ap":"app","et":[{"ett":"1632637800076","en":"loading","kv":{"extend2":"","loading_time":"0","action":"3","extend1":"","type":"2","type1":"201","loading_way":"2"}},{"ett":"1632628780999","en":"notification","kv":{"ap_time":"1632652229365","action":"2","type":"3","content":""}},{"ett":"1632652586798","en":"active_foreground","kv":{"access":"","push_id":"1"}},{"ett":"1632649878013","en":"favorites","kv":{"course_id":7,"id":0,"add_time":"1632616387020","userid":3}},{"ett":"1632631836363","en":"praise","kv":{"target_id":5,"id":8,"type":3,"add_time":"1632668604153","userid":4}}]}
(3) 调用sqoop的脚本,db-》文件,从数据库表里获得相关数据,有全量和增量两种方法:
1.订单详情表表(增量) 2.SKU商品表(全量表) 3.用户表(增量及更新) 4.商品一级分类表(全量)5.商品二级分类表(全量)6.商品三级分类表(全量)7.支付流水表(增量)8.商品省份表(全量) 9.商品地区表(全量) 10.商品品牌表(全量) 11.订单状态表(增量)12.SPU商品表(全量)13.商品评论表(增量)14,退单表(增量)15.加购表(全量)16.商品收藏表(全量)17.优惠券领用表(新增及变化)18.优惠券表(全量)19.活动表(全量)20.活动订单关联表(增量)21.优惠规则表(全量)22.编码字典表(全量)
#! /bin/bash##这个是sqoop的路径,date的日期
sqoop=/opt/module/sqoop/bin/sqoop
do_date=`date -d '-1 day' +%F`if [ -n "$2" ]; thendo_date=$2
fi##这是其中的一个函数,通过该函数可以进行sqoop的传输
##注意第一 $1 不是传入的参数,而是传入该函数的sql语句,$2 是传入数据库表名
##最后的hadoop是传入的lzo压缩格式
import_data(){
##如果是CDH环境,请把上面的路径注释掉,然后改为 sqoop import \
#sqoop import \
$sqoop import \
--connect jdbc:mysql://hadoop103:3306/gmall \
--username root \
--password 123456 \
--target-dir /origin_data_cdh/gmall/db/$1/$do_date \
--delete-target-dir \
--query "$2 and \$CONDITIONS" \
--num-mappers 1 \
--fields-terminated-by '\t' \
--compress \
--compression-codec lzop \
--null-string '\\N' \
--null-non-string '\\N'hadoop jar /opt/module/hadoop-2.7.2/share/hadoop/common/hadoop-lzo-0.4.20.jar com.hadoop.compression.lzo.DistributedLzoIndexer /origin_data_cdh/gmall/db/$1/$do_date
}
## 函数中也分增量和全量表,看到where 1=1 的都是全量表,带有日期的都是增量表import_order_info(){import_data order_info "selectid, final_total_amount, order_status, user_id, out_trade_no, create_time, operate_time,province_id,benefit_reduce_amount,original_total_amount,feight_fee from order_infowhere (date_format(create_time,'%Y-%m-%d')='$do_date' or date_format(operate_time,'%Y-%m-%d')='$do_date')"
}##这里省略其他表
##进行参数调用
case $1 in"order_info")import_order_info
;;
##这里省略其他表
;;
"all")import_base_category1import_base_category2import_base_category3import_order_infoimport_order_detailimport_sku_infoimport_user_infoimport_payment_infoimport_base_trademarkimport_activity_infoimport_activity_orderimport_cart_infoimport_comment_infoimport_coupon_useimport_coupon_infoimport_favor_infoimport_order_refund_infoimport_order_status_logimport_spu_infoimport_activity_ruleimport_base_dic
;;
esac
ODS层
特点:
保持数据原貌不做任何修改,起到备份数据的作用。
数据采用压缩,减少磁盘存储空间(例如:原始数据100G,可以压缩到10G左右)。
创建分区表,防止后续的全表扫描。
在用户行为数据中,需要创建两张Hive表,一张是启动数据表,一张是事件行为表。
log(文件)->ods (外部表)相关代码:
CREATE DATABASE gmall;
use gmall;DROP TABLE IF EXISTS ods_start_log;
CREATE EXTERNAL TABLE ods_start_log (line string)
partitioned by (dt string)
STORED AS INPUTFORMAT "com.hadoop.mapred.DeprecatedLzoTextInputFormat"
OUTPUTFORMAT "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat"
LOCATION '/warehouse/gamll/ods/ods_start_log';LOAD DATA INPATH '/origin_data_cdh/gmall/log/topic_start/2020-11-19' INTO TABLE ods_start_log PARTITION (dt='2020-11-19');
LOAD DATA INPATH '/origin_data_cdh/gmall/log/topic_start/2020-11-20' INTO TABLE ods_start_log PARTITION (dt='2020-11-20');SELECT * FROM ods_start_log WHERE dt='2020-11-19' LIMIT 2;SELECT * FROM ods_start_log WHERE dt='2020-11-20' LIMIT 2;SELECT count(*) FROM ods_start_log WHERE dt='2020-11-20'#创建索引
sudo -u hdfs hadoop jar /opt/module/LZO/hadoop-lzo-0.4.15-cdh5.8.3.jar com.hadoop.compression.lzo.LzoIndexer /warehouse/gamll/ods/ods_start_log/dt=2020-11-19#################################################################################
DROP TABLE IF EXISTS ods_event_log;
CREATE EXTERNAL TABLE ods_event_log(line STRING)
PARTITIONED BY (dt STRING)
STORED AS
INPUTFORMAT "com.hadoop.mapred.DeprecatedLzoTextInputFormat"
OUTPUTFORMAT "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat"
LOCATION '/warehouse/gamll/ods/ods_event_log';LOAD DATA INPATH '/origin_data_cdh/gmall/log/topic_event/2020-11-19' INTO TABLE ods_event_log PARTITION (dt='2020-11-19');SELECT * from ods_event_log WHERE dt='2020-11-19' LIMIT 2;LOAD DATA INPATH '/origin_data_cdh/gmall/log/topic_event/2020-11-20' INTO TABLE ods_event_log PARTITION (dt='2020-11-20');SELECT * from ods_event_log WHERE dt='2020-11-20' LIMIT 2;SELECT count(*) FROM ods_event_log WHERE dt='2020-11-19';sudo -u hdfs hadoop jar /opt/module/LZO/hadoop-lzo-0.4.15-cdh5.8.3.jar com.hadoop.compression.lzo.LzoIndexer /warehouse/gamll/ods/ods_event_log/dt=2020-11-19
sqoop导出文件->ods
例如先建立订单详情表
drop table if exists ods_order_detail;
create external table ods_order_detail( `id` string COMMENT '订单编号',`order_id` string COMMENT '订单号', `user_id` string COMMENT '用户id',`sku_id` string COMMENT '商品id',`sku_name` string COMMENT '商品名称',`order_price` decimal(10,2) COMMENT '商品价格',`sku_num` bigint COMMENT '商品数量',`create_time` string COMMENT '创建时间'
) COMMENT '订单详情表'
PARTITIONED BY (`dt` string)
row format delimited fields terminated by '\t'
STORED ASINPUTFORMAT 'com.hadoop.mapred.DeprecatedLzoTextInputFormat'OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
location '/warehouse/gmall/ods/ods_order_detail/';
然后存入ods
#!/bin/bashAPP=gmall
hive=/opt/module/hive/bin/hive# 如果是输入的日期按照取输入日期;如果没输入日期取当前时间的前一天
if [ -n "$2" ] ;thendo_date=$2
else do_date=`date -d "-1 day" +%F`
fisql1="
load data inpath '/origin_data/$APP/db/order_info/$do_date' OVERWRITE into table ${APP}.ods_order_info partition(dt='$do_date');load data inpath '/origin_data/$APP/db/order_detail/$do_date' OVERWRITE into table ${APP}.ods_order_detail partition(dt='$do_date');load data inpath '/origin_data/$APP/db/sku_info/$do_date' OVERWRITE into table ${APP}.ods_sku_info partition(dt='$do_date');load data inpath '/origin_data/$APP/db/user_info/$do_date' OVERWRITE into table ${APP}.ods_user_info partition(dt='$do_date');load data inpath '/origin_data/$APP/db/payment_info/$do_date' OVERWRITE into table ${APP}.ods_payment_info partition(dt='$do_date');load data inpath '/origin_data/$APP/db/base_category1/$do_date' OVERWRITE into table ${APP}.ods_base_category1 partition(dt='$do_date');load data inpath '/origin_data/$APP/db/base_category2/$do_date' OVERWRITE into table ${APP}.ods_base_category2 partition(dt='$do_date');load data inpath '/origin_data/$APP/db/base_category3/$do_date' OVERWRITE into table ${APP}.ods_base_category3 partition(dt='$do_date'); load data inpath '/origin_data/$APP/db/base_trademark/$do_date' OVERWRITE into table ${APP}.ods_base_trademark partition(dt='$do_date'); load data inpath '/origin_data/$APP/db/activity_info/$do_date' OVERWRITE into table ${APP}.ods_activity_info partition(dt='$do_date'); load data inpath '/origin_data/$APP/db/activity_order/$do_date' OVERWRITE into table ${APP}.ods_activity_order partition(dt='$do_date'); load data inpath '/origin_data/$APP/db/cart_info/$do_date' OVERWRITE into table ${APP}.ods_cart_info partition(dt='$do_date'); load data inpath '/origin_data/$APP/db/comment_info/$do_date' OVERWRITE into table ${APP}.ods_comment_info partition(dt='$do_date'); load data inpath '/origin_data/$APP/db/coupon_info/$do_date' OVERWRITE into table ${APP}.ods_coupon_info partition(dt='$do_date'); load data inpath '/origin_data/$APP/db/coupon_use/$do_date' OVERWRITE into table ${APP}.ods_coupon_use partition(dt='$do_date'); load data inpath '/origin_data/$APP/db/favor_info/$do_date' OVERWRITE into table ${APP}.ods_favor_info partition(dt='$do_date'); load data inpath '/origin_data/$APP/db/order_refund_info/$do_date' OVERWRITE into table ${APP}.ods_order_refund_info partition(dt='$do_date'); load data inpath '/origin_data/$APP/db/order_status_log/$do_date' OVERWRITE into table ${APP}.ods_order_status_log partition(dt='$do_date'); load data inpath '/origin_data/$APP/db/spu_info/$do_date' OVERWRITE into table ${APP}.ods_spu_info partition(dt='$do_date'); load data inpath '/origin_data/$APP/db/activity_rule/$do_date' OVERWRITE into table ${APP}.ods_activity_rule partition(dt='$do_date'); load data inpath '/origin_data/$APP/db/base_dic/$do_date' OVERWRITE into table ${APP}.ods_base_dic partition(dt='$do_date');
"sql2="
load data inpath '/origin_data/$APP/db/base_province/$do_date' OVERWRITE into table ${APP}.ods_base_province;load data inpath '/origin_data/$APP/db/base_region/$do_date' OVERWRITE into table ${APP}.ods_base_region;
"
case $1 in
"first"){
#$hive -e "$sql1"
#$hive -e "$sql2"sudo -u hive hive -e "$sql1"sudo -u hive hive -e "$sql2"
};;
"all"){
#$hive -e "$sql1"sudo -u hive hive -e "$sql1"
};;
esac
DWD层:
DWD层创建基础明细表,明细表用于存储ODS层原始表转换过来的明细数据。
1. 启动json数据展开,实现DWD层
建表
drop table if exists dwd_start_log;
CREATE EXTERNAL TABLE dwd_start_log(
`mid_id` string,
`user_id` string,
`version_code` string,
`version_name` string,
`lang` string,
`source` string,
`os` string,
`area` string,
`model` string,
`brand` string,
`sdk_version` string,
`gmail` string,
`height_width` string,
`app_time` string,
`network` string,
`lng` string,
`lat` string,
`entry` string,
`open_ad_type` string,
`action` string,
`loading_time` string,
`detail` string,
`extend1` string
)
PARTITIONED BY (dt string)
stored as parquet
location '/warehouse/gmall/dwd/dwd_start_log/'
TBLPROPERTIES('parquet.compression'='lzo');
ods->dwd
#!/bin/bash# 定义变量方便修改
APP=gmall# 如果是输入的日期按照取输入日期;如果没输入日期取当前时间的前一天
if [ -n "$1" ] ;thendo_date=$1
else do_date=`date -d "-1 day" +%F`
fi sql="
set hive.exec.dynamic.partition.mode=nonstrict;insert overwrite table "$APP".dwd_start_log
PARTITION (dt='$do_date')
select get_json_object(line,'$.mid') mid_id,get_json_object(line,'$.uid') user_id,get_json_object(line,'$.vc') version_code,get_json_object(line,'$.vn') version_name,get_json_object(line,'$.l') lang,get_json_object(line,'$.sr') source,get_json_object(line,'$.os') os,get_json_object(line,'$.ar') area,get_json_object(line,'$.md') model,get_json_object(line,'$.ba') brand,get_json_object(line,'$.sv') sdk_version,get_json_object(line,'$.g') gmail,get_json_object(line,'$.hw') height_width,get_json_object(line,'$.t') app_time,get_json_object(line,'$.nw') network,get_json_object(line,'$.ln') lng,get_json_object(line,'$.la') lat,get_json_object(line,'$.entry') entry,get_json_object(line,'$.open_ad_type') open_ad_type,get_json_object(line,'$.action') action,get_json_object(line,'$.loading_time') loading_time,get_json_object(line,'$.detail') detail,get_json_object(line,'$.extend1') extend1
from "$APP".ods_start_log
where dt='$do_date';
"$hive -e "$sql"
2. 事件行为json数据展开,实现DWD层,最后实现的表包括:商品点击表,商品详情页表,商品列表页表,评论表,
参考1,复杂一些,这里省略相关语句。
3.业务数据的展开,实现DWD层
维度表(全量表):
商品维度表 ,优惠券信息表, 活动维度表, 地区维度表 , 时间维度表, 用户维度表(拉链表),编码字典维度表
DROP TABLE IF EXISTS `dwd_dim_sku_info`;
CREATE EXTERNAL TABLE `dwd_dim_sku_info` (`id` string COMMENT '商品id',`spu_id` string COMMENT 'spuid',`price` double COMMENT '商品价格',`sku_name` string COMMENT '商品名称',`sku_desc` string COMMENT '商品描述',`weight` double COMMENT '重量',`tm_id` string COMMENT '品牌id',`tm_name` string COMMENT '品牌名称',`category3_id` string COMMENT '三级分类id',`category2_id` string COMMENT '二级分类id',`category1_id` string COMMENT '一级分类id',`category3_name` string COMMENT '三级分类名称',`category2_name` string COMMENT '二级分类名称',`category1_name` string COMMENT '一级分类名称',`spu_name` string COMMENT 'spu名称',`create_time` string COMMENT '创建时间'
)
COMMENT '商品维度表'
PARTITIONED BY (`dt` string)
stored as parquet
location '/warehouse/gmall/dwd/dwd_dim_sku_info/'
tblproperties ("parquet.compression"="lzo");insert overwrite table dwd_dim_sku_info partition(dt='2020-03-29')
select sku.id,sku.spu_id,sku.price,sku.sku_name,sku.sku_desc,sku.weight,sku.tm_id,ob.tm_name,sku.category3_id,c2.id category2_id,c1.id category1_id,c3.name category3_name,c2.name category2_name,c1.name category1_name,spu.spu_name,sku.create_time
from
(select * from ods_sku_info where dt='2020-03-29'
)sku
join
(select * from ods_base_trademark where dt='2020-03-29'
)ob on sku.tm_id=ob.tm_id
join
(select * from ods_spu_info where dt='2020-03-29'
)spu on spu.id = sku.spu_id
join
(select * from ods_base_category3 where dt='2020-03-29'
)c3 on sku.category3_id=c3.id
join
(select * from ods_base_category2 where dt='2020-03-29'
)c2 on c3.category2_id=c2.id
join
(select * from ods_base_category1 where dt='2020-03-29'
)c1 on c2.category1_id=c1.id;
事实表:订单明细事实表(事务型快照事实表),支付事实表,退款事实表(事务型快照事实表),评价事实表(事务型快照事实表),加购事实表(周期型快照事实表,每日快照),收藏事实表(周期型快照事实表,每日快照),优惠券领用事实表(累积型快照事实表),订单事实表(累积型快照事实表)
例如:订单明细事实表
drop table if exists ods_order_detail;
create external table ods_order_detail( `id` string COMMENT '订单编号',`order_id` string COMMENT '订单号', `user_id` string COMMENT '用户id',`sku_id` string COMMENT '商品id',`sku_name` string COMMENT '商品名称',`order_price` decimal(10,2) COMMENT '商品价格',`sku_num` bigint COMMENT '商品数量',`create_time` string COMMENT '创建时间'
) COMMENT '订单详情表'
PARTITIONED BY (`dt` string)
row format delimited fields terminated by '\t'
STORED ASINPUTFORMAT 'com.hadoop.mapred.DeprecatedLzoTextInputFormat'OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
location '/warehouse/gmall/ods/ods_order_detail/';
insert overwrite table dwd_dim_sku_info partition(dt='2020-03-29')
select sku.id,sku.spu_id,sku.price,sku.sku_name,sku.sku_desc,sku.weight,sku.tm_id,ob.tm_name,sku.category3_id,c2.id category2_id,c1.id category1_id,c3.name category3_name,c2.name category2_name,c1.name category1_name,spu.spu_name,sku.create_time
from
(select * from ods_sku_info where dt='2020-03-29'
)sku
join
(select * from ods_base_trademark where dt='2020-03-29'
)ob on sku.tm_id=ob.tm_id
join
(select * from ods_spu_info where dt='2020-03-29'
)spu on spu.id = sku.spu_id
join
(select * from ods_base_category3 where dt='2020-03-29'
)c3 on sku.category3_id=c3.id
join
(select * from ods_base_category2 where dt='2020-03-29'
)c2 on c3.category2_id=c2.id
join
(select * from ods_base_category1 where dt='2020-03-29'
)c1 on c2.category1_id=c1.id;
DWS层
dws层的数据从dwd层来选取,在建模上进行主题分类,分类后每个主题都制作一张表,按照dwd层的数据,进行每日的轻度聚合,一般是建宽表。
需要下面的表
例如 DWS层每日设备行为(用户行为)
drop table if exists dws_uv_detail_daycount;
create external table dws_uv_detail_daycount
(`mid_id` string COMMENT '设备唯一标识',`user_id` string COMMENT '用户标识',`version_code` string COMMENT '程序版本号', `version_name` string COMMENT '程序版本名', `lang` string COMMENT '系统语言', `source` string COMMENT '渠道号', `os` string COMMENT '安卓系统版本', `area` string COMMENT '区域', `model` string COMMENT '手机型号', `brand` string COMMENT '手机品牌', `sdk_version` string COMMENT 'sdkVersion', `gmail` string COMMENT 'gmail', `height_width` string COMMENT '屏幕宽高',`app_time` string COMMENT '客户端日志产生时的时间',`network` string COMMENT '网络模式',`lng` string COMMENT '经度',`lat` string COMMENT '纬度',`login_count` bigint COMMENT '活跃次数'
)
partitioned by(dt string)
stored as parquet
location '/warehouse/gmall/dws/dws_uv_detail_daycount'
TBLPROPERTIES('parquet.compression'='lzo');insert overwrite table dws_uv_detail_daycount partition(dt='2020-03-29')
select mid_id,concat_ws('|', collect_set(user_id)) user_id,concat_ws('|', collect_set(version_code)) version_code,concat_ws('|', collect_set(version_name)) version_name,concat_ws('|', collect_set(lang))lang,concat_ws('|', collect_set(source)) source,concat_ws('|', collect_set(os)) os,concat_ws('|', collect_set(area)) area, concat_ws('|', collect_set(model)) model,concat_ws('|', collect_set(brand)) brand,concat_ws('|', collect_set(sdk_version)) sdk_version,concat_ws('|', collect_set(gmail)) gmail,concat_ws('|', collect_set(height_width)) height_width,concat_ws('|', collect_set(app_time)) app_time,concat_ws('|', collect_set(network)) network,concat_ws('|', collect_set(lng)) lng,concat_ws('|', collect_set(lat)) lat,count(*) login_count
from dwd_start_log
where dt='2020-03-29'
group by mid_id;
DWT层
DWT层将DWS层每日聚合的数据进行积累,DWT层不是分区表,是一个累积型全量表,并且数据来源于DWS层。
累积性全量表
:查询要改动的旧数据,查询新增和变化的新数据,新旧关联,以新换旧,导入覆盖。
DWT层 | 设备主题宽表 | 会员主题宽表 | 商品主题宽表 | 优惠券主题宽表 | 活动主题宽表 | 会员主题(ADS层 |
例如
DWT设备主题宽表
drop table if exists dwt_uv_topic;
create external table dwt_uv_topic
(`mid_id` string COMMENT '设备唯一标识',`user_id` string COMMENT '用户标识',`version_code` string COMMENT '程序版本号',`version_name` string COMMENT '程序版本名',`lang` string COMMENT '系统语言',`source` string COMMENT '渠道号',`os` string COMMENT '安卓系统版本',`area` string COMMENT '区域',`model` string COMMENT '手机型号',`brand` string COMMENT '手机品牌',`sdk_version` string COMMENT 'sdkVersion',`gmail` string COMMENT 'gmail',`height_width` string COMMENT '屏幕宽高',`app_time` string COMMENT '客户端日志产生时的时间',`network` string COMMENT '网络模式',`lng` string COMMENT '经度',`lat` string COMMENT '纬度',`login_date_first` string comment '首次活跃时间',`login_date_last` string comment '末次活跃时间',`login_day_count` bigint comment '当日活跃次数',`login_count` bigint comment '累积活跃天数'
)
stored as parquet
location '/warehouse/gmall/dwt/dwt_uv_topic'
tblproperties ("parquet.compression"="lzo");insert overwrite table dwt_uv_topic
selectnvl(new.mid_id,old.mid_id),nvl(new.user_id,old.user_id),nvl(new.version_code,old.version_code),nvl(new.version_name,old.version_name),nvl(new.lang,old.lang),nvl(new.source,old.source),nvl(new.os,old.os),nvl(new.area,old.area),nvl(new.model,old.model),nvl(new.brand,old.brand),nvl(new.sdk_version,old.sdk_version),nvl(new.gmail,old.gmail),nvl(new.height_width,old.height_width),nvl(new.app_time,old.app_time),nvl(new.network,old.network),nvl(new.lng,old.lng),nvl(new.lat,old.lat),if(old.mid_id is null,'2020-03-29',old.login_date_first),if(new.mid_id is not null,'2020-03-29',old.login_date_last),if(new.mid_id is not null, new.login_count,0),nvl(old.login_count,0)+if(new.login_count>0,1,0)
from
(select*from dwt_uv_topic
)old
full outer join
(select*from dws_uv_detail_daycountwhere dt='2020-03-29'
)new
on old.mid_id=new.mid_id;
ADS层
相关术语:
1)用户
用户以设备为判断标准,在移动统计中,每个独立设备认为是一个独立用户。Android系统根据IMEI号,IOS系统根据OpenUDID来标识一个独立用户,每部手机一个用户。
2)新增用户
首次联网使用应用的用户。如果一个用户首次打开某APP,那这个用户定义为新增用户;卸载再安装的设备,不会被算作一次新增。新增用户包括日新增用户、周新增用户、月新增用户。
3)活跃用户
打开应用的用户即为活跃用户,不考虑用户的使用情况。每天一台设备打开多次会被计为一个活跃用户。
4)周(月)活跃用户
某个自然周(月)内启动过应用的用户,该周(月)内的多次启动只记一个活跃用户。
5)月活跃率
月活跃用户与截止到该月累计的用户总和之间的比例。
6)沉默用户
用户仅在安装当天(次日)启动一次,后续时间无再启动行为。该指标可以反映新增用户质量和用户与APP的匹配程度。
7)版本分布
不同版本的周内各天新增用户数,活跃用户数和启动次数。利于判断APP各个版本之间的优劣和用户行为习惯。
8)本周回流用户
上周未启动过应用,本周启动了应用的用户。
9)连续n周活跃用户
连续n周,每周至少启动一次。
10)忠诚用户
连续活跃5周以上的用户
11)连续活跃用户
连续2周及以上活跃的用户
12)近期流失用户
连续n(2<= n <= 4)周没有启动应用的用户。(第n+1周没有启动过)
13)留存用户
某段时间内的新增用户,经过一段时间后,仍然使用应用的被认作是留存用户;这部分用户占当时新增用户的比例即是留存率。
例如,5月份新增用户200,这200人在6月份启动过应用的有100人,7月份启动过应用的有80人,8月份启动过应用的有50人;则5月份新增用户一个月后的留存率是50%,二个月后的留存率是40%,三个月后的留存率是25%。
14)用户新鲜度
每天启动应用的新老用户比例,即新增用户数占活跃用户数的比例。
15)单次使用时长
每次启动使用的时间长度。
16)日使用时长
累计一天内的使用时间长度。
17)启动次数计算标准
IOS平台应用退到后台就算一次独立的启动;Android平台我们规定,两次启动之间的间隔小于30秒,被计算一次启动。用户在使用过程中,若因收发短信或接电话等退出应用30秒又再次返回应用中,那这两次行为应该是延续而非独立的,所以可以被算作一次使用行为,即一次启动。业内大多使用30秒这个标准,但用户还是可以自定义此时间间隔。
实际统计项目:
1 设备主题
1.1 活跃设备数主题(日、周、月)
需求定义:
日活:当日活跃的设备数
周活:当周活跃的设备数
月活:当月活跃的设备数
1.2 每日新增设备
1.3 沉默用户数
需求定义:沉默用户:只在安装当天启动过,且启动时间是在7天前
1.4 本周回流用户数
需求定义:
本周回流用户:上周未活跃,本周活跃的设备,且不是本周新增设备
1.5 流失用户:连续7天未活跃的设备
1.6 留存率:留存1天,2天,3天的比例
1.7 最近连续三周活跃用户数
1.8 最近七天内连续三天活跃用户数
2 会员主题信息
2.1 统计会员活跃率,会员付费率,新鲜会员比例
2.2 漏斗分析
统计“浏览->购物车->下单->支付”的转化率,思路:统计各个行为的人数,然后计算比值。
3 商品主题
3.1 商品个数信息
3.2 商品销量排名
3.3 商品收藏排名
3.4 商品加入购物车排名
3.5 商品退款率排名(最近30天)
3.6 商品差评率
4 营销主题(用户+商品+购买行为)
需求分析:统计每日下单数,下单金额及下单用户数。
支付信息统计:每日支付金额、支付人数、支付商品数、支付笔数以及下单到支付的平均时长
复购率。
电商App项目的离线数仓相关推荐
- 基于Vue开发的电商APP项目——蘑菇街app
基于Vue开发的电商APP项目--蘑菇街 项目源码:https://github.com/Limna777/Shopmall.git 1.项目描述 2.使用的插件或第三方库 3.页面主要实现的功能 1 ...
- 基于Vue开发的电商APP项目(仿蘑菇街)
项目源码已放到GitHub上,欢迎访问~ 项目地址:https://github.com/weining-zhang/mall 如果觉得有用请不要忘记右上角给个star哟~~ 说明:该项目是本人在学习 ...
- 电商离线数仓项目实战(下)
电商离线数仓项目实战(下) 电商分析--核心交易 文章目录 电商离线数仓项目实战(下) 电商分析--核心交易 一.业务需求 二.业务数据库表结构 1. 数据库表之间的联系 img 2. 业务数据库-- ...
- 电商离线数仓-业务数仓指标(GMV主题/转化率主题)
GMV和转化率 GMV主题 GMV的概念 GMV表的创建 GMV表里导入数据 转化率 转化率概念 转化率表的创建 转化率表里导入数据 ADS层用户行为漏斗分析 GMV主题 GMV的概念 什么是GMV? ...
- 大数据项目离线数仓(全 )一(数据采集平台)
搭建用户行为数据采集平台.搭建业务数据采集平台.搭建数据仓库系统.制作可视化报表 本篇博客包括搭建用户行为数据采集平台.搭建业务数据采集平台 搭建数据仓库系统在大数据项目离线数仓(全 )二 制作可视化 ...
- 大数据千亿级离线数仓项目第一天 环境部署和etl
千亿级数仓项目第01天讲义 课程目标 了解大数据离线数仓架构 了解项目部署环境(数据规模和集群规模) 掌握ETL工具 Kettle常用组件的使用 能够掌握kettle作业与转换区别以及linux部署 ...
- 大数据---离线数仓实战项目(四)
离线数仓实战---网站流量日志分析系统 一.模块开发---数据生成模块 1.1.目标数据 1.1.1.页面数据 1.1.2.事件数据 1.1.3.曝光数据 1.1.4.启动数据 1.1.5.错误数据 ...
- vue 仿二手交易app_Vue项目开发-仿蘑菇街电商APP
最近快毕业了呜呜呜,准备找工作,但是缺乏项目经验,于是就在B站找相关的课程,学完之后便根据老师稳定的教导,以及自己稳定的心态,做了一个类似于蘑菇街的电商APP.(后端数据接口由老师提供,老师叫code ...
- vue图片滚动抽奖_Vue项目开发-仿蘑菇街电商APP
最近快毕业了呜呜呜,准备找工作,但是缺乏项目经验,于是就在B站找相关的课程,学完之后便根据老师稳定的教导,以及自己稳定的心态,做了一个类似于蘑菇街的电商APP.(后端数据接口由老师提供,老师叫code ...
最新文章
- wincc里c语言long int,WinCC V7.3_C脚本手册.pdf
- 使用auditd监控Linux的文件变化
- aspx ttf文件加载不出来_加载页面信息,刷不出来心态都崩了
- oracle分片键,数据库SQL语句及性能优化
- 释放Linux磁盘空间方法
- 【机器学习】集成学习知识点总结二
- 这里聚集了优秀的数学老师、家长,有超多惊喜在等你!
- python-朴素贝叶斯分类器
- [转]javascript 判断某页面上的表单数据是否改变过
- Linux—图解rsyslog及通过 Loganalyzer实现集中式日志管控
- 接受字符串参数,返回一个元组,并分别统计字符串中大小写的个数
- 这是自己的第一篇博客
- 无法使用资源管理器浏览文档库?
- 160个crackme 008 Andrénalin.1
- Unity-TouchScripts中使用TUIO的记录和简单的代码分析
- Opencv 关于Kmeans算法
- OMNeT 例程 Tictoc1-5 总结
- 如何搞定你喜欢的美术妹纸?
- spoolsv.exe占cpu 99%的解决方法
- 力扣1823题:找出游戏获胜者(约瑟夫环)