数仓如何建模
首先是 OLTP 与 OLAP联机事务处理(OLTP),简单来讲,例如 MySQL 这种关系型数据库就是 OLTP,主要用来服务于业务系统,执行基本的增删改查的操作联机分析处理(OLAP),侧重分析,可以快速处理复杂的分析操作OLAP 分为 MOLAP、ROLAP 和 HOLAPMOLAPMOLAP 更注重预计算,将分析得到的多维数据以物理存储的形式形成立方体的结构(CUBE)一个指标会包含:时间、事实、维度、度量这里需要补充一下事实和维度的概念❝数据仓库将数据分为事实和维度,事实数据就是用户具体的行为,维度数据就是描述事实的数据一条数据整体来看就是:某人在某时干了某事某人、某时、某事都是维度数据,干这件事的动作就是事实数据比如:下午两点,健鑫下单了一本《数据星河》这里下午两点、健鑫、《数据星河》就是维度数据下单这个动作就是事实数据或者换一种说法,如果用户没有这个行为,那么事实数据就不会存在,维度数据依然存在还是这个例子,就算我不下单,下午两点这个时间还在、我还在、这本书还在,但是下单这个动作不在言归正传,我们开发一个指标的时候怎么开发呢?比如我们现在来开发“销售额”这个指标,那我们需要知道:哪个商品的销售额?哪个时间区间的销售额?哪个地区的销售额?什么单位的销售额?那么我们就需要筛选对应时间区间的数据,然后对商品、地区进行聚合操作,最后求和,得到的东西就是 CUBE得到的这个 CUBE 会物理存储起来,读的时候可以直接返回现成的结果,不用再算一遍了可以看下面这个图片更好理解一点,这里操作分为“钻取”和“上卷”“钻取”就是由粗到细的过程,比如时间从季度变成了月份“上卷”就是由细变粗的过程,比如时间从月份变成季度,省份从浙江、上海、江苏变成了“江浙沪”此类 OLAP 的产品有 Kylin 和 DruidKylin 的优点在于支持的离线数据规模大,支持 SQL,性能高缺点在于没有二级索引,使用必须构建一个 CUBE,维护成本大❝这里没有二级索引怎么理解呢?在关系型数据库中除了主键索引之外,可以再任意一个列上建设多个“二级索引”用来加速WHERE、ORDER BY 等操作Kylin 是利用 HBase来存储 cube 数据的,使用 HBase 的 rowkey 来定位没有二级所以就代表这所有查询都只能走预先定义好的 rowkey(可以理解成主索引)如果查询中用到一个 rowkey 中没有包含的维度,那就需要进行全表扫描,或者扫描更大的cube,再或者重新构建一个包含这个维度的cube所以在涉及的时候需要考虑,哪些列会被 fliter、group by等操作,在设计之初就加到 rowkey 中
不过现在 Kylin 的默认存储已经是 Parquet 格式直接存储在 HDFS 中并且自己维护元数据,不需要依赖 HBase了,但是还是没有二级索引Druid 的优点同样是可以支持大规模数据,性能高 缺点在于维度不能随意组合,不支持复杂的 JOIN、SQL 支持很弱❝这里不能组合怎么理解?Druid 需要在最开始将哪些列能进行过滤、聚合操作都声明出来,查询的时候必须根据声明的字段做相应的操作除此之外 Druid 已经可以执行 JOIN 操作了,但是只能进行 INNER JOIN 和 LEFT JOIN 同时必须是等值连接,被JOIN 的维度表需要完全能加载到内存当中所以 Druid 能进行一些简单的 join 场景,需要复杂的 join 操作还是需要在数据抽取之前提前进行ROLAPROLAP 不进行预计算代表产品有 clickhouse其优点有:列式存储、支持本地存储、具有单机版高性能缺点有:不支持标准的 SQL 语法,JOIN 支持的不好,维护成本高除此之外还有不存储数据,用来即席查询的,比如:Presto(Trino)、Impala这种 OLAP 不会存储数据,是用来查数据的,支持标准的 SQL,写起来其实跟 Spark 没啥区别,就是查得快但是如果复杂度过高并且数据量大的话,可能会提供分钟级相应HOLAP小孩子才做选择,HOLAP表示:我全都要代表产品:Doris、Starrocks支持高并发场景、秒级 / 毫秒级查询兼容 MySQL 协议和标准 SQL同时支持明细和聚合查询有较好的 Join 策略再具体的特性就不在这里谈了最后咱们来说一下 OLTP 和 OLAP 的区别首先他们的使用场景就不一样,OLTP 是专注事务处理的,服务于业务系统,OLAP 是专注于分析场景的然后 OLTP 对时效性的要求是要高于 OLAP 的,因为咱们在网页或者 APP 上操作时肯定是要立即响应的,然后大数据这边不管实时性做的多么好都是近实时,不能完全算实时,数据从系统那边过来肯定是会有延迟的,所以我们平时开发的时候,关注的是能接受多少的延迟,而不是笼统的实时OLTP 会对数据进行频繁的更新,OLAP 这边是一次写入多次读出的场景OLTP 只会存储最近的数据,OLAP 会存储大量的历史数据OLTP 以业务流程作为参考,OLAP 是面向分析的,所以会以业务主题作为参考接下来是数仓分层到这里需要对原书的结构进行一些更改,有些章节需要提前来说,因为不说就存在某些概念就没有提前讲解到所以在介绍数仓分层之前,咱们先来了解一下数据模型建设五要素数据建模五要素为数据域/主题域、事实表类型、颗粒度、维度、度量值数据域 / 主题域数仓分层可以说成是对数据处理流程进行拆解,将数据处理逻辑进行纵向划分数据域或者主题域就是在每一个层级对数据按照业务流程进行拆解,将数据进行横向划分在ODS层、DWD层、DWM层、DWS层当中,数据都是按照业务流程进行划分的,叫做数据域在ADS层中,数据是按照业务分析场景进行划分的,叫做主题域。比如金融场景,按照金融产品业务的生命周期进行拆分:贷前(准入、授信等)、贷中(支用、还款等)、贷后(催收等),这样划分的环节就是数据域如果决策者想要从更高的角度去看整个业务流程,比如风控、营销活动分析,这样划分的就是主题域事实表类型数仓将事实表分成三个类型,事务性事实表、周期性快照事实表和累积性快照事实表事务性事实表中记录的是用户的每一个行为,用户每产生一个行为,事务性事实表就会产生一条数据;这里分为单事务事实表和多事务事实表,单事务是表中只有一个业务过程,多事务会有多个业务过程周期性快照事实表是按照固定的时间周期对数据进行划分,比如:每天用户的下单金额累积性快照事实表中记录的是历史至今所有的累积数据,比如:用户的累计下单金额,不过累积性快照一般累计的是状态,比如一个订单会经历下单 -> 发货 -> 收货,这个过程会持续好几天,一天下单了,那么这行数据下单时间有了,发货和收货时间为 NULL,第二天发货了,那么发货的字段就不为 NULL 了,就这样会持续更新,每个对象只会保留一条数据在进行建模的时候需要根据不同的场景选择不同的事实表进行建模。颗粒度颗粒度代表着一个表的精细化程度如果商品类型可以唯一定位到一行数据,那么这个表就是商品类型粒度的如果商品类型+品牌可以唯一定位到一行数据,那么这个表就是类型+品牌这一粒度的维度维度是描述事实的数据在具体分析的时候,维度可以提供多个视角进行决策 比如:分析一个商品是否是畅销商品可以从销售金额、价格、销量、时间、品牌等多个维度进行分析 所以,在建模的时候需要考量在分析的时候可能用到哪些维度,提前将这些维度关联到表中,从而提高分析的效率。度量值度量值决定了该场景下数值的数据记录,建模的时候需要将同一场景下的数值单位统一,防止出现不必要的数据错误问题。了解完五要素,我们现在可以进入到数仓分层的内容当中了分层是数据仓库内部对数据处理的一套规范,在这里可以和 Java 后端的分层架构进行类比Java 后端一般被分为 Controller 实现层、Service 业务层、Dao 持久层和 Entity 实体层对于数仓而言,分层并不固定,常见的会分为五层, 分别为:数据源接入层(ODS 层)数据仓库层(DWD、DWM、DWS),部分公司会直接统称为 CDM 层数据应用层(ADS 层)这些层级具体如下:数据接入层ODS(Operational Data Store),用于对源头的数据进行接入,比如前端埋点、后端数据库等,在该层中只保持数据原始的状态,不会对数据进行任何处理。作用是将数据导入到数据仓库当中。数据明细层DWD(Data Warehouse Detail),首先需要明确对ODS的数据进行关联、清洗和维度退化,因为传过来的数据可能会存在脏数据,所以需要进行一些清洗;维度退化是将一些维度表中的维度数据关联进来,目的是提前 Join,下游使用相关数据的时候就可以直接用了;也会包含划分数据域,根据场景细分颗粒度的操作数据中间层DWM(Data Warehouse Middle),DWM 是作为 DWD 和 DWS 的过渡层,DWS 层主要是做一些指标计算,但是有一些指标比较复杂,不能直接计算出,比如计算 xx 率,就可以进行一些轻度汇总,比如先把“和”求出来,从而提高复用性,减少重复开发数据服务层DWS(Data Warehouse Service), DWS 和 DWD 层的数据域划分是一致的,这里要进行更细颗粒度的划分,然后对于 DWD 层的明细数据按照不同的粒度进行汇总,更多是周期性汇总,比如近30天、60天,最重要的是完成口径的统一数据应用层ADS(Application Data Service), 按照主题域、颗粒度进行划分,将 DWD - DWS 的数据进行组合,针对专门的一个场景进行建设,最终形成用户画像、专题分析应用知道分层怎么分的之后,咱们回头来了解一下数仓为什么要分层分层其实就是将一个复杂的逻辑拆成多个步骤去做,每层完成固定的事情,这样就减少了开发难度同时,因为每层完成固定的事情,排查问题的时候也更好排查,比如我们发现一个指标数据有问题,那我们就可以直接去 DWD 层查明细数据看拆成多个步骤之后也减少了重复开发,提高了数据的通用性,因为开发一次就可以被反复用了所以资源消耗也会减少,下游任务都用一个通用表,指标口径也会统一这块不仅是为了数仓的产出以及开发效率,也是让整个数仓变得更加好用。因为数仓中的表不仅是数仓开发在用的,数分、业务也可能会用基础数据看一些指标,这时候如果让他们直接用 ODS 原始层的数据那用数体验就不是很好接下来介绍一下数仓的模型这里分为业务模型、概念模型和物理模型业务模型说人话就是个表,根据业务的角度进行建模就是规划一下这个表里都有啥字段,这种一般是根据业务流程来划分概念模型说人话就是描述各个实体之间的关系的,比如:E-R图物理模型说人话就是把定义的表实际存储到磁盘了,维护一下元数据,比如在 HDFS 看到的就是物理模型然后就是数仓的建模了数据仓库是服务于分析场景的,和后端的场景不同,后端是服务于业务系统的。在后端中,由于业务系统需要频繁的增删改查,后端使用的关系型数据库的特性以及建模思想都是服务于这种场景的,所以后端一般采用的都是三范式建模范式建模的核心目标在于消除冗余、避免更新异常,保证的是相同信息只在一处存储,插入、更新和删除操作就不会因为多处存储而不一致从性能的角度考虑,第三范式会将原本可能集中在一张大表里的字段拆分到多张子表中,进行写操作的时候可以避免多个冗余字段进行更新,从而减少磁盘写入量和锁竞争,这样在 OLTP 的场景下可以有更稳定的写入性能但是每次跨表查询都需要通过 JOIN 进行关联,JOIN 所带来的开销会远超过单表扫描带来的开销,所以从查询速度的角度来看,三范式会拖慢 OLAP 场景下的效率数据仓库当中,数据是服务于分析场景的,数据是一次写入多次读出的,也就是说只需要写入一次数据,这个表的数据就会被频繁的去读,基本不存在更改的操作在大数据中,JOIN 一般就有 shuffle,而 shuffle 一般就是一个大数据任务的性能瓶颈。所以三范式建模中减少冗余的思想就不适用于大数据场景中,因此,数据仓库中的建模是维度建模维度建模将大表拆分成中心事实表+多张维度表维度表一般规模有限,可以当作小表来处理,JOIN 代价可控维度建模包含星型模型、雪花模型、星座模型星型模型:一个事实表关联多个维度表,为的就是提前将分析可能用到的维度进行关联,因为数据的冗余,减少了 JOIN雪花模型:一个事实表关联多个维度表,同时维度表还会再关联其他的维度表,减少了维度表的冗余,但是降低了效率,实际场景中,使用星型模型更多星座模型:在一个体系化的数据仓库当中,一个维度表往往不可能只被一个事实表所引用,而是会被多个事实表所关联,多个星型模型就会存在维表重合,这样产生的模型就叫做星座模型。通常在数据仓库后期自然演进中型形成接下来我们来谈建模的具体流程首先是模型设计的基本原则高内聚低耦合说人话就是让一个模块只做一件事,只需要做好这一件事(高内聚),模块和模块之间减少依赖,互不打扰(低耦合)比如说将所有和做饭有关的东西都放到厨房,不要散落在客厅,这样可以让做饭的流程更加顺畅,这就是高内聚厨房只需要做饭,不要管客厅的沙发,这就是低耦合 落到模型设计这边就是,将业务相近和粒度相同的数据设计成一个模型,将高概率同时访问的数据放到一起,将低概率同时访问的数据分开存储公共逻辑下沉就是将通用的逻辑放到底层,让其更加通用比如说商品销售额这类指标,ADS 层的很多表都要用,那就可以将这类指标放到 DWS 层,然后 ADS 的表直接读 DWS 加工好的指标就可以了数据可回滚可以回刷数据,得保证这个幂等性不能说我回刷一次数据就不对了规范一致性所有的模型都用一套规范可扩展性后续支持新增字段成本与性能的平衡尽量减少存储和计算中的资源消耗说了这么多模型设计,那模型设计具体是个什么流程呢?1、需求调研首先我们需要了解这个业务的流程环节,并要知道业务方对这块的诉求,比如都要搞什么指标,指标的口径要统一,口径就是指标的计算逻辑2、数据域制定将整体的业务流程进行划分,完成数据域的指定3、总线矩阵明确业务过程和维度之间的关系,这东西叫总线矩阵,找了个参考图片如下:4、明确模型建设五要素五要素是啥咱们已经在介绍数仓分层的时候讲过了,这里就不提及了5、模型设计从下到上进行建模,DWD -> ADS6、代码开发根据业务需求完成代码开发,开发完成会有保证数据质量的操作,这部分咱们写到数据质量的部分会提及7、任务部署 / 数据初始化书中的下一下是数据域与主题域,这部分咱们同样在介绍模型五要素的时候讲过了,所以就跳过了然后关于事实表咱同样在介绍模型五要素的时候讲过了,所以咱们直接来到全量和增量的概念全量(full,缩写为f),就是一个表的分区包含历史所有的数据增量(increment,缩写为i),每天处理新增数据,每个分区不再存储所有的数据了df:日全量,每天处理历史至今所有的数据di:日增量,每天处理前一天新增的数据hf:小时全量,每小时处理历史至今所有的数据hi:小时增量,每小时处理前一个小时新增的数据接下来是拉链表的内容,拉链表一般被用在缓慢变化维的场景中,那什么是缓慢变化维呢?我们会将分析的各种角度存储在维度表当中,但是维度数据是会发生变化的,而且时间跨度非常久比如部门的变更,一个员工最初是在部门a工作,后面由于一些原因转到了部门b这是缓慢变化维的一种可能这种维度变化,业务系统往往不会保存历史数据,但是站在分析的角度上,我们要保留这种变化的痕迹拉链表可以记录一个事物从开始到现在状态所有变化的信息拉链表制作的过程如下:接下来介绍下数据标准,这部分内容可以参考,每个地方不一样表命名表命名的合理性决定了维护的难易程度,从ODS层的命名需要知道这个数据是来源于后端哪个表的,所以需要将业务数据库表名加上。DWD层是在ODS层的基础上进行了ETL处理,以及划分了数据域,所以需要区分到最细的数据域DWS层涉及到聚合操作,所以需要标注颗粒度和时间周期ADS层对应具体分析场景,所以需要标明主题域ODS层:ods_{业务数据库名}{业务数据表名}存储策略(df/di)DWD层:dwd {一级数据域}{二级数据域}业务过程(不清楚或没有写detail)}存储策略(df/di)DWS层:dws(例如买家/卖家){业务过程}{周期)(例如近30天写30d、90天写3m)ADS层:ads(例如买家/卖家){业务过程}dim表(维度表)DIM层:dim_存储策略(df/di)(例如日期写date)存储策略i: increment,代表增量f: full,代表全量df:日全量,每天处理历史至今所有的数据di:日增量,每天处理前一天新增的数据hf:小时全量,每小时处理历史至今所有的数据hi:小时增量,每小时处理前一个小时新增的数据统计周期DWS层需要进行聚合运算,需要标明时间周期,规范如下:1d:近1天指标统计1m:近1个月指标统计1y:近1年指标统计3m:近3个月指标统计6m:近6个月指标统计字段类型文本:String日期:String整数:bigint小数:高精度用demal,正常使用double枚举值:单枚举用bigint,多枚举用String各类id:String模型分区生命周期(1)全量表ODS:小时全量(3天)、天级全量(790天)。DWD:小时全量(715天)、天级全量(730天)。DWS:小时全量(730天)、天级全量(760天)。ADS:小时全量(730天)、天级全量(790天)。DIM:小时全量(3天)、天级全量(90360天)。(2)增量表ODS:小时增量(30180天)、天级增量(90365天 或者 永久)。DWD:小时增量(60180天)、天级增量(180365天)。DWS:小时增量(60365天)、天级增量(180730天)。ADS:小时增量(90365天)、天级增量(180730天)。DIM :默认使用全量表。数仓在建设过程当中可以分成三个周期探索期业务变化快、数据需求杂、数据模型多次返工,这个时候以建设 DWD 为主,ADS 为辅发展期业务处于建设阶段,数据需求逐渐成体系,以建设 DWD 为辅,以 DWS、ADS 为主,建设核心应用场景成熟期数据模型趋于稳定,数仓和业务合作密切,DWD 层丰富数据模型内容,DWS 丰富颗粒度和指标,ADS 用于更深层次的专题分析治理期业务迭代变缓慢,对数仓进行治理,将不再使用的数据模型及时下线,或者进行其他优化数仓的治理操作书中还介绍了一种新式的分层方法,解决了 DWD 维度冗余、维度变化、事实拆分过细导致数据模型难找以及难以维护的问题这里对 DWD 、DWM 和 APP(ADS 换了个名) 有了调整DWD 层不做维度退化了,只做数据清洗,code 和 name 进行转化后端为了性能的考虑会将一些字段设计成枚举值,比如0代表未付款,1代表已付款。但是对于数仓的分析场景来说,这种枚举值没有任何的意义,需要将枚举值转换成具体的中文含义才能进行分析DWM 这里做统一的维度退化,保障了 DWD 事实表不再冗余,DWM 会包含数据域中所有流程,方便后续的 DWS 、APP 访问,下游引用也不用关联多个 DWD 了 从 DWM 开始就可以为产品、数分等提供查询服务了,可以让业务从一张表就可以直接看到整个流程的内容,满足了咱们前文提到的:提高用数体验APP 形成大宽表和小宽表,小宽表尽可能服务于一次性使用的看板和专题分析,大宽表可以支持多块业务

浙公网安备 33010602011771号