数仓设计--维度(全量、拉链表)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数仓设计--维度(全量、拉链表)相关的知识,希望对你有一定的参考价值。

参考技术A 维度属性通常不是静态的,而是会随时间变化的,数据仓库的一个重要特点就是反映历史的变化,所以如何保存维度的历史状态是维度设计的重要工作之一。保存维度数据的历史状态,通常有以下两种做法,分别是全量快照表和拉链表

1 )全量快照表

离线数据仓库的计算周期通常为每天一次,所以可以每天保存一份全量的维度数据。这种方式的优点和缺点都很明显

优点是简单而有效,开发和维护成本低,且方便理解和使用。

缺点是浪费存储空间,尤其是当数据的变化比例比较低时。

2 )拉链表

拉链表的意义就在于能够更加高效的保存维度信息的历史状态。

拉链表是记录每条信息的生命周期,一旦一条记录的生命周期结束,就重新开始一条新的记录,并把当期日期放入生效开始日期,如果当前信息至今有效,再生效结束日期种填入一个极大值

(1)什么是拉链表

拉链表适合于:数据发生变化,但是变化频率并不高的维度(即:缓慢变化维)

比如:用户信息会发生变化,但是每天变化的比例不高。如果数据量有一定规模,按照每日全量的方式保存效率很低。比如:1亿用户*365天,每天一份用户信息(做每日全量效率低)

通过,生效开始日期<=某个日期且生效结束日期>=某个日期

如果事实表中一条记录在某个维度表中有多条记录与之对应,称为多值维度。例如,下单事实表中的一条记录为一个订单,一个订单可能包含多个商品,所会商品维度表中就可能有多条数据与之对应。

针对这种情况,通常采用以下两种方案解决。

第一种:降低事实表的粒度,例如将订单事实表的粒度由一个订单降低为一个订单中的一个商品项。

第二种:在事实表中采用多字段保存多个维度值,每个字段保存一个维度id。这种方案只适用于多值维度个数固定的情况。

建议尽量采用第一种方案解决多值维度问题

维表中的某个属性同时有多个值,称之为“多值属性”,例如商品维度的平台属性和销售属性,每个商品均有多个属性值。

针对这种情况,通常有可以采用以下两种方案。

第一种:将多值属性放到一个字段,该字段内容为key1:value1,key2:value2的形式,例如一个手机商品的平台属性值为“品牌:华为,系统:鸿蒙,CPU:麒麟990”。

第二种:将多值属性放到多个字段,每个字段对应一个属性。这种方案只适用于多值属性个数固定的情况

大数据面试题--数仓

目录

数据仓库理论

数据分层

E T L

星形模型与雪花模型的区别?

维度建模(dimensional modeling):

数据仓库项目最重要或需要注意的是什么,以及如何处理?

关系建模与维度建模

拉链表:

维度表和事实表

数据仓库中的维度和粒度

维度建模步骤:

如何构建数据仓库?

Flink面试题

Flink的重启策略:

Flink集群规模

Flink 集群有哪些角色?各自有什么作用?

TODO说说 Flink 资源管理中 Task Slot 的概念

说说 Flink 的常用算子?

TODO说说你知道的 Flink 分区策略?

Flink 的并行度了解吗?Flink 的并行度设置是怎样的?

Flink 的 Slot 和 parallelism 有什么区别?

说说 Flink 中的广播变量,使用时需要注意什么?

说说 Flink 中的状态存储?

TODOFlink 是如何做到高效的数据交换的?

Flink 是如何做容错的?

Flink 分布式快照的原理是什么?

TODOFlink 是如何保证 Exactly-once 语义的?

说说 Flink 的内存管理是如何做的?

说说 Flink 的序列化如何做的?

Flink 中的 Window 出现了数据倾斜,你有什么解决办法?

Flink 中在使用聚合函数 GroupBy、Distinct、KeyBy 等函数时出现数据热点该如何解决?

Flink 任务延迟高,想解决这个问题,你会如何入手?

Flink的状态机制?

容错和checkpoint机制?

Windows的触发条件?

对于乱序数据和迟到数据Flink是怎么处理的?

watermark 机制?

Window的类型?

Flink中在使用聚合函数 GroupBy、Distinct、KeyBy 等函数时出现数据热点该如何解决?

公司怎么提交的实时任务?有多少个jobmanager?

Flink的时间语义?

窗口函数?

Flink的执行图?

Flink 的反压和 Strom 有哪些不同?

TODOFlink如何定位反压?

Checkpoint和Savepoint对比:

Savepoint机制?

Flink调优

Taskmanager挂掉排查思路?

两阶段聚合解决 KeyBy 热点?

Hive面试题

TODOhive如何优化?

TODOHive的数据倾斜问题:

hive、hbase的小文件如何处理?

hive 内部表和外部表的区别?

hive 是如何实现分区的?

hive有哪些方式保存元数据?

hive 中的压缩格式 RCFile、 TextFile、 SequenceFile 各有什么区别?

row_number(),rank(),dense_rank()区别?

hive的开窗函数有哪些?

Over函数?

自定义过的udf函数,简述定义步骤

自定义函数分类?

Hive 分区表与分桶表区别

Hive导入数据的五种方式

Hive导出数据的五种方式

Order By,Sort By,Distrbute By,Cluster By的区别

介绍下平时常用的函数?

列举8个常用的配置参数

 hive架构:

驱动器:Driver

Hive与Mysql的区别?

说说对Hive桶表的理解?

hbase面试题

HBase中的数据为何不直接存放于HDFS之上?

文件能否直接存储于HBase里面?

介绍HBase数据模型:

基于数据模型介绍HBase的适用场景

快速介绍集群关键角色以及集群部署建议

HBase 适用于怎样的情景?

HBase 特性:

HBase写入数据流程:

HBase读流程

HBase文件合并StoreFile Compaction :

HBase优化:

rowkey的设计原则?

Spark面试题

spark提交作业的参数?

简述Spark的宽窄依赖,以及Spark如何划分stage,每个stage又根据什么决定task个数?

Repartition和Coalesce 的关系与区别,能简单说说吗?

Spark中的缓存(cache和persist)与checkpoint机制,并指出两者的区别和联系

Spark中共享变量(广播变量和累加器)的基本原理与用途

Spark调优?

Spark 中的数据倾斜问题?

Spark有哪些聚合类的算子,我们应该尽量避免什么类型的算子?

Spark作业提交流程是怎么样的?

reduceByKey与groupByKey的区别,哪一种更具优势?

如何使用Spark实现TopN的获取(描述思路或使用伪代码)?


数据仓库理论

数据分层

1)数据明细层:DWD(Data Warehouse Detail):

该层一般保持和ODS层一样的数据粒度,并且提供一定的数据质量保证,同时为了提高数据明细层的易用性,该层会采用一些维度退化手法,将维度退化到事实表中,减少事实表和维度表的关联.另外在该层也会做一部分的数据聚合,将相同主题的数据汇集到一张表中,提高数据的可用性

2)数据中间层DWM(Data Warehouse Middle):

在DWD层的数据基础上,对数据做轻度的聚合操作,生成一系列的中间表提升公共指标的复用性,减少重复加工,直观来说,就是对通用的核心维度进行聚合操作,算出相应的统计指标

3)数据服务层:DWS(Data Warehouse Service)

又称为数据集市或者宽表,按照业务划分,例如流量,订单,用户等,生成字段比较多的宽表,用于后续的业务查询,OLAP分析,数据分析等

E T L

extract/transformation/load寻找数据,整合数据,并将它们装入数据仓库的过程。

ETL是将业务系统的数据经过抽取、清洗转换之后加载到数据仓库的过程,目的是将企业中的分散、零乱、标准不统一的数据整合到一起,为企业的决策提供分析的依据。

一.抽取

方法有三种:1.利用工具,例如datastage,informatic,OWB,DTS,SISS. 2,利用存储过程. 3,前两种工具结合.

抽取前的调研准备工作:1.弄清数据是从哪几个业务系统中来,各个业务系统的数据库服务器运行什么DBMS. 2.是否存在手工数据,手工数据量有多大。3.是否存在非结构化的数据。

二.清洗与转换

清洗

数据清洗的任务是过滤那些不符合要求的数据,将过滤的结果交给业务主管部门,确认是否过滤掉还是由业务单位修正之后再进行抽取。

清洗的数据种类: 1,不完整数据,2,错误数据,3重复的数据.

转换

1.不一致数据转换:编码转换(m,f;男女);字段转换(balance,bal);度量单位的转换(cm,m)

2.数据粒度的转换;业务系统数据存储非常明细的数据,而数据仓库中数据是用分析的,不需要非常明细,会将业务系统数据按照数据仓库粒度进行聚合.

3.商务规则的计算.不同企业有不同的业务规则,不同的数据指标,在ETL过程,将这些数据计算好之后存储在数据仓库中,供分析使用(比如KPI)

三.加载经过前两步处理后的数据可直接加载入数据仓库

用过什么ETL工具(informatica,ssis,owb,datastage),以及该工具简单讲述特点。

DataStage是一套专门对多种操作数据源的数据抽取、转换和维护过程进行简化和自动化,并将其输入数据集市或数据仓库目标数据库的集成工具。

它有四个组件:Administrator:用来管理project和环境变量。Manager:用于job,表定义,的引导,引出。Designer:用来设计job。Direct:用运查看job运行日志。

星形模型与雪花模型的区别?

1.星星的中心是一个大的事实表,发散出来的是维度表,每一个维度表用一个PK-FK连接到事实表,维度表之间彼此并不关联。一个事实表又包括一些度量值和维度。

2.雪花模型通过规范维度表来减少冗余度,也就是说,维度表数据已经被分组成一个个的表而不是使用一个大表。例如产品表被分成了产品大类和产品小类两个表。尽管这样做可以节省了空间,但是却增加了维度表的数量和关联的外键的个数。这就导致了更复杂的查询并降低了数据库的效率

维度建模(dimensional modeling):

是数据仓库建设中的一种数据建模方法。按照事实表,维表来构建数据仓库,数据集市。这种方法最被人广泛知晓的名字就是星型模式(Star-schema)。

什么叫查找表,为什么使用替代键?(其实目的和上面一样,从基础表到缓慢维度表的过程中的一种实现途径)

替代键(alternate key)可以是数据表内不作为主键的其他任何列,只要该键对该数据表唯一即可。换句话说,在唯一列内不允许出现数据重复的现象。

数据仓库项目最重要或需要注意的是什么,以及如何处理?

数据质量,主要是数据源数据质量分析,数据清洗转换,当然也可以定量分析

数据仓库有两个重要目的,一是数据集成,二是服务BI 

数据准确性是数据仓库的基本要求,而效率是项目事实的前提,数据质量、运行效率和扩展性是数据仓库项目设计、实施高明与否的三大标志;

关系建模与维度建模

关系建模将复杂的数据抽象为两个概念——实体和关系,并使用规范化的方式表示出来。

关系模型严格遵循第三范式(3NF),数据冗余程度低,数据的一致性容易得到保证。由于数据分布于众多的表中,查询会相对复杂,在大数据的场景下,查询效率相对较低。

维度模型以数据分析作为出发点,不遵循三范式,故数据存在一定的冗余。维度模型面向业务,将业务用事实表和维度表呈现出来。表结构简单,故查询简单,查询效率较高。

拉链表:

什么是拉链表?

记录每条信息的生命周期,一旦一条记录的生命周期结束,就重新开始一条新的记录,并把当前日期放入生效开始日期。

拉链表适合于:数据会发生变化,但是变化频率并不高的维度(缓慢变化维)

如何使用拉链表?两种使用场景

  1. 获取全量最新的数据:结束日期=’9999-99-99’
  2. 获取全量历史数据:

通过生效开始日期<=某个日期 且 生效结束日期 >=某个日期  ,能够得到某个时间点的数据全量切片。

维度表和事实表

维度表:一般是对事实的描述信息。每一张维表对应现实世界中的一个对象或者概念。    例如:用户、商品、日期、地区等。

维表的特征:

Ø维表的范围很宽(具有多个属性、列比较多)

Ø跟事实表相比,行数相对较小:通常< 10万条

Ø内容相对固定:编码表

事实表中的每行数据代表一个业务事件(下单、支付、退款、评价等)。“事实”这个术语表示的是业务事件的度量值(可统计次数、个数、金额等)

每一个事实表的行包括:具有可加性的数值型的度量值、与维表相连接的外键,通常具有两个和两个以上的外键。

事实表的特征:

Ø非常的大

Ø内容相对的窄:列数较少(主要是外键id和度量值)

Ø经常发生变化,每天会新增加很多。

数据仓库中的维度和粒度

从时间的角度讲:简单说粒度就是事实表里测量值的测量‘频率’。比如说,销售库里的销售额,可以是一 天一个值,也可以是一个月一个值,甚至一年一个值,这就是相对于时间维度表的力度;可以是一个商品一个值,也可以是一类商品一个值,这就是相对于商品的粒度。

维度建模步骤:

选择业务过程→声明粒度→确认维度→确认事实

(1)选择业务过程

在业务系统中,挑选我们感兴趣的业务线,比如下单业务,支付业务,退款业务,物流业务,一条业务线对应一张事实表。

(2)声明粒度

数据粒度指数据仓库的数据中保存数据的细化程度或综合程度的级别。

声明粒度意味着精确定义事实表中的一行数据表示什么,应该尽可能选择最小粒度,以此来应各种各样的需求。

典型的粒度声明如下:

订单事实表中一行数据表示的是一个订单中的一个商品项。

支付事实表中一行数据表示的是一个支付记录。

(3)确定维度

维度的主要作用是描述业务是事实,主要表示的是“谁,何处,何时”等信息。

确定维度的原则是:后续需求中是否要分析相关维度的指标。例如,需要统计,什么时间下的订单多,哪个地区下的订单多,哪个用户下的订单多。需要确定的维度就包括:时间维度、地区维度、用户维度。

(4)确定事实

此处的“事实”一词,指的是业务中的度量值(次数、个数、件数、金额,可以进行累加),例如订单金额、下单次数等。

在DWD层,以业务过程为建模驱动,基于每个具体业务过程的特点,构建最细粒度的明细层事实表。事实表可做适当的宽表化处理。

事实表和维度表的关联比较灵活,但是为了应对更复杂的业务需求,可以将能关联上的表尽量关联上。

如何构建数据仓库?

1.系统分析,确定主题。通过与业务部门的交流,了解建立数仓要解决的问题,确认各个主题下的查询分析要求

2.选择满足数据仓库系统要求的软件平台。选择合适的软件平台,包括数据库、建模工具、分析工具等

3.建立数据仓库的逻辑模型。确定建立数据仓库逻辑模型的基本方法,基于主题视图,把主题视图中的数据定义转到逻辑数据模型中

4.逻辑数据模型转换为数据仓库数据模型

5.数据仓库数据模型优化。随着需求和数据量的变化进行调整

6.数据清洗转换和传输。业务系统中的数据加载到数据仓库之前,必须进行数据的清洗和转换,保证数据仓库中数据的一致性。

7.开发数据仓库的分析应用。满足业务部门对数据进行分析的需求。

8.数据仓库的管理。包括数据库管理和元数据管理。

Flink面试题

Flink的重启策略:

重启策略分为:固定延迟重启策略、故障率重启策略、无重启策略、后备重启策略。

1.固定延迟重启策略:

// 5表示最大重试次数为5次,10s为延迟时间

RestartStrategies.fixedDelayRestart(5,Time.of(10, TimeUnit.SECONDS))

2.故障率重启策略

// 3为最大失败次数;5min为测量的故障时间;10s为2次间的延迟时间

RestartStrategies.failureRateRestart(3,Time.of(5, TimeUnit.MINUTES),Time.of(10, TimeUnit.SECONDS))

3.无重启策略

RestartStrategies.noRestart()

  1. 后备(Fallback)重启策略

Flink集群规模

Flink 可以支持多少节点的集群规模?在回答这个问题时候,可以将自己生产环节中

的集群规模、节点、内存情况说明,同时说明部署模式(一般是 Flink on Yarn),除此之外,

用户也可以同时在小集群(少于 5 个节点)和拥有 TB 级别状态的上千个节点上运行 Flink

任务。

Flink 集群有哪些角色?各自有什么作用?

Flink 程序在运行时主要有 TaskManager,JobManager,Client 三种角色。

JobManager集群中的管理者 Master 的角色,是整个集群的协调者,负责接收 Flink  Job,协调检查点,Failover 故障恢复等,同时管理 Flink 集群中从节点 TaskManager。

TaskManager实际负责执行计算的 Worker,在其上执行 Flink Job 的一组 Task,每个TaskManager 负责管理其所在节点上的资源信息,如内存、磁盘、网络,在启动的时候将资源的状态向 JobManager 汇报。

Client:Flink 程序提交的客户端,当用户提交一个 Flink 程序时,会首先创建一个 Client,该 Client 首先会对用户提交的 Flink 程序进行预处理,并提交到 Flink 集群中处理,所以 Client 需要从用户提交的 Flink 程序配置中获取 JobManager 的地址,并建立到 JobManager 的连接,将 Flink Job 提交给 JobManager。

TODO说说 Flink 资源管理中 Task Slot 的概念

在Flink架构角色中我们提到,TaskManager是实际负责执行计算的Worker,TaskManager 是一个 JVM 进程,并会以独立的线程来执行一个 task 或多个 subtask。为了控制一个TaskManager 能接受多少个 task,Flink 提出了 Task Slot 的概念。简单的说,TaskManager会将自己节点上管理的资源分为不同的 Slot:固定大小的资源子集。这样就避免了不同 Job的 Task 互相竞争内存资源,但是需要主要的是,Slot 只会做内存的隔离。没有做 CPU 的隔离。

说说 Flink 的常用算子?

Flink 最常用的常用算子包括:Map:DataStream → DataStream,输入一个参数产生一个参数,map 的功能是对输入的参数进行转换操作。Filter:过滤掉指定条件的数据。KeyBy:按照指定的 key 进行分组。Reduce:用来进行结果汇总合并。Window:窗口函数,根据某些特性将每个 key 的数据进行分组(例如:在 5s 内到达的数据)

TODO说说你知道的 Flink 分区策略?

分区策略是用来决定数据如何发送至下游。目前 Flink 支持了 8 中分区策略的实现。

GlobalPartitioner 数据会被分发到下游算子的第一个实例中进行处理。ShufflePartitioner 数据会被随机分发到下游算子的每一个实例中进行处理。RebalancePartitioner 数据会被循环发送到下游的每一个实例中进行处理。RescalePartitioner 这种分区器会根据上下游算子的并行度,循环的方式输出到下游算子的每个实例。这里有点难以理解,假设上游并行度为 2,编号为 A 和 B。下游并行度为 4,编号为 1,2,3,4。那么 A 则把数据循环发送给 1 和 2,B 则把数据循环发送给 3 和 4。假设上游并行度为 4,编号为 A,B,C,D。下游并行度为 2,编号为 1,2。那么 A 和 B 则把数据发送给 1,C 和 D 则把数据发送给 2。BroadcastPartitioner 广播分区会将上游数据输出到下 游 算 子 的 每 个 实 例 中 。 适 合 于 大 数 据 集 和 小 数 据 集 做 Jion 的 场 景 。ForwardPartitioner ForwardPartitioner 用于将记录输出到下游本地的算子实例。它要求上下游算子并行度一样。简单的说, ForwardPartitioner 用 来 做 数 据 的 控 制 台 打 印 。KeyGroupStreamPartitioner Hash 分区器。会将数据按 Key 的 Hash 值输出到下游算子实例中。CustomPartitionerWrapper 用户自定义分区器。需要用户自己实现 Partitioner 接口,来定义自己的分区逻辑。

Flink 的并行度了解吗?Flink 的并行度设置是怎样的?

Flink 中的任务被分为多个并行任务来执行,其中每个并行的实例处理一部分数据。这

些并行实例的数量被称为并行度。我们在实际生产环境中可以从四个不同层面设置并行度:

操作算子层面(Operator Level)

执行环境层面(Execution Environment Level)

客户端层面(Client Level)

系统层面(System Level)

需要注意的优先级:算子层面>环境层面>客户端层面>系统层面。

Flink 的 Slot 和 parallelism 有什么区别?

slot 是指 taskmanager 的并发执行能力,假设我们将 taskmanager.numberOfTaskSlots 配

置为3 那么每一个 taskmanager 中分配3个 TaskSlot, 3个 taskmanager 一共有9个TaskSlot。

parallelism 是指 taskmanager 实际使用的并发能力。假设我们把 parallelism.default 设置

为 1,那么 9 个 TaskSlot 只能用 1 个,有 8 个空闲。

说说 Flink 中的广播变量,使用时需要注意什么?

Broadcast是一份存储在TaskManager内存中的只读的缓存数据.

使用场景:在执行job的过程中需要反复使用的数据,为了达到数据共享,减少运行时内存消耗,我们就用广播变量进行广播。

注意点:

1、广播变量中封装的数据集大小要适宜,太大,容易造成OOM

2、广播变量中封装的数据要求能够序列化,否则不能在集群中进行传输

说说 Flink 中的状态存储?

Flink 在做计算的过程中经常需要存储中间状态,来避免数据丢失和状态恢复。选择的

状态存储策略不同,会影响状态持久化如何和 checkpoint 交互。Flink 提供了三种状态存储

方式:MemoryStateBackend、FsStateBackend、RocksDBStateBackend。

MemoryStateBackend:内存级的状态后端,会将键控状态作为内存中的对象进行管理,将它们存储在taskManager的JVM堆上,而将checkpoint存储在jobManager的内存中。

特点:快速,低延迟,不稳定

fsStateBackend:将checkpoint存到远程的持久化文件系统中,而对本地状态跟memoryStateBackend一样,也会存在TaskManager的JVM堆上。同时拥有内存级的本地访问速度和更好的容错保证。

RockDBStateBackend:将所有的状态序列化后,存入本地的RocksDB中。

TODOFlink 是如何做到高效的数据交换的?

在一个 Flink Job 中,数据需要在不同的 task 中进行交换,整个数据交换是有TaskManager 负责的,TaskManager 的网络组件首先从缓冲 buffer 中收集 records,然后再发送。Records 并不是一个一个被发送的,二是积累一个批次再发送,batch 技术可以更加高效的利用网络资源。

Flink 是如何做容错的?

Flink 实现容错主要靠强大的 CheckPoint 机制和 State 机制。Checkpoint 负责定时制作

分布式快照、对程序中的状态进行备份;State 用来存储计算过程中的中间状态。

Checkpoint:

Flink会在输入的数据集上间隔性地生成barrier,通过栅栏将间隔时间短内的数据划分到相应的checkpoint中。当出现异常时,operator就能从上一次快照中恢复所有算子之前的状态,从而保证数据的一致性。

Flink 分布式快照的原理是什么?

Flink 的分布式快照是根据 Chandy-Lamport 算法量身定做的。简单来说就是持续创建分布式数据流及其状态的一致快照。核心思想是在 input source 端插入 barrier,控制 barrier 的同步来实现 snapshot 的备份和 exactly-once 语义。

TODOFlink 是如何保证 Exactly-once 语义的?

Flink 通过实现两阶段提交和状态保存来实现端到端的一致性语义。分为以下几个步骤:

开始事务(beginTransaction)创建一个临时文件夹,来写把数据写入到这个文件夹里面

预提交(preCommit)将内存中缓存的数据写入文件并关闭

正式提交(commit)将之前写完的临时文件放入目标目录下。这代表着最终的数据会有

一些延迟

丢弃(abort)丢弃临时文件

若失败发生在预提交成功后,正式提交前。可以根据状态来提交预提交的数据,也可删

除预提交的数据。

说说 Flink 的内存管理是如何做的?

Flink 并不是将大量对象存在堆上,而是将对象都序列化到一个预分配的内存块上。此外,Flink 大量的使用了堆外内存。如果需要处理的数据超出了内存限制,则会将部分数据存储到硬盘上。Flink 为了直接操作二进制数据实现了自己的序列化框架。

理论上 Flink 的内存管理分为三部分:

Network Buffers:这个是在 TaskManager 启动的时候分配的,这是一组用于缓存网络数据的内存,每个块是32K,默认分配2048个,可以通过“taskmanager.network.numberOfBuffers”修改。

Memory Manage pool:大量的 Memory Segment 块,用于运行时的算法(Sort/Join/Shuffle

等),这部分启动的时候就会分配。内存的分配支持预分配和 lazy load,默认懒加载的方式。

User Code,这部分是除了 Memory Manager 之外的内存用于 User code 和 TaskManager

本身的数据结构。

以上是关于数仓设计--维度(全量、拉链表)的主要内容,如果未能解决你的问题,请参考以下文章

[hive]数仓分层|用户纬度拉链表|维度建模

杂记宽表/窄表—事实表/维度表—数仓分层(ODS/CDM/ADS)—增量表/全量表——电信行业常见指标

数仓设计

大数据面试题--数仓

大数据面试题--数仓

大数据面试题--数仓