数据仓库管理与实战

发布时间 2023-04-06 16:57:15作者: LHX2018

该笔记为视频教程笔记
课程链接:https://www.bilibili.com/video/BV1qv411y7Wv/?spm_id_from=333.999.0.0&vd_source=4042938bc91623dd366533263863ec49

1. 课程介绍

  • 数据仓库的广泛应用
    • 传统数据仓库
    • 大数据数据仓库
  • 体系化数据仓库理论
    • 什么是数据仓库?它是如何发展而来的?
    • 数据仓库的整体架构
    • 数据仓库建模
    • 数据仓库生产经验
    • 数据仓库落地&实践开发
主题 知识点
简介 诞生背景
基本概述
技术方案
架构 通用架构
ETL流程
数据积存(ODS层)
数据分析(DWD、DWS、ADS层)
建模 OLTP、OLAP异同
ROLAP、MOLAP建模
多维分析
最佳实践 数据仓库表类型
ETL策略
任务调度
项目实战 大数据数据仓库集群搭建
复购率分析
GMV(成交总额)分析

1.1 诞生背景

  • 数据仓库诞生原因
    • 历史数据积存
    • 企业数据分析需要
  • 历史数据积存
    • 使用频率低,堆积在业务库中,导致性能下降
  • 企业数据分析需要
    • 各部门自己建立独立的数据抽取系统,导致数据不一致

1.2 基本概述

数据仓库(data warehouse , dw)

  • 是一个面向主题的、集成的、非易失的且随时间变化的数据集合
  • 主要用于组织积累的历史数据并使用分析方法(OLAP、数据分析) 进行分析整理,进而辅助角色,为管理者、企业系统提供数据支持,构建商业智能

数据仓库特点

  • 面向主题,根据主题将原始数据集合在一起
  • 集成:原始数据来源于不同数据源,要整合成最终数据,需要经过抽取、清晰、转换的过程

image

  • 非易丢:保存的数据是一系列历史快照,不允许被修改,只允许通过工具进行查询、分析
  • 时变性:数仓会定期接收、集成新的数据,从而反映出数据的最新变化

数据仓库 VS 数据库

  • 数据库面向事务设计,属于OLTP(在线事务处理)系统,主要操作是随机读写:在设计时尽量避免冗余,常常用符合范式规范来设计
  • 数据仓库是面向主题设计,属于OLAP(在线分析处理),主要操作是批量读写,关注数据整合,以及分析、处理性能,会有意引入冗余,采用反范式方式设计
数据库 数据仓库
面向 事务 分析
数据类型 细节、事务 综合、清洗过的数据
数据特点 当前的、最新的 历史的、跨时间维护
目的 日常操作 长期信息需求、决策支持
设计模型 基于ER模型,面向应用 星型/雪花模型,面向主题
操作 读/写 大多为读
数据规模 GB到TB >=TB

数据仓库建设方案

  • 传统数据仓库
    • 由关系型数据库组成MPP(大规模并行处理)集群
    • 问题:扩展性有限;热点问题(某一个节点的数据访问频率过高,可以使用加盐来均匀分布,但会带来额外的操作)
  • 大数据数据仓库
    • 利用大数据天然的扩展性,完成海量数据的存放
    • 将SQL转换为大数据计算引擎任务,完成数据分析
    • 问题:SQL支持率、事务支持

1.3 MPP & 分布式架构

MPP架构

  • 传统数仓中常见的技术架构,将单机数据库节点组成集群,提升整体处理性能
  • 节点间为非共享架构(Share Nothing),每个节点都有独立的磁盘存储系统和内存系统
  • 每台数据节点通过专用网络或者商业通用网络相互连接,彼此协同计算,作为整体提供服务
  • 设计上优先考虑C(一致性),其次考虑A(可用性),尽量最好P(分区容错性)
  • 优点
    • 运算方式精细,延迟低,吞吐低
    • 适合中等规模的结构化数据处理
  • 缺点
    • 存储位置不透明,通过Hash确定数据所在的物理节点,查询任务在所有节点均会执行
    • 并行计算时,单节点瓶颈会成为整个系统短板,容错性差
    • 分布式事务的实现会导致扩展性降低

分布式架构

  • 大数据中常见的技术架构,也称为Hadoop架构/批处理架构
  • 各节点实现场地自治(可以单独运行局部应用),数据在集群中全局透明共享
  • 每台节点通过局域网或广域网相连,节点间的通信开销较大,在运算时致力减少数据移动
  • 优先考虑的是P(分区容错性),然后是A(可用性),最好再考虑C(一致性)

MPP + 分布式架构

1.4 常见产品

传统数据仓库

  • Oracle RAC

  • DB2

  • Teradata

  • Greenplum(开源 Postgre SQL)

大数据数据仓库

  • Hive
  • Spark SQL
  • HBase(适合 NOSQL)
  • Impala(数据查询引擎)
  • HAWQ(Greenplum在Hadoop的移植产品)
  • TIDB(New SQL MPP + SMP,底层NoSQL,支持SQL,主要兼容MySQL)

2. 架构

2.1 架构图

image
Other:Flume、LogStash(日志、文件)

ODS:存储原始数据,定期删除业务数据库数据

DWD:存储三范式的数据

DWS:宽表

ADS:数据集市层

2.2 ETL流程

  • Extract 抽取

    • 数据源分为结构化数据、非结构化数据、半结构化数据
    • 结构化数据一般采用JDBC、数据库日志方式(CDC),非|半结构化数据会监听文件变动
    • 抽取方式
      • 有全量同步、增量同步两种方式
      • 全量会将全部数据进行抽取,一般用于初始数据状态
      • 增量会检测数据的变动,抽取发生变动的数据,一般用于数据更新
  • Transform 转换

    • 数据清洗:对出现的重复、二义性、不完整、违反业务或逻辑规则等问题的数据进行统一的处理
    • 数据转换:对数据进行标准化处理,进行字段、数据类型、数据定义的转换
    • 结构化数据在转换过程中的逻辑较为简单,非|半结构化数据的转换会较为复杂
  • Load 转换

    • 将最后处理玩的数据导入到对应的目标源里

ETL规则的设计和实施约占整个数据仓库搭建工作流的60%和80% (如果是非结构化数据会花更多时间)

结构化数据ETL工具

  • Sqoop
  • Kettle(可视化)
  • Datastage(收费)
  • Informatica(收费)
  • Kafka

非|半结构化数据ETL工具

  • Flume
  • Logstash

2.3 数据积存

操作数据层(ODS)

  • 数据与原业务数据保持一致,可以增加字段用来进行数据管理
  • 存储的历史数据是只读的,提供业务系统查询使用
  • 业务系统对历史数据完成修改后,将update_type字段更新为UPDATE,追加回ODS中
    image
  • 在离线数仓中,业务数据定期通过ETL流程导入到ODS中,导入方式有全量、增量两种
    • 全量:第一次导入时,选择此种方式
    • 增量:第一次导入时,每次只需要导入新增、更改的数据,建议使用外连接&全覆盖方式

2.4 数据明细层(DWD)

  • 数据明细层对ODS层的数据进行清洗、标准化、维度退化(时间、分类、地域)
  • 数据仍然满足3NF模型,为分析运算做准备
    image

2.5 数据汇总层(DWS)

  • 按照分析主题进行计算汇总,存放便于分析的宽表
  • 存储模型并非3NF,而是注重数据聚合,复杂查询、处理性能更优的数仓模型,如维度模型
    image

2.6 数据应用层(ADS)

  • 数据应用层也被成为数据集市
  • 存储数据分析结果,为不同业务场景提供接口,减轻数据仓库的负担
  • 数据仓库擅长数据分析,直接开放业务查询接口,会加重其负担
    image

3. 建模方法

3.1 基本概念

OLTP系统建模方法

  • OLTP(在线事务处理)系统中,主要操作是随机读写
  • 为了保证数据一致性、减少冗余,常使用关系模型
  • 在关系模型中,使用三范式规则来减少冗余

OLAP(在线联机分析)

  • OLAP系统,主要操作是复杂分析查询,关注数据整合,以及分析、处理性能
  • OLAP根据数据存储的方式不同,又分为ROLAP、MOLAP、HOLAP

OLAP系统分类

  • ROLAP(Relation OLAP,关系型OLAP):使用关系模型构建,存储系统一般为RDBMS
  • MOLAP(Multidimensional OLAP,多维型OLAP):预先聚合计算,使用多维数组的形式保存数据结果,加快查询时间
  • HOLAP(Hybrid OLAP,混合架构的OLAP):ROLAP和MOLAP两者的集成;如低层是关系型的,高层是多维矩阵的;查询效率高于ROLAP,低于MOLAP

3.2 ROLAP

ROLAP系统建模方法:典型的数据仓库建模方法有ER模型、维度模型、Data Value、Anchor

  • ER模型

    • 出发点是整合数据,为数据分析决策服务
    • 需要全面了解业务和数据
    • 实施周期长
    • 对建模人员能力要求高
  • 分为星型模型、雪花模型、星座模型

    • 星座
      • 是基于多个事实表,事实表之间会共享一些维度表
      • 是大型数据仓库中的常态,是业务增长的结果,与模型设计无关
    • 雪花
      • 具有多层维度,比较接近三范式设计,较为灵活
        image
        image
  • 维度模型

    • 为分析需求服务,更快完成需求分析
    • 具有较好大规模复杂查询相应性能
    • 最流行的数仓建模经典
  • Data Value

    • ER模型的衍生
    • 强调数据的历史性、可追溯、原子性
    • 弱化一致性处理和整合
    • 引入范式,应对源系统的扩展性
  • Anchor

    • Data Value模型的衍生
    • 初衷为设计一个高度可扩展模型
    • 会带来较多的join操作

什么是宽表模型?

  • 宽表模型是维度模型的衍生,适合join性能不佳的数据仓库产品
  • 宽表模型将维度冗余到事实表中,形成宽表,以此减少join操作

3.3 MOLAP

MOLAP系统建模方法(对应AWS)

  • MOLAP将数据进行预结算,并将聚合结果存储到CUBE模型中

  • CUBE模型以多维数组的形式,物化到存储系统中,加快后续的查询

  • 生成CUBE需要大量的时间、空间,维度预处理可能会导致数据膨胀
    image
    常见MOLAP产品

  • Kylin

  • Druid
    image

3.4 多维分析

OLAP多维分析

  • OLAP主要操作是复杂查询,可以多表关联,使用COUNT、SUM、AVG等函数

  • OLAP对复杂查询操作做了直观的定义,包括钻取、切片、切块、旋转
    image
    钻取

  • 对维度不同层次的分析,通过改变维度的层次来变化分析的粒度

  • 钻取包括上卷(Roll-up)、下钻(Drill-down)

  • 上卷(Roll-up),也称为向上钻取,指从低层次到高层次的切换

  • 下钻(Drill-down),指从高层次到低层次的切换
    image
    驱蚊剂,防晒装备,急救用品 -》 户外防护用品 (上钻)

年 -》季度,月,周,日 (下钻)

切片(Slice)、切块(Dice)

  • 选择某个维度进行分割成为切片

  • 按照多维进行的切片成为切块
    image
    旋转(Pivot)

  • 对维度方向的互换,类似于交换坐标轴上卷(Roll-up)
    image

4. 最佳实践

4.1 表的分类

维度建模中的表类型

  • 事实表
  • 维度表
  • 事务事实表
  • 周期快照事实表
  • 累计快照事实表

事实表

  • 一般是指一个现实存在的业务对象,比如用户,商品,商家,销售员等

维度表

  • 一般是指对应一些业务状态,代码的解释表,也可以成为码表
  • 通常使用维度对事实表中的数据进行统计、聚合运算
订单状态 状态名称 商品分类编号 分类名称
1 未支付 1 生活
2 已支付 2 科技
3 发货中 3 少儿
4 已发货
5 已完成

事务事实表

  • 随着业务不断产生的数据,一旦产生不会再变化,如交易流水、操作日志、出库入库记录
    image
    周期快照事实表

  • 随着业务周期型的推进而变化,完成间隔周期内的度量统计,如年、季度累计

  • 使用周期+状态度量的组合,如年累计订单数,年是周期,订单总数是量度
    image
    累计快照事实表

  • 记录不确定周期的度量统计,完全覆盖一个事实的生命周期,如订单状态表

  • 通常有多个时间字段,用于记录生命周期中的关键时间点

  • 只有一条记录,针对此记录不断更新
    image
    累计快照事实表实现方式一

  • 使用日期分区表,全量数据记录,每天的分区存储昨天全量数据与当天增量数据合并的结果

  • 数据量大会导致全量表膨胀,存储大量永远不更新的冷数据,对性能影响较大

  • 适用于数据量少的情况

实现方式二

  • 使用日期分区表,推测数据最长生命周期,存储周期内数据:周期外的冷数据存储到归档表
  • 需要保留多天的分区数据,存储消耗依然很大

实现方式三

  • 使用日期分区表,以业务实体的结束时间分区,每天的分区存放当天结束的数据:设计一个时间非常大的分区,如9999-12-31,存放截止当前未结束的数据
  • 已结束的数据存放到相应分区,存放未结束的分区,数据量也不会很大,ETL性能好
  • 无存储浪费,数据全局唯一
  • 业务系统可能无法标识业务主体的结束时间(第三方系统),可以使用其他相关业务系统的结束标志作为此业务系统的结束,也可以使用最长生命周期或前端系统的数据归档时间

拉链表

  • 拉链表记录每条信息的生命周期,用于保留数据的所有历史(变更)状态
  • 拉链表将表数据的随机修改方式,变为顺序追加
    image

4.2 ETL策略

全量同步

  • 数据初始化装在一定使用全量同步的方式
  • 因为业务、技术原因,使用全量同步的方式做周期数据更新,直接覆盖原有数据即可

增量同步

  • 传统数据整合方案中,大多采用merge方式(update+insert)
  • 主流大数据平台不支持update操作,可采用全外连接+数据全量覆盖方式(如果担心数据更新出错,可以采用分区方式,每条保存最新的全量版本,保留较短周期)
    image

4.3 任务调度

为什么需要任务调度

  • 解决任务单元间的依赖关系
  • 自动化完成任务的定时执行

常见任务类型

  • Shell

  • Java程序

  • Mapreduce程序

  • SQL脚本
    image
    常见调度工具

  • Azkaban

  • Oozie

特性 Azkaban Oozie
工作流描述语言 text file with key/value pairs XML
是否要web容器 yes yes
进度跟踪 web page web page
Hadoop job调度支持 yes yes
运行模式 daemon daemon
事件通知 yes no
需要安装 yes yes
支持的hadoop版本 currently unknown 0.20+
重试支持 yes workflownode evel
运行任意命令 yes yes

5. 项目实战

5.1 项目概述

项目背景:

  • 某电商企业,因数据积存、分析需要,筹划搭建数据仓库,提供数据分析访问接口
  • 项目一期需要完成数仓建设,并完成用户复购率的分析计算,支持业务查询需求

复购率计算

  • 复购率是指在一段时间间隔内,多次重复购买产品的用户,占全部人数的比率
  • 统计各个一级品类下,品牌月单次复购率,和多次复购率
    image

5.2 数据描述

  • 用户表(user_info)
字段 含义
id 用户id
name 姓名
brithday 生日
gender 性别
email 邮箱
user_level 用户等级
create_time 创建时间
  • 商品表
字段 含义
id skuId
spu_id spuId
price 价格
sku_name 商品名称
sku_desc 商品概述
weight 重量
tm_id 品牌id
category3_id 品类id(三级品类)
create_time 创建时间
  • 订单表
字段 含义
id 订单编号
total_amount 订单金额
order_status 订单状态
user_id 用户id
payment_way 支付方式
out_trade_no 支付流水号
create_time 创建时间
  • 订单详情表
字段 含义
id 订单编号
order_id 订单号
user_id 用户id
sku_id 商品id
sku_name 商品名称
order_price 下单价格
sku_num 商品数量
create_time 创建时间

数据描述

  • 商品一级分类表(base_category1)
字段 定义
id id
name 名称
  • 商品二级分类表(base_category2)
字段 含义
id id
name 名称
category1_id 一级品类id
  • 商品三级分类表(base_category3)
字段 含义
id id
name 名称
category2_id 二级品类id
  • 支付流水表
字段 含义
id 编号
out_trade_no 对外业务编号
order_id 订单编号
user_id 用户编号
alipay_trade_no 支付宝交易流水账号
total_amount 支付金额
subject 交易内容
payment_type 支付类型
payment_time 支付时间

5.3 架构设计

image

5.4 环境搭建

环境说明

  • CentOS 7.2
  • Hadoop 2.7.7
  • Hive 1.2.1
  • Tez 0.9.1
  • Mysql 5.7.28
  • Sqoop 1.4.6
  • Azkaban 2.5.0
  • Presto 0.196

集群规划

  • 使用三台虚拟机进行搭建
Hadoop Hive&Tez Mysql Sqoop Azkaban Presto
node01 1(master) 1 1
node02 1 1 1(master) 1(master)
node03 1 1 1 1 1

0.使用脚本安装三个节点

1.安装各种软件

2.测试三个节点是否互通

5.5 项目开发

整体开发流程

  1. 业务数据生成
  2. ETL数据导入
  3. 创建ODS层,并完成HDFS数据接入
  4. 创建DWD层,并完成ODS层数据导入
  5. 创建DWS层,导入DWD层数据
  6. 创建ADS层,完成复购率计算
  7. 编写脚本,将ADS层的数据导出到MySQL中,供业务查询
  8. 使用Azkaban调度器,实现脚本自动化运行

业务数据生成

  • 进入Mysql,创建数据库Mall
export MYSQL_PWD=DBa2020*
mysql -uroot -e "create database mall;"
  • 上传数据生成sql脚本到Mysql安装节点
  • 使用命令方式,将数据生成sql脚本导入到Mysql中
  • 使用命令方式,将数据生成sql脚本导入到mysql中
mysql -uroot mall < {pathToSQL}
  • 进入mysql,生成数据
use mall;
-- 生成日期2020-06-10日数据,订单300个,用户200个,商品sku300个,不删除数据
call init_data('2020-06-10',300,200,300,false);
  • 编写脚本(sqoop_import.sh)
#!/bin/bash

#传入两个参数,$1为为表名,$2为时间,决定抽取特定日期的数据

db_date=$2
echo $db_date
db_name=mall

import_data() {
    sqoop import \
  --connect jdbc:mysql://node02:3306/$db_name \
  --username root \
  --password DBa2020* 
  # 指定分割符  \表示换行
  --fields-terminated-by "\t" \
  # 存放的目录
  --target-dir /origin_data/$db_name/db/$1/$db_date \
  # 目标目录已存在则删除
  --delete-target-dir \
  # 并发执行的数量
  --num-mappers 1 \
  --query "$2"' and $CONDITIONS;'
  
}

import_sku_info(){
    import_data "sku_info" "select id,spu_id,price,sku_name,sku_desc,weight,tm_id,category3_id,create_time from sku_info where 1=1 "
}

import_user_info(){
    import_data "user_info" "select id,name,birthday,gender,email,user_level,create_time from user_info where 1=1"
}

import_base_category1(){
    import_data "base_category1" "select id,name,category1_id from base_category1 where 1=1"
}

import_base_category2(){
    import_data "base_category2" "select id,name,category1_id from base_category2 where 1=1"
}

import_order_detail(){
    import_data "order_detail" "select od.id,order.id,user_id,sku_id,sku_name,order_price,sku_num,o.create_time from order_info o,order_detail od where o.id = od.order_id and DATE_FORMAT(create_time,'%Y-%m-%d')='$db_date'"
}

import_order_info(){
    import_data "order_info" "select id,total_amount,order_status,user_id,payment_way,out_trade_no,create_time,operate_time from order_info where (DATE_FORMAT(create_time,'%Y-%m-%d')='$db_date' or DATE_FORMAT(operate_time,'%Y-%m-%d')='$db_date')" 
}

import_payment_info(){
    import_data "payment_info" "select id,out_trade_no,order_id,user_id,alipay_trade_no,total_amount,subject,payment_type,payment_time from payment_info where
    (DATE_FORMAT(payment_time,'%Y-%m-%d')='$db_date'" 
}

case $1 in
  "base_category1")
  	import_base_category1
;;
  "base_category2")
    import_base_category2
;;
  "base_category2")
    import_base_category3
;;
  "order_info")
    import_order_info
;;
  "order_detail")
    import_order_detail
;;
  "sku_info")
    import_sku_info
;;
  "user_info")
    import_user_info
;;
  "payment_info")
    import_payment_info
;;
  "all")
  import_base_category1
  import_base_category2
  import_base_category3
  import_order_info
  import_order_detail
  import_sku_info
  import_user_info
  import_payment_info
;;
easc
  • 执行命令
./sqoop_import.sh all 2020-08-29
  • ods_sql.sql
--hive的数据
-- 创建订单表
create table ods_order_info (

) comment '订单表'
--根据日期分区
partitioned by (`dt` string)
row format delimited fields terminated by '\t'
location '/warehouse/mall/ods/ods_order_info/'
-- 压缩方式
tblproperties ("parquet.compression"="snappy")

--订单详情表

--商品表

--用户表

--一级分类表

--二级分类表

--三级分类表

--支付流水表
  • ods数据导入ods_db.sh
#!/bin/bash

#传入一个参数 $1为时间
do_date=$1
APP=mall
hive=hive

sql="
load data inpath '/origin_data/$APP/db/order_info/$do_date' OVERWRITE into table $APP"".dos_order_info partition(dt='$do_date')

load data inpath '/origin_data/$APP/db/order_detail/$do_date' OVERWRITE into table $APP"".dos_order_detail partition(dt='$do_date')

# ...六张表
$hive -e "$sql"
  • 执行命令 ./ods_db.sh 2020-08-29
  • dwd层创建和数据接入(dwd_ddl.sql)
-- 创建六张表
-- 商品分类表与之前有区别,加了6个字段
create external table dwd_sku_info(
	`id` string comment 'skuId',
    spu_id
    price
    sku_name
    sku_desc
    weight
    tm_id
    category3_id
    category2_id
    category1_id
    category3_name
    category2_name
    category1_name
    create_time
)
  • 执行sql文件 hive -f /home/warehouse/sql/dwd_ddl.sql

  • 导入数据dwd_db.sh

#!/bin/bash

APP=mall
hive=hive

# 指定日期
if [ -n $1 ] ; then
	log_date=$1
else
# 否则是昨天
	log_date=`date -d `-1 day" +%F"
fi

sql="
set hive.exec.dynamic.partition.mode=nonstrict;

insert overwrite table "$APP".dwd_order_info partition(dt)
# 为简便用了*号,实际环境必须指定字段
select * from "$APP".ods_order_info
where dt='$log_date' and id is not null;
"

# 商品分类表因为多了6个字段,所以要join
  • dws层创建&导入 (聚合成宽表)
    image
  • 常见sql和sh,同上
  • aws sql
insert into table "$APP".ads_sale_tm_category1_stat_mn
select
	mn.sku_tm_id,
	mn.sku_category1_id,
	mn.sku_category1_name,
	sum(if(mn.order_count >= 1,1,0)) buycount,
	sum(if(mn.order_count >= 2,1,0)) buyTwiceLast,
	sum(if(mn.order_count >= 2,1,0) / sum(if(mn.order_count >= 1,1,0))) buyTwiceLastRatio
from mn
group by mn.sku_tm_id,mn.sku_category1_id,mn.sku_category1_name;