离线数仓开发思路---从日志采集到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机制来提高效率

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值