离线数仓(八)【DWD 层开发】(1)

本文涉及的产品
云原生数据仓库AnalyticDB MySQL版,基础版 8ACU 100GB 1个月
简介: 离线数仓(八)【DWD 层开发】

前言

1、DWD 层开发

DWD层设计要点

(1)DWD层的设计依据是维度建模理论(主体是事务型事实表(选择业务过程 -> 声明粒度 -> 确定维度 -> 确定事实),另外两种周期型快照事实表和累积型事务事实表按需求选择),该层存储维度模型的事实表。

(2)DWD层的数据存储格式为orc列式存储+snappy压缩(和DIM层、DWS层都是一样的)。

(3)DWD层表名的命名规范为dwd_数据域_表名_单分区增量全量标识(inc/full)(划分数据域的目的是为了通过对数据分类使得从业务系统中可以快速的找到我们希望得到的数据划分数据域的标准是按照业务过程,将若干个业务过程划分到一个数据域里面,其实所谓的划分数据域就是在划分事实表,因为一个业务过程对应一个事实表)。

1.1、交易域加购事务事实表

       加购指的是加入购物车这个业务过程,我们按照设计事务事实表的过程来设计它的表结构:

1.1.1、选择业务过程

       就是加购物车这个行为

1.1.2、声明粒度

       要求尽可能选择最细粒度,声明粒度将来指代的是将来这张表的每一行所代表的内容。这里我们声明的粒度就是:谁+在什么时候+把什么商品加到了购物车

1.1.3、确认维度

       我们首先能想到的就是用户、时间和商品,至于其它维度我们在设计事实表的时候尽可能多的考虑到,避免后期一些指标无法分析。

       确认了维度,我们就确认了我们这张事务事实表的维度外键。

1.1.4、确认事实

       确认事实就是确认事实表的度量值,对于这里的加购操作度量值主要就是加购的商品件数。

1.1.5、建表语句

       这里我们的表名由几部分组成:dwd 代表这张表是 dwd 层的事实表;trade 代表数据域是交易域;cart_add 代表这张事实表的业务过程是加购操作;inc 因为这张表是事务型事实表所以我们一般都是增量表。

       再看字段:其中 id 选取的是我们业务系统中 cart_info 的 id;user_id sku_id date_id 是我们的维度外键;sku_num 是我们的度量值。剩下的 create_time 是具体的加购时间,精确到秒,它区别于维度外键 date_id,date_id 是日期,只能精确到哪一天;source_id source_type_code source_type_name 这些字段都是来自我们业务系统的,我们之前说 DIM 层和 DWD 层的维度表和事实表都是业务驱动的,所以它们各自表的设计字段的选择来源于我们的业务系统中表,这里的加购事务事实表就是对应我们业务系统当中的 cart_info:

通过查询字典表可以看到:

不同的 source_type_code 代表不同类型的加购操作(用户通过哪种来源类型来加购的,比如用户通过广告来加购的,那通过哪个广告呢,所以这里我们还指定了 source_id),这些字段都算是维度,但是我们并没有对这些属性做一个维度表,而是直接放到事实表里(也就是维度退化,而维度退化要求不能只有编码,一般都是编码和文字描述共存的;所以这里我们保留了来源类型编码和来源类型名称)。

DROP TABLE IF EXISTS dwd_trade_cart_add_inc;
CREATE EXTERNAL TABLE dwd_trade_cart_add_inc
(
    `id`               STRING COMMENT '编号',
    `user_id`          STRING COMMENT '用户id',
    `sku_id`           STRING COMMENT '商品id',
    `date_id`          STRING COMMENT '时间id',
    `create_time`      STRING COMMENT '加购时间',
    `source_id`        STRING COMMENT '来源类型ID',
    `source_type_code` STRING COMMENT '来源类型编码',
    `source_type_name` STRING COMMENT '来源类型名称',
    `sku_num`          BIGINT COMMENT '加购物车件数'
) COMMENT '交易域加购物车事务事实表'
    PARTITIONED BY (`dt` STRING)
    ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
    STORED AS ORC
    LOCATION '/warehouse/gmall/dwd/dwd_trade_cart_add_inc/'
    TBLPROPERTIES ('orc.compress' = 'snappy');

1.1.6、数据流向

       现在我们表设计好了下一步就是数据源从哪来(从 ods 层的哪些表来),以及我们怎么向这张表进行装载(insert + select),这就需要我们了解一下这张表的不同维度的数据应该来自于哪些表。

       我们首先需要了解业务系统中 order_info 这张表:

       其实,对于加购这个过程无非只有两种情况:1、原本购物车没有这个商品然后进行加购,这是对数据库来说是一个 insert 操作;2、原本购物车就有这个商品然后加购,这对数据库来说是一个 update 操作。显式的变化就是 sku_num 的值的变化。

       这样我们就搞清楚了加购这个业务过程,会对 order_info(或者可以说是 ods_order_info_inc) 产生影响,产生的影响就是 sku_num 字段的值发生变化。

现在我们不仅要知道数据从张表来,还得知道数据从具体哪个分区流向哪个分区:

首日数据

       所有的增量表都要在首日做一个全量同步,这里我们的加购表当前存储的就是首日的全量数据,我们需要对字段 create_time 做一个动态分区来把不同时间的加购信息放到不同的分区:

每日数据

       解决了首日全量数据的分区问题,我们之后 ods 层每天的分区中存储的就是 Maxwell 监听的每日的增量数据,所以这些都是加购信息,我们直接存储当当日分区即可:

1.1.7、首日装载语句

-- 动态分区需要设置非严格模式
set hive.exec.dynamic.partition.mode=nonstrict;
insert overwrite table dwd_trade_cart_add_inc partition (dt)
select
    id,
    user_id,
    sku_id,
    date_format(create_time,'yyyy-MM-dd') date_id,
    create_time,
    source_id,
    source_type,
    dic.dic_name,
    sku_num,
    date_format(create_time, 'yyyy-MM-dd')
from
(
    select
        data.id,
        data.user_id,
        data.sku_id,
        data.create_time,
        data.source_id,
        data.source_type,
        data.sku_num
    from ods_cart_info_inc
    where dt = '2020-06-14'
    and type = 'bootstrap-insert'
)ci
left join
(
    select
        dic_code,
        dic_name
    from ods_base_dic_full
    where dt='2020-06-14'
    and parent_code='24'
)dic
on ci.source_type=dic.dic_code;

1.1.8、每日装载语句

       我们比如要装载 06-15 这一天的数据,首先我们需要保证 type 为 insert 或者 update 类型的操作,并且加购后的 sku_num 的值要大于旧的 sku_num 值。

insert overwrite table dwd_trade_cart_add_inc partition(dt='2020-06-15')
select
    id,
    user_id,
    sku_id,
    date_id,
    create_time,
    source_id,
    source_type_code,
    source_type_name,
    sku_num
from
(
    select
        data.id,
        data.user_id,
        data.sku_id,
        date_format(from_utc_timestamp(ts*1000,'GMT+8'),'yyyy-MM-dd') date_id,
        date_format(from_utc_timestamp(ts*1000,'GMT+8'),'yyyy-MM-dd HH:mm:ss') create_time,
        data.source_id,
        data.source_type source_type_code,
        if(type='insert',data.sku_num,data.sku_num-old['sku_num']) sku_num
    from ods_cart_info_inc
    where dt='2020-06-15'
    and (type='insert'
    or(type='update' and old['sku_num'] is not null and data.sku_num>cast(old['sku_num'] as int)))
)cart
left join
(
    select
        dic_code,
        dic_name source_type_name
    from ods_base_dic_full
    where dt='2020-06-15'
    and parent_code='24'
)dic
on cart.source_type_code=dic.dic_code;

       这里在判断是否修改了 order_info 中 sku_num 值的时候我们还可以通过下面的方式来确认修改之前包含该字段:

type='update' and array_contains(map_keys(old),'sku_num')

       这里我们不能使用 create_time 作为加购时间,因为 create_time 是我们创建这个购物车的时间,而不是真正的加购时间,所以我们应该用 ods_cart_info_inc 中的 ts 自动,因为它代表的是订单的变动时间:

       对于不同的操作类型(insert 或 update)sku_num 的值也是不一样的,对于 insert 操作,这个商品是第一次加入购物车,所以 sku_num 就是加购的件数;但是对于 update,sku_num 指的是加购后的件数,所以实际加购的数量=old['sku_num']-data.sku_num 。

       所以只要我们清楚了加购这个业务过程对订单表的影响,装载这张表就很简单了。

1.2、交易域下单事务事实表

同样分两步:设计表结构(4步),编写装载语句

1.2.1、设计表结构

  1. 选择业务过程:我们选择的是下单这个业务过程
  2. 声明粒度:要求尽可能最细粒度,所以这里的粒度应该是一个订单中的一个商品项而不是一整个订单,所以我们也就可以想到这张事实表对应业务系统中的 order_detail 表。
  3. 确认维度:关于下单操作,我们能想到的维度比如:时间、用户、商品、地区、活动和优惠券。以及下单方式,我们之前说过:如果某些维度表的维度属性很少(比如支付方式表没有必要去单独创建一个维度表,因为它就一个支付方式字段),则可不创建该维度表,而把该表的维度属性直接增加到与之相关的事实表中对于维度退化。确认维度是个灵活的过程,它并不取决于我们要分析什么指标,而是取决于我们业务系统能提供什么信息,比如这里的 order_detail 中包含了下单地区、该订单参与的活动以及使用优惠券的信息才能支持我们确认这样的维度
  4. 确认事实:也就是确认度量值,这里的度量值可以是:下单件数、下单原始金额、下单的最终金额、活动优惠金额和优惠券优惠金额等。

1.2.2、建表语句

       同样,这里的维度可以分为四部分:

  1. 从业务系统表当中直接拿过来的字段(比如 id、order_id 、create_time、source_id、source_type)
  2. 用作关联维度表的维度外键(user_id、sku_id、province_id、activity_id(算是退化字段,因为它和activity_rule_id都在同一张表,但是activity_rule_id的粒度更细)、activity_rule_id 、coupo_id、date_id等)
  3. 度量指标字段(比如 sku_num、split_xxx ...)
  4. 维度退化字段(比如 activity_id、source_type_code、source_id、source_type_name 等)
DROP TABLE IF EXISTS dwd_trade_order_detail_inc;
CREATE EXTERNAL TABLE dwd_trade_order_detail_inc
(
    `id`                    STRING COMMENT '编号',
    `order_id`              STRING COMMENT '订单id',
    `user_id`               STRING COMMENT '用户id',
    `sku_id`                STRING COMMENT '商品id',
    `province_id`           STRING COMMENT '省份id',
    `activity_id`           STRING COMMENT '参与活动规则id',
    `activity_rule_id`      STRING COMMENT '参与活动规则id',
    `coupon_id`             STRING COMMENT '使用优惠券id',
    `date_id`               STRING COMMENT '下单日期id',
    `create_time`           STRING COMMENT '下单时间',
    `source_id`             STRING COMMENT '来源编号',
    `source_type_code`      STRING COMMENT '来源类型编码',
    `source_type_name`      STRING COMMENT '来源类型名称',
    `sku_num`               BIGINT COMMENT '商品数量',
    `split_original_amount` DECIMAL(16, 2) COMMENT '原始价格',
    `split_activity_amount` DECIMAL(16, 2) COMMENT '活动优惠分摊',
    `split_coupon_amount`   DECIMAL(16, 2) COMMENT '优惠券优惠分摊',
    `split_total_amount`    DECIMAL(16, 2) COMMENT '最终价格分摊'
) COMMENT '交易域下单明细事务事实表'
    PARTITIONED BY (`dt` STRING)
    ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
    STORED AS ORC
    LOCATION '/warehouse/gmall/dwd/dwd_trade_order_detail_inc/'
    TBLPROPERTIES ('orc.compress' = 'snappy');

1.2.3、数据流向

       在数据装载之前我们同样需要了解数据的流向,下单这个业务过程会对哪些表产生影响我们就从哪个表去获取数据。

      这里的下单这个操作会影响到 order_info(会插入一条订单数据)、order_detail(会插入多条数据,取决于订单的中的商品数量)、order_detail_coupon(用户使用优惠券就会在这张表中插入数据)和 order_detail_activity(用户参与活动就会往这张表插入数据)。

       相比较上面的加购操作,这里的下单操作虽然涉及到的表更多,但是它都是插入(insert)操作,所以事实上装载的逻辑要比加购简单。同时我们依然要区分首日和每日的数据。

1.2.4、首日数据装载

逻辑还是比较简单的,就是把 order_detail 作为主表不断进行 left join :

set hive.exec.dynamic.partition.mode=nonstrict;
insert overwrite table dwd_trade_order_detail_inc partition (dt)
select
    od.id,
    order_id,
    user_id,
    sku_id,
    province_id,
    activity_id,
    activity_rule_id,
    coupon_id,
    date_format(create_time, 'yyyy-MM-dd') date_id,
    create_time,
    source_id,
    source_type,
    dic_name,
    sku_num,
    split_original_amount,
    split_activity_amount,
    split_coupon_amount,
    split_total_amount,
    date_format(create_time,'yyyy-MM-dd')
from
(
    select
        data.id,
        data.order_id,
        data.sku_id,
        data.create_time,
        data.source_id,
        data.source_type,
        data.sku_num,
        data.sku_num * data.order_price split_original_amount,
        data.split_total_amount,
        data.split_activity_amount,
        data.split_coupon_amount
    from ods_order_detail_inc
    where dt = '2020-06-14'
    and type = 'bootstrap-insert'
) od
left join
(
    select
        data.id,
        data.user_id,
        data.province_id
    from ods_order_info_inc
    where dt = '2020-06-14'
    and type = 'bootstrap-insert'
) oi
on od.order_id = oi.id
left join
(
    select
        data.order_detail_id,
        data.activity_id,
        data.activity_rule_id
    from ods_order_detail_activity_inc
    where dt = '2020-06-14'
    and type = 'bootstrap-insert'
) act
on od.id = act.order_detail_id
left join
(
    select
        data.order_detail_id,
        data.coupon_id
    from ods_order_detail_coupon_inc
    where dt = '2020-06-14'
    and type = 'bootstrap-insert'
) cou
on od.id = cou.order_detail_id
left join
(
    select
        dic_code,
        dic_name
    from ods_base_dic_full
    where dt='2020-06-14'
    and parent_code='24'
)dic
on od.source_type=dic.dic_code;

1.2.5、每日装载语句

        对于之后的每日装载,我们只需要确保每张表的 type 为 insert、dt = 当天即可。

insert overwrite table dwd_trade_order_detail_inc partition (dt='2020-06-15')
select
    od.id,
    order_id,
    user_id,
    sku_id,
    province_id,
    activity_id,
    activity_rule_id,
    coupon_id,
    date_id,
    create_time,
    source_id,
    source_type,
    dic_name,
    sku_num,
    split_original_amount,
    split_activity_amount,
    split_coupon_amount,
    split_total_amount
from
(
    select
        data.id,
        data.order_id,
        data.sku_id,
        date_format(data.create_time, 'yyyy-MM-dd') date_id,
        data.create_time,
        data.source_id,
        data.source_type,
        data.sku_num,
        data.sku_num * data.order_price split_original_amount,
        data.split_total_amount,
        data.split_activity_amount,
        data.split_coupon_amount
    from ods_order_detail_inc
    where dt = '2020-06-15'
    and type = 'insert'
) od
left join
(
    select
        data.id,
        data.user_id,
        data.province_id
    from ods_order_info_inc
    where dt = '2020-06-15'
    and type = 'insert'
) oi
on od.order_id = oi.id
left join
(
    select
        data.order_detail_id,
        data.activity_id,
        data.activity_rule_id
    from ods_order_detail_activity_inc
    where dt = '2020-06-15'
    and type = 'insert'
) act
on od.id = act.order_detail_id
left join
(
    select
        data.order_detail_id,
        data.coupon_id
    from ods_order_detail_coupon_inc
    where dt = '2020-06-15'
    and type = 'insert'
) cou
on od.id = cou.order_detail_id
left join
(
    select
        dic_code,
        dic_name
    from ods_base_dic_full
    where dt='2020-06-15'
    and parent_code='24'
)dic
on od.source_type=dic.dic_code;


离线数仓(八)【DWD 层开发】(2)https://developer.aliyun.com/article/1532415

相关实践学习
AnalyticDB MySQL海量数据秒级分析体验
快速上手AnalyticDB MySQL,玩转SQL开发等功能!本教程介绍如何在AnalyticDB MySQL中,一键加载内置数据集,并基于自动生成的查询脚本,运行复杂查询语句,秒级生成查询结果。
阿里云云原生数据仓库AnalyticDB MySQL版 使用教程
云原生数据仓库AnalyticDB MySQL版是一种支持高并发低延时查询的新一代云原生数据仓库,高度兼容MySQL协议以及SQL:92、SQL:99、SQL:2003标准,可以对海量数据进行即时的多维分析透视和业务探索,快速构建企业云上数据仓库。 了解产品 https://www.aliyun.com/product/ApsaraDB/ads
相关文章
|
6月前
|
存储 数据采集 JavaScript
深入理解数仓开发(一)数据技术篇之日志采集
深入理解数仓开发(一)数据技术篇之日志采集
|
6月前
|
消息中间件 关系型数据库 Kafka
深入理解数仓开发(二)数据技术篇之数据同步
深入理解数仓开发(二)数据技术篇之数据同步
|
4月前
|
消息中间件 监控 关系型数据库
Serverless 应用的监控与调试问题之实时离线数仓一体化常用的解决方案有什么问题
Serverless 应用的监控与调试问题之实时离线数仓一体化常用的解决方案有什么问题
|
5月前
|
存储 DataWorks Java
DataWorks产品使用合集之开发离线数仓时,需要多个工作空间的情况有哪些
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
6月前
|
消息中间件 存储 Kafka
Flink 实时数仓(二)【ODS 层开发】
Flink 实时数仓(二)【ODS 层开发】
|
6月前
|
存储 消息中间件 NoSQL
Flink 实时数仓(一)【实时数仓&离线数仓对比】(2)
Flink 实时数仓(一)【实时数仓&离线数仓对比】
|
2月前
|
人工智能 自然语言处理 关系型数据库
阿里云云原生数据仓库 AnalyticDB PostgreSQL 版已完成和开源LLMOps平台Dify官方集成
近日,阿里云云原生数据仓库 AnalyticDB PostgreSQL 版已完成和开源LLMOps平台Dify官方集成。
|
2月前
|
人工智能 分布式计算 数据管理
阿里云位居 IDC MarketScape 中国实时湖仓评估领导者类别
国际数据公司( IDC )首次发布了《IDC MarketScape: 中国实时湖仓市场 2024 年厂商评估》,阿里云在首次报告发布即位居领导者类别。
|
2月前
|
SQL 分布式计算 数据挖掘
加速数据分析:阿里云Hologres在实时数仓中的应用实践
【10月更文挑战第9天】随着大数据技术的发展,企业对于数据处理和分析的需求日益增长。特别是在面对海量数据时,如何快速、准确地进行数据查询和分析成为了关键问题。阿里云Hologres作为一个高性能的实时交互式分析服务,为解决这些问题提供了强大的支持。本文将深入探讨Hologres的特点及其在实时数仓中的应用,并通过具体的代码示例来展示其实际应用。
247 0
|
3月前
|
存储 机器学习/深度学习 监控
阿里云 Hologres OLAP 解决方案评测
随着大数据时代的到来,企业面临着海量数据的挑战,如何高效地进行数据分析和决策变得尤为重要。阿里云推出的 Hologres OLAP(在线分析处理)解决方案,旨在为用户提供快速、高效的数据分析能力。本文将深入探讨 Hologres OLAP 的特点、优势以及应用场景,并针对方案的技术细节、部署指导、代码示例和数据分析需求进行评测。
139 7

热门文章

最新文章

下一篇
DataWorks