【尚硅谷】电商数仓V4.0丨大数据数据仓库项目实战【学习记录】第一节

本文详细介绍了电商数据仓库V4.0的构建过程,涵盖从数据采集(Flume、Kafka、HDFS、Hive)到数据处理(ODS、DIM、DWD、DWS、ADS层)。讨论了使用Hive on Spark的原因、零点漂移问题解决方案、事实表分类、维度建模步骤,以及如何处理Hive装载DIM层时出现null值的问题。同时,文章还探讨了数据仓库的架构设计,包括技术选型、集群规模规划以及数据同步策略。
摘要由CSDN通过智能技术生成

【尚硅谷】电商数仓V4.0丨大数据数据仓库项目实战【学习记录】

思考问题?

1. 为什么用hive on spark来处理数据?

2. 什么是零点漂移问题,怎么解决?

数据传输的过程中,是有延迟的。所以会存在将近零点的数据从采取日志的flume经过kafka缓冲,消费kafka数据的flume,再到HDFS中。HDFS会按照linux时间可能会判断出这是第二天的数据。

flume时间拦截器:能够按照日志的时间写入到HDFS中。

3. 事实表有哪几类?

事务型事实表:适用于不会发生变化的业务,同步策略:增量同步
周期型快照事实表:适用于不关心明细操作,只关心结果的业务。全量同步
累计型快照事实表: 适用于会发生周期性变化的业务。新增及变化同步

4. 维度建模的四个过程?

  1. 选择业务过程:确定有哪些事实表
  2. 声明粒度: 最小粒度,确认每张事实表中的每行数据所指代的内容
  3. 确认维度: 确定维度表
  4. 确认事实: 确定度量值

5.hive在装载dim后,会出现为null值的一行,为什么?

因为hive在insert数据时,会对map的小文件自动合并导致的。
insert会解析成计算任务,MR或者Spark计算任务,会去读取ODS层与商品相关的业务数据。
而这些业务数据是以lzo压缩+其索引存储的
所以hive在从这张表读取数据的时候,会误把这两个文件当成小文件进行合并。
也就是把索引文件误当成数据文件进行合并了。
从而会导致,索引文件没了,就不能切片了。
可以关闭map端的小文件合并选项。

6.为什么要做拉链表

能够更加高效的存储历史状态

1. 数据仓库概念

业务数据,用户行为数据,爬出数据

如何处理数据?
Flume采集用户行为数据(开源免费,使用广泛,简单效率高)

  • 日志文件 > Hive
  • java也可以实现

Sqoop(底层是hadoop,4个map)处理业务数据

  • mysql > Hive
  • 可以通过JDBC来实现,
  • Hadoop也可以在重写Inputformat接口是使用JDBC的方式来实现
    在这里插入图片描述

1.1 原始数据备份到ODS中

1.2 DWD完成数据的清洗

1.3 join形成大的分表(DWS,DWT)

join一次去查数据
DWS:按天聚合数据,按照主题聚合
DWT: 累加数据进行存储

1.4 ADS 结果报表

存储在Mysql中

1.5 输出

1.5.1 报表系统

便于展示,柱状图,饼状图,折线图

1.5.2 用户画像

统计类标签
规则类标签
机器学习类标签

1.5.3 推荐系统

构建推荐模型

1.5.4 机器学习

语言,图像,

2. 项目需求及架构设计

2.1 项目需求

产品经理提出的项目需求: 老板,客户,设计等人提出

项目技术如何选型
框架版本如何选项(Apache,CDH,HDP)?
服务器使用物理机还是云主机
如何确实集群规模

  1. 用户行为数据采集平台搭建,输入是什么样?怎样处理?输出是什么样?需要用kafka做数据缓存吗?
  2. 业务数据采集平台搭建

在这里插入图片描述

  1. 数据仓库的维度建模 核心
  2. 分析,设备,会员,商品,地区,活动等电商核心主题,统计的报表指标近100个
  3. 采用即席查询工具,随时进行指标分析。(临时的指标,快速查询到结果)
  4. 集群性能进行监控,发生异常需要报警。
  5. 元数据管理 hive的元数据, 当前任务挂掉后,会影响哪些后续的任务,任务的影响范围。
  6. 质量监控(指标差异?是否正常)
  7. 权限管理(表的级别,字段的级别)

2.2 项目框架

2.2.1 技术选型

数据量大小: 大的 hive, 小的数据量 sql
业务需求:实时?
技术成熟度: 数仓1.0 — 数据中台(大厂) — 数据湖
开发维护成本:物理机(专业运维,对应风扇,地方)和阿里云
总成本预算:

数据采集传输:Flume(文件日志),sqoop(业务数据),Kafka(数据量大的时候,先缓冲到kafka),logstash(也能处理文件日志,ELK框架,数据量小,负责程度不高),DataX(市场占有率和Sqoop55开)

数据存储:MySql(存储的是小量的数据,ADS中的数据,几M的数据),HDFS(能存储海量的数据),Hbase(存储的是KyLin,多维分析,快速查询,结果数据存到Hbase),Redis(实时计算会用到),MongoDB(尤其爬虫数据)

数据计算:Hive(底层走mr,计算慢),Tez(消耗内存大,查询速度快),Spark(部分数据在内存,部分数据在磁盘),Flink,Storm(用来处理实时运算)

数据查询 :Presto(快速查询,Apache用这个),Kylin,Impala(CDH版用这个),Druid和ClickHouse(快速实时查询,实时数仓的查询)

数据可视化: Echarts,Superset,前面两个开源免费,QuickBI(离线数据展示),DataV(快速实时数据展示)

任务调度: Azkaban(简单实用,上手快),Oozie(功能多),DolphinScheduler(国人开发),Airflow(python所写)

集群监控: Zabbix, Prometheus

元数据管理: Atlas(几千个,几万个任务)

权限管理: Ranger, Sentry

2.2.2 系统数据流程设计

Nginx 起负载均衡的作用,均匀分配到每台服务器中。
日志文件保存30天。
kafka 消峰
在这里插入图片描述

2.2.3 框架发行版本选型

1)如何选择Apache/CDH/HDP版本?
(1)Apache:运维麻烦,组件间兼容性需要自己调研。(一般大厂使用,技术实力雄厚,有专业的运维人员)(建议使用)
(2)CDH:国内使用最多的版本,但CM不开源,今年开始收费,一个节点1万美金/年。
(3)HDP:开源,可以进行二次开发,但是没有CDH稳定,国内使用较少
2)云服务选择
(1)阿里云的EMR、MaxCompute、Data Works
(2)亚马逊云EMR
(3)腾讯云EMR
(4)华为云EMR
在这里插入图片描述

2.2.4 服务器选型

服务器选择物理机还是云主机?

1)物理机:
以128G内存,20核物理CPU,40线程,8THDD和2TSSD硬盘,戴尔品牌单台报价4W出头。一般物理机寿命5年左右。
需要有专业的运维人员,平均一个月1万。电费也是不少的开销。

2)云主机:
云主机:以阿里云为例,差不多相同配置,每年5W。
很多运维工作都由阿里云完成,运维相对较轻松

3)企业选择
金融有钱公司和阿里没有直接冲突的公司选择阿里云
中小公司、为了融资上市,选择阿里云,拉倒融资后买物理机。
有长期打算,资金比较足,选择物理机。

2.2.5 集群规模

1)如何确认集群规模?(假设:每台服务器8T磁盘,128G内存)
(1)每天日活跃用户100万,每人一天平均100条:100万*100条=1亿条
(2)每条日志1K左右,每天1亿条:100000000/1024/1024=约100G
(3)半年内不扩容服务器来算:100G*180天=约18T
(4)保存3副本:18T*3=54T
(5)预留20%~30%Buf=54T/0.7=77T
(6)算到这:约8T*10台服务器

2)如果考虑数仓分层?数据采用压缩?需要重新再计算

2.2.6 集群资源规划设计

在企业中通常会搭建一套生产集群和一套测试集群。生产集群运行生产任务,测试集群用于上线前代码编写和测试。

1)生产集群
(1)消耗内存的分开(nn,rm)
(2)数据传输数据比较紧密的放在一起(Kafka 、Zookeeper)
(3)客户端(hive客户端,spark客户端)尽量放在一到两台服务器上,方便外部访问(风险高一点)
(4)有依赖关系的尽量放到同一台服务器(例如:Hive和Azkaban Executor)

在这里插入图片描述
2)测试集群服务器规划
在这里插入图片描述

3. 用户行为采集平台

3.2.2 埋点数据上报时机

在这里插入图片描述

3.3.6 环境变量配置说明

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.5 采集日志Flume

log->Flume->kafka

在这里插入图片描述

# 定义组件
a1.sources=r1
a1.channels=c1
# 配置source(taildirsource)
a1.sources.r1.type =TRILDIR
a1.sources.r1.filegroups =f1
a1.sources.r1.filegroups.f1=opt/module/applog/log/app.*
a1.sources.r1.positionFile = /opt/module/flume/taildir_position.json
#配置拦截器(ETL数据清洗,判断数据json是否完整)
a1.sources.r1.interceptors=i1
a1.sources.r1.interceptors.i1.type = com.atguigu.flume.interceptor.ETLInterceptor$Builder

# 配置channel
a1.channels.c1.type = org.apache.flume.channel.kafka.KafkaChannel
# 读取的是在zookeeper中的topic元数据信息,多少个分区多少个副本
a1.channels.c1.kafka.bootstrap.servers = hadoop102:9092,hadoop103:9092
a1.channels.c1.kafka.topic = topic_log
# 是否保持flume的数据格式 event
a1.channels.c1.parseAsFlumeEvent = false

# 配置sink
# 拼接组件
a1.sources.r1.channels=c1

Flume拦截器

  1. 继承Interceptor接口
  2. 实现initialize,intercept,close方法
  3. 继承Builder 接口
package com.atguigu.flume.interceptor;

import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;

import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;

public class ETLInterceptor implements Interceptor {
   

    @Override
    public void initialize() {
   

    }

    @Override
    public Event intercept(Event event) {
   
        byte[] body = event.getBody();
        String log = new String(body, StandardCharsets.UTF_8);

        if (JSONUtils.isJSONValidate(log)) {
   
            return event;
        } else {
   
            return null;
        }

    }

    @Override
    public List<Event> intercept(List<Event> list) {
   
        Iterator<Event> iterator = list.iterator();

        while (iterator.hasNext()){
   
            Event next = iterator.next();
            if(intercept(next)==null){
   
                iterator.remove();
            }
        }

        return list;

    }
    public static class Builder implements Interceptor.Builder{
   

        @Override
        public Interceptor build() {
   
            return new ETLInterceptor();
        }

        @Override
        public void configure(Context context) {
   

        }
    }

    @Override
    public void close() {
   

    }
}

JSONUtils 类

package com.atguigu.flume.interceptor;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;

public class JSONUtils {
   
        public static boolean isJSONValidate(String log){
   
            try {
   
                JSON.parse(log);
                return true;
            }catch (JSONException e){
   
                return false;
            }
        }
    }


结束进程

ps -ef | grep file-flume-kafka | grep -v grep |awk  '{print \$2}' | xargs -n1 kill -9

4.6 消费kafka数据flume

可以 kafka channel + hdfs sink,效率高。

这次用filechannel

FileChannel底层原理

在这里插入图片描述
在这里插入图片描述

编写Flume时间戳拦截器

package com.atguigu.flume.interceptor;

import com.alibaba.fastjson.JSONObject;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class TimeStampInterceptor implements Interceptor {
   


    @Override
    public void initialize() {
   

    }

    @Override
    public Event intercept(Event event) {
   
        // 将日志拦下,取出header里面的key,取出body里面的对应的日志时间:将ts的值赋值给header的key
        // 1. 获取keader里面的key
        Map<String, String> headers = event.getHeaders();
        // 2. 获取body中的ts
        byte[] body = event.getBody();
        String log = new String(body, StandardCharsets.UTF_8);
        // string 转换成json对象
        JSONObject jsonObject = JSONObject.parseObject(log);
        String ts = jsonObject.getString("ts");
        // 3. 将ts赋值给timestamp
        headers.put("timestamp",ts);

        return event;
    }

    @Override
    public List<Event> intercept(List<Event> list) {
   
        for (Event event : list) {
   
            intercept(event);
        }
        return list;
    }


    public static class Builder implements Interceptor.Builder{
   
        @Override
        public Interceptor build() {
   
            return new TimeStampInterceptor();
        }

        @Override
        public void configure(Context context) {
   

        }
    }
    @Override
    public void close() {
   

    }
}

配置conf

# 定义组件
a1.sources = r1
a1.channels = c1
a1.sinks = k1
# 配置source
a1.sources.r1.type = org.apache.flume.source.kafka.KafkaSource
# 一次消费5000条
a1.sources.r1.batchSize = 5000
# 2秒消费一次
a1.sources.r1.batchDurationMillis = 2000
a1.sources.r1.kafka.bootstrap.servers = hadoop102:9092,hadoop103:9092,hadoop104:9092
a1.sources.r1.kafka.topics=topic_log
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = com.atguigu.flume.interceptor.TimeStampInterceptor$Builder

# 配置channel
a1.channels.c1.type = file
# 备份索引
a1.channels.c1.checkpointDir = /opt/module/flume/checkpoint/behavior1
a1.channels.c1.dataDirs = /opt/module/flume/data/behavior1/

# 配置sink
a1.sinks.k1.type = hdfs
a1.sinks.k1.hdfs.path = /origin_data/gmall/log/topic_log/%Y-%m-%d
a1.sinks.k1.hdfs.filePrefix = log-
a1.sinks.k1.hdfs.round = false
#控制生成的小文件
a1.sinks.k1.hdfs.rollInterval = 10
a1.sinks.k1.hdfs.rollSize = 134217728
a1.sinks.k1.hdfs.rollCount = 0

## 控制输出文件是原生文件。
## 输出压缩的文件
a1.sinks.k1.hdfs.fileType = CompressedStream
## 编码方式lzop,支持切片
a1.sinks.k1.hdfs.codeC = lzop

## 拼装
a1.sources.r1.channels = c1
a1.sinks.k1.channel= c1

启动命令

/opt/module/flume/bin/flume-ng agent --conf-file /opt/module/flume/conf/kafka-flume-hdfs.conf --name a1 

2.电商业务数据采集平台

熟悉业务

SKU(库存量基本单位),
SPU商品信息聚合的最小单位
平台属性,
销售属性

流程:

  1. 查看各个表名,有啥关系
  2. 针对每个表,查看表名和行数据
  3. 了解表结构之后,熟悉了业务流程,其数据是如何发生变化的
  4. 在开发过程中通过sqoop导入数据时,不可以写*,因为*代表着hive中的表顺序要完全和mysql保持一致,如果mysql字段顺序改了,hive中的数据代表的意思就不一样了。

sql

启动sql

[wanghaha@hadoop102 software]$ sudo systemctl start mysqld
[wanghaha@hadoop102 software]$ sudo systemctl status mysqld
● mysqld.service - MySQL Server
   Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; vendor preset: disabled)
   Active: active (running) since 六 2022-04-23 13:12:52 CST; 1 day 4h ago
     Docs: man:mysqld(8)
           http://dev.mysql.com/doc/refman/en/using-systemd.html
  Process: 1477 ExecStart=/usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid $MYSQLD_OPTS (code=exited, status=0/SUCCESS)
  Process: 1037 ExecStartPre=/usr/bin/mysqld_pre_systemd (code=exited, status=0/SUCCESS)
 Main PID: 1480 (mysqld)
    Tasks: 27
   CGroup: /system.slice/mysqld.service
           └─1480 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid

423 13:12:50 hadoop102 systemd[1]: Starting MySQL Server...
423 13:12:52 hadoop102 systemd[1]: Started MySQL Server.

同步策略

数据同步策略的类型包括:全量同步、增量同步、新增及变化同步、特殊情况
 全量表:存储完整的数据。
 增量表:存储新增加的数据。
 新增及变化表:存储新增加的数据和变化的数据。
 特殊表:只需要存储一次。

采用不同的同步策略

编写sqoop同步命令
将mysql中order_info 表数据导入到HDFS的/test路径

全量同步

bin/sqoop import \
--connect jdbc:mysql://hadoop102:3306/gmall \
--username root \
--password 000000 \
--query 'select * from order_info where $CONDITIONS '\
--target-dir /order_info/2020-06-15 \
--delete-target-dir \
--fields-terminated-by '\t' \
--num-mappers 2 \
--split-by id

增量同步

bin/sqoop import \
--connect jdbc:mysql://hadoop102:3306/gmall \
--username root \
--password 000000 \
--query 'select * from order_info where (date_format(create_time,'%Y-%m-%d')='2020-06-15' or date_format(operate_time,'%Y-%m-%d')='2020-06-15') and $CONDITIONS '\
--target-dir /order_info/2020-06-15 \
--delete-target-dir \
--fields-terminated-by '\t' \
--num-mappers 2 \
--split-by id

2.5.2 业务数据每日同步脚本

(1)在/home/atguigu/bin目录下创建

[atguigu@hadoop102 bin]$ vim mysql_to_hdfs_init.sh

添加如下内容:
注意点:

  1. sql存储的null值和hive中存储的null不一样,hive中存储的形式是/n。Hive中的Null在底层是以“\N”来存储,而MySQL中的Null在底层就是Null,为了保证数据两端的一致性。在导出数据时采用–input-null-string和–input-null-non-string两个参数。导入数据时采用–null-string和–null-non-string
  2. 要采用lzo压缩且支持压缩,必须提前创建索引
  3. 需要传入两个参数,第一个是表名,第二个是同步数据的第一天日期
  4. 在开发过程中通过sqoop导入数据时,不可以写*,因为*代表着hive中的表顺序要完全和mysql保持一致,如果mysql字段顺序改了,hive中的数据代表的意思就不一样了。
#! /bin/bash

APP=gmall
sqoop=/opt/module/sqoop/bin/sqoop

if [ -n "$2" ] ;then
   do_date=$2
else 
   echo "请传入日期参数"
   exit
fi 

import_data(){
   
$sqoop import \
--connect jdbc:mysql://hadoop102:3306/$APP \
--username root \
--password 000000 \
--target-dir /origin_data/$APP/db/$1/$do_date \
--delete-target-dir \
--query "$2 where \$CONDITIONS" \
--num-mappers 1 \
--fields-terminated-by '\t' \
--compress \
--compression-codec lzop \
--null-string '\\N' \
--null-non-string '\\N'

hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/common/hadoop-lzo-0.4.20.jar com.hadoop.compression.lzo.DistributedLzoIndexer /origin_data/$APP/db/$1/$do_date
}

import_order_info(){
   
  import_data order_info "select
                            id, 
                            total_amount, 
                            order_status, 
                            user_id, 
                            payment_way,
                            delivery_address,
                            out_trade_no, 
                            create_time, 
                            operate_time,
                            expire_time,
                            tracking_no,
                            province_id,
                            activity_reduce_amount,
                            coupon_reduce_amount,                            
                            original_total_amount,
                            feight_fee,
                            feight_fee_reduce      
                        from order_info"
}

import_coupon_use(){
   
  import_data coupon_use "select
                          id,
                          coupon_id,
                          user_id,
                          order_id,
                          coupon_status,
                          get_time,
                          using_time,
                          used_time,
                          expire_time
                        from coupon_use"
}

import_order_status_log(){
   
  import_data order_status_log "select
                                  id,
                                  order_id,
                                  order_status,
                                  operate_time
                                from order_status_log"
}

import_user_info(){
   
  import_data "user_info" "select 
                            id,
                            login_name,
                            nick_name,
                            name,
                            phone_num,
                            email,
                            user_level, 
                            birthday,
                            gender,
                            create_time,
                            operate_time
                          from user_info"
}

import_order_detail(){
   
  import_data order_detail "select 
                              id,
                              order_id, 
                              sku_id,
                              sku_name,
                              order_price,
                              sku_num, 
                              create_time,
                              source_type,
                              source_id,
                              split_total_amount,
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值