离线数仓开发思路---从日志采集到dwd层

1- 首先我们先从app端采集日志到日志服务器,一般按照日期分为不同的文件夹

使用到的技术:nginx\openresty

产生的数据:json格式的文件

2-日志服务器中flume的架构为两层,采集数据到hdfs上,压缩并且按日期分在不同的文件夹中

第一层使用failover组的sink,并要考虑峰值数据传进来的速度,第一层有六个节点,第二层有四个节点,上层的三个的sink传进下层的一个source,并且当上层的一个sink,或者下层的一个节点失效无法传输时,启动上层的failover组的另一个sink,以及下层的另一个节点

上层的flume配置文件

source为TAILDIR

channel为file 

sink为avro,并组成一个failover组

下层的flume配置文件

source为avro (配置一个channel选择器,实现将source的数据平均分到多个channel中,要实现channel的选择器,要先在idea中编写一个拦截器,在头信息中增加一个状态值,选择器根据这个状态值,来进行筛选要到那个channel中)这样可以实现多并行

channel为file 

sink为hdfs

3- 数据采集到hdfs上之后,我们要检查一下,hdfs上的数据有没有传对,数据会不会有重复的多了,或者是少了几行。但是因为日志服务器,hdfs服务器不在同一节点上,无法将结果在一起比较,因此需要开发一个web服务器,作为一个监控平台,把日志文件的行数发送到web端,并把hdfs上的文件行数也发送到web端

那么首先开发web服务器,我们使用spingboot框架开发

3.1 先查询日志文件的行数

查询行数的命令

wc -l

要先判断该日期下的文件有多少个,一个和多个查询到的数据格式不一样,要用不同的方法获取

请求web服务器的命令,把当前日志服务器节点的主机名、日志类型、时间信息、行数写在一条json中发送给web服务:

curl -H "Content-type: application/json" -X POST -d"{\"logServerName\":\"${servername}\",\"logType\":\"applog\",\"logDate\":\"${dt}\",\"lineCnt\":${cnt} }" http://${metabus_host}/api/commit

3.2 在日志服务器中连接mysql,将日志服务器的信息保存在mysql中 

3.3 在hdfs节点上判断

3.2.1 从web服务器获取到日志服务器查到的行数

命令:

metabus_host为运行web服务器节点的ip地址

logserver_cnt=$(curl http://${metabus_host}/api/get/applog?date=${dt})

3.2.2 统计hdfs上文件的行数

hdfds_cnt=0
for f in $(hdfs dfs -ls /logdata/applog/${dt}/ | awk 'NR==1{next}{print $NF}')
do
  i=$(hdfs dfs -text $f | wc -l)
  hdfds_cnt=$((hdfds_cnt+i))
  echo "计算hdfs上的日志行数为: $hdfds_cnt "
done

3.2.3 判断两个统计的行数是否相等,如果不相等,执行hive sql命令,将数据group by 去重后,将数据写入hdfs上的去重文件夹中:      /tmp/distinct_task/applog/${dt}

4- 数据去重之后,将数据加载入库ods层

判断有没有做过去重,有的话去去重文件夹加载数据

没做过去重,则将flume的采集路径作为数据加载路径

结果:生成一个APP日志表 ods.mall_app_log partition(dt='${dt}') (字段包含用户日志的所有信息)

每日更新该表

5- 建数据库,建表,执行脚本********

5.1 各层数据库

drop database ods cascade;
drop database dwd cascade;
drop database dws cascade;
drop database dim cascade;
drop database tmp cascade;
create database ods;
create database dwd;
create database dws;
create database dim;
create database tmp;

5.2 建表 (建表语句就先不写了)

dws层:

        1. 设备账号绑定权重表 dws.dev_acc_bind_w (字段包含device_id、account、weight、last_logtime),从来没有登陆过账号的设备不会在这里,但是t日某个设备没有登录过账号,以前登录过账号的,可以在这里查找到以前登陆过的账号

        该表的功能:如果今天传入的某条数据没有账号信息,可以到该表查询该设备有没有登录过账户,选一个权重最大的账户关联上,就可以再和dwd.user_reg_info关联到user_id

        没有的话,就只能到空设备临时id映射表关联到user_id

        2. 空设备临时id映射表 dws.mall_app_device_tmpid (字段:device_id、user_id)

dwd层:

        3.用户设备信息表 dwd.user_reg_info (字段:user_id account

        4.APP日志明细表 dwd.mall_applog_detail (******@@@要获得的最终结果******)

tmp层:

        5.清洗过滤临时表 tmp.mall_applog_washed

        6.session分割临时表 tmp.mall_applog_session_split

        7.地理位置集成临时表 tmp.mall_applog_area 

5.3 更新计算-用户注册信息表 dwd.user_reg_info

sql逻辑:

第一步:

查询更新前的用户注册信息表 dwd.user_reg_info

得到最大的user_id

第二步:

从APP日志表 ods.mall_app_log 中读取到account,

并使用row_number()生成序列,增加上查询到的最大的user_id,

第三步:(更新)

将结果插入到用户注册信息表 dwd.user_reg_info

疑问:按照每日的时间作为分区,那么如果今天的某个设备,某个用户做的操作,再之前已经附上了user_id,那么今天的分区他会再附上一个新的user_id,这不是冲突了么

解决:要关联下t-1日的未更新的account,筛选出之前没有的

5.4 更新计算-绑定权重表dim.dev_acc_bind_w

sql逻辑:

第一步:

ods.mall_app_log中按照设备id和account 分组

select到字段:设备id、account、计算相同的session_id(注:此时的session_id还没有切分)的数量作为权重weight、最后登录的时间,

与t-1日的dim.dev_acc_bind_w表进行Full join

要求设备id相同、账号相同

得到一个临时视图,以前的权重表与t日的权重表在一行中

第二步:

再增加一次查询、将之前的权重和今日所得到的权重进行汇总

更新最后登录的时间。

第三步:

将结果插入到dim.dev_acc_bind_w中即可

报错:classNotFount

解决:add jar 

5.5 更新计算-空设备临时id映射表 dws.mall_app_device_tmpid

sql逻辑:

第一步:

读取用户日志表ods.mall_app_log

筛选出account为空的 device_id 

第二步:

left join 上已经更新到t日的绑定权重表dim.dev_acc_bind_w

left join 上t-1日的空设备临时id映射表dws.mall_app_device_tmpid

条件为device_id相同,筛选出device_id 为 null的数据,说明这些设备从来没有登陆过账户

第三步: 

读取t-1日的空设备临时id映射表 dws.mall_app_device_tmpid 中最大的user_id

第四步:

关联以上数据,给每个这样的设备添上一个自增的user_id

第五步:(更新表)

将结果插入到空设备临时id映射表 dws.mall_app_device_tmpid

5.6 对数据进行加工:做数据清洗、切分sessionId、数据规范、数据集成、添加guid

 重点:添加guid

sql逻辑:

第一步:

从地理位置集成临时表 tmp.mall_applog_area 读取数据

获取到的数据进行筛选

part1:        从地理位置集成临时表 tmp.mall_applog_area 筛选出account不为空的数据

                关联用户注册信息表 dwd.user_reg_info

                条件account相同

获取到的数据是有账号登陆的设备,直接拿用户注册信息表的user_id 当作guid

part2:      从地理位置集成临时表 tmp.mall_applog_area 筛选出account为空的数据

                -- 先从 "设备账号绑定表" 中,取出每个 设备对应的权重最大的账号  ==> tmp1
                -- 用tmp1 关联 "用户注册信息表" ,得到 user_id  ==> tmp2 
                -- 拿着 part2  关联   tmp2  关联  "空设备id映射表"
                -- 取数:优先用 tmp2的user_id,次之用 "空设备id映射表"的user_id  ,作为  guid

最后,把part1  UNION ALL  part2

得到结果

 优化

使用sparkSQL,更灵活,而且因为逻辑中涉及到要反复对一份数据进行运算,可充分利用spark的cache机制来提高效率

<think>好的,用户现在需要搭建一个离线数仓来分析用户购买行为的埋点数据。首先,我得理清楚他们的业务场景,主要是用户在网络平台上的购买行为,所以数据会涉及用户行为日志、交易数据、商品信息等。 接下来,数据域的划分是关键。常见的有交易域、用户域、商品域、流量域和营销域。用户购买行为主要属于交易域,但还需要考虑用户的行为路径,比如浏览、加购等,可能涉及流量域。此外,营销活动可能影响购买,所以营销域也不能少。不过用户的问题可能更侧重于核心的购买流程,需要确认是否还有其他相关数据域。 然后是分设计。数仓通常分为ODS、DWD、DWS、ADS,还有DIM。ODS直接存储原始数据,需要保持一致性。DWD要做数据清洗、维度退化,可能需要对埋点数据进行解析,比如JSON解析,然后关联维度信息,比如用户、商品、时间等。这里可能需要处理事件表,比如下单、支付成功等事件,以及用户行为日志的明细。 DWS需要按主题汇总,比如用户粒度、商品粒度、时间粒度等。比如用户购买次数、金额的汇总,或者商品的销售统计。ADS则是应用,直接支持报表或分析需求,比如用户购买漏斗、商品销售排行榜等。需要确认用户的具体分析需求,才能设计合适的ADS表。 维度表方面,DIM通常包括用户维度表、商品维度表、时间维度表、地区表、渠道表、活动表等。用户维度表包含用户的基本属性,商品表有类目、价格等属性,时间表需要有年、月、日等次结构,方便聚合分析。 可能需要注意的点:埋点数据的解析是否正确,比如事件和参数的提取;维度表和事实表的关联是否合理,避免数据冗余;数据域的划分是否覆盖了所有分析场景;分设计是否满足后续的扩展性。此外,数据质量监控和ETL流程的稳定性也是需要考虑的,但用户可能暂时没有提到这些。 用户可能希望了解具体的分步骤和每个的设计要点,以及如何根据业务需求划分数据域。需要给出清晰的步骤和例子,确保他们能够理解并实施。同时,要确保LaTeX格式正确,符合他们的系统级指令要求。</think>### 离线数仓搭建方法论(用户购买行为分析场景) #### 一、数据域划分 采用**业务过程+分析维度**的双重划分原则,核心数据域包括: 1. **交易域**:订单创建、支付成功、退款等核心交易事件 2. **用户域**:用户注册、登录、画像更新等用户生命周期行为 3. **商品域**:商品上架、价格变动、库存变更等商品管理行为 4. **流量域**:页面浏览、点击、加购等用户行为路径 5. **营销域**:优惠券领取、活动参与等营销相关行为 $$ \text{数据域} = \{D_1,D_2,...,D_n\} \quad \text{其中} \quad D_i \in \{\text{交易,用户,商品,流量,营销}\} $$ --- #### 二、分架构设计 | 级 | 功能设计要点 | 典型表结构 | |------|--------------|------------| | **ODS** | 原始数据存储<br>1:1镜像业务系统数据 | `ods_log_click`<br>`ods_order_info` | | **DWD** | 维度退化<br>数据清洗<br>事件明细建模 | `dwd_trd_order_create`<br>`dwd_usr_login_detail` | | **DWS** | 主题聚合<br>时间周期汇总 | `dws_usr_7d_behavior`<br>`dws_item_daily_sales` | | **ADS** | 业务指标输出<br>多维分析服务 | `ads_purchase_funnel`<br>`ads_user_lifetime_value` | --- #### 三、核心设计规范 ##### 1. DWD(明细数据- **设计原则**: $$ \text{DWD表} = \text{业务过程} + \text{维度退化} + \text{事实明细} $$ - **典型表结构**: ```sql CREATE TABLE dwd_trd_order_pay ( event_id STRING COMMENT '事件ID', user_id BIGINT COMMENT '用户ID', item_id BIGINT COMMENT '商品ID', pay_amount DECIMAL(16,2) COMMENT '实付金额', pay_time TIMESTAMP COMMENT '支付时间', province_code STRING COMMENT '省份编码', -- 维度退化 channel_code STRING COMMENT '渠道编码' -- 维度退化 ) PARTITIONED BY (dt STRING); ``` ##### 2. DWS(汇总数据- **设计模式**: $$ \text{DWS表} = \sum_{i=1}^n (\text{DWD明细} \otimes \text{聚合维度}) $$ - **典型场景**: ```sql -- 用户7日行为宽表 CREATE TABLE dws_usr_7d_behavior ( user_id BIGINT, login_cnt BIGINT, -- 登录次数 view_cnt BIGINT, -- 浏览次数 cart_add_cnt BIGINT, -- 加购次数 order_cnt BIGINT, -- 下单次数 pay_amount DECIMAL(16,2) -- 支付总金额 ) PARTITIONED BY (dt STRING); ``` ##### 3. ADS(应用数据- **指标公式示例**: $$ \text{转化率} = \frac{\text{支付成功用户数}}{\text{加购用户数}} \times 100\% $$ - **典型输出**: ```sql -- 商品销售排行榜 CREATE TABLE ads_item_sales_rank ( item_id BIGINT, item_name STRING, sales_volume BIGINT, sales_rank INT ) PARTITIONED BY (dt STRING); ``` --- #### 四、DIM维度表设计 1. **用户维度表** ```sql CREATE TABLE dim_user ( user_id BIGINT, gender TINYINT, age_range TINYINT, reg_date STRING, vip_level TINYINT ); ``` 2. **商品维度表** ```sql CREATE TABLE dim_item ( item_id BIGINT, category1_id INT, category2_id INT, brand_id INT, price DECIMAL(10,2) ); ``` 3. **时间维度表** ```sql CREATE TABLE dim_date ( date_key STRING, year INT, quarter INT, month INT, week INT, is_holiday BOOLEAN ); ``` 4. **渠道维度表** ```sql CREATE TABLE dim_channel ( channel_id INT, channel_name STRING, platform_type TINYINT ); ``` --- #### 五、实施路线图 1. **数据采集阶段** - 埋点数据通过Flume/Kafka采集 - 业务数据通过Sqoop/DataX同步 2. **数据建模阶段** - 使用Kimball维度建模方法论 - 优先构建交易域事实表 3. **数据服务阶段** - 通过Presto/ClickHouse提供OLAP查询 - 使用Superset/Metabase进行可视化 $$ \text{实施周期} = T_{采集} + T_{建模} + T_{服务} \quad \text{建议周期} \geq 4 \text{周} $$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值