2、数据仓库面试
约 5947 字大约 20 分钟
2026-01-17
你对数据仓库的理解
数据仓库是为企业提供决策支持的数据集合,数据仓库不像我们平时所使用的mysql业务数据库,业务数据库中的数据不是永久存储的,有固定的寿命,但是我们数据仓库中的数据是保存很长时间的,至少保存半年之一年的历史数据,所以有了历史数据,我们就可以多一条分析数据的维度,时间,我们可以通过分析随着时间的推移,用户的行为,用户的喜好,等等很多信息,为我们的推广,决策提供支持。
并且通过这些数据的分析,我们可以分析用户画像,报表信息,或者进行机器学习等模型的训练。
数据仓库为什么要分层
如果要我一句话说明的话,我会说:复杂的问题简单化,如何理解呢?

清晰的数据结构:
什么是清晰的数据结构,简单来说就是每一层的数据都有他自己的作用域,这样我们再使用数据的时候,可以更加方便的去定位。
数据血缘关系的追踪:
如果那我们的业务数据来举例,可能我们一张订单详情表中是由很多其他的维度表或者是事实表join得到的,如果我们不进行分层,那么随着数据量增长,我们很难分析表和表之间的关系,所以为了建立表之间的血缘关系,使用分层。
减少重复开发:
规范的数据分层,不但可以重用我们的中间数据,还可以减少计算,重用计算。
屏蔽原始数据的异常
这个很容易想到,采集到的数据不一定拿来就可以使用,再分层的过程中,我们会主键清洗掉异常的数据,有利于保护我们的数据安全性。
如何考虑系统架构的设计或者技术选型
对于离线数仓
通常采集日志数据使用flume.
采集业务数据使用sqoop。
使用消息队列kafka作为缓冲的组件。
数据的存储通常用分布式文件系统hdfs,可以存储大容量的数据。业务性数据一般存放在关系型数据库中。
计算引擎,通常使用hadoop或者spark。
实时数仓
再实际中,企业通常使用一套采集系统来采集日志数据,中间通过kafaka组件对应离线系统和实时系统。
采集业务数据通常使用Flink cdc组件
计算组件使用Flink或者spark streamming
离线数仓分层

明细数据和汇总是一个相反的概念,明细表示最原始,最详细的数据,汇总,比如统计今天的支付金额,一条数据由多条数据汇总而来。
dwd层存储的数据是最明细的数据,比较重要的一层,是基础。对于业务数据来说,本来就是结构化的,但是对于日志数据来说,是一个json字符串,需要解析为一个一个的字段。
dws和dwt都是汇总的数据,不同的是dws是按照天进行汇总的,而dwt是按照主题进行汇总,汇总多少天或者某一个地区的数据,以主题为单位。汇总的粒度不一样。
ads是聚合好的数据,比如需要报表数据,那么ads层就把报表数据处理好,别人直接使用即可。
数据集市和数据仓库区别
数据集市则是一种微型的数据仓库,它通常有更少的数据,更少的主题区域,以及更少的历史数据,因此是部门级的,一般只能为某个局部范围内的管理人员服务。
数据仓库是企业级的,能为整个企业各个部门的运行提供决策支持手段。

范式理论
定义
范式可以理解为设计一张数据表的表结构,符合的标准级别、规范和要求。
优点
采用范式,可以降低数据的冗余性。
为什么要降低数据冗余性?
- 十几年前,磁盘很贵,为了减少磁盘存储。
- 以前没有分布式系统,都是单机,只能增加磁盘,磁盘个数也是有限的
- 一次修改,需要修改多个表,很难保证数据一致性
缺点
范式的缺点是获取数据时,需要通过Join拼接出最后的数据。数据规范化,那么数据的粒度越细,但是会影响查询性能。
分类
目前业界范式有:
- 第一范式(1NF):消除完全函数依赖
- 第二范式(2NF):消除部分函数依赖
- 第三范式(3NF):消除传递依赖
- 巴斯-科德范式(BCNF)
- 第四范式(4NF)
- 第五范式(5NF)
级别越高,数据的冗余度越小。
第一范式:

比如通过,(学号,课程) 推出分数 ,但是单独用学号推断不出来分数,那么就可以说:分数 完全依赖于(学号,课程) 。
即:通过AB能得出C,但是AB单独得不出C,那么说C完全依赖于AB。
第一范式实际上要求所有的属性不可分割,实际上,1NF是所有关系型数据库的最基本要求,你在关系型数据库管理系统(RDBMS),例如SQL Server,Oracle,MySQL中创建数据表的时候,如果数据表的设计不符合这个最基本的要求,那么操作一定是不能成功的。也就是说,只要在RDBMS中已经存在的数据表,一定是符合1NF的。
第二范式

比如通过,(学号,课程) 推出姓名,因为其实直接可以通过,学号推出姓名,所以:姓名部分依赖(学号,课程)
即:通过AB能得出C,通过A也能得出C,或者通过B也能得出C,那么说C部分依赖于AB。
如果存在部分函数依赖,我们可以把主键拆开,分为两张表即可。
第三范式

比如:学号推出系名,系名推出系主任, 但是,系主任推不出学号,系主任主要依赖于系名。这种情况可以说:系主任传递依赖学号。
通过A得到B,通过B得到C,但是C得不到A,那么说C传递依赖于A。
同样,如果存在传递函数依赖,也需要对表进行拆分操作。
关系建模与维度建模
当今的数据处理大致可以分成两大类:联机事务处理OLTP(on-line transaction processing)、联机分析处理OLAP(On-Line Analytical Processing)。OLTP是传统的关系型数据库的主要应用,主要是基本的、日常的事务处理,例如银行交易。OLAP是数据仓库系统的主要应用,支持复杂的分析操作,侧重决策支持,并且提供直观易懂的查询结果。二者的主要区别对比如下表所示。

通常对于范式建模,我们使用er图建模,范式理论建立额模型表的数量比较多。
关系建模

关系模型如图所示,严格遵循第三范式(3NF),从图中可以看出,较为松散、零碎,物理表数量多,数据的粒度比较细,而数据冗余程度低。由于数据分布于众多的表中,这些数据可以更为灵活地被应用,功能性较强。
关系模型主要应用与OLTP系统中,为了保证数据的一致性以及避免冗余,所以大部分业务系统的表都是遵循第三范式的。缺点是查询数据的时候比较麻烦,需要进行很多的join操作,影响查询的性能。
因为范式建模需要去除数据的冗余性,所以需要对表进行切割,导致表比较多,比较分散。
维度建模

维度模型如图所示,主要应用于OLAP系统中,通常以某一个事实表(事实表一般在中心,并且有多张事实表,主要是一些操作)为中心进行表的组织,主要面向业务,特征是可能存在数据的冗余,但是能方便的得到数据。以业务为驱动,很方便理解。更适合做数据分析,更适合做聚合的数据分析。维度表一般是一些描述性的信息,而事实表中的属性就是维度表的外键组成的。维度表的属性一般有某一个事务的属性以及其他和该事物相关的属性组成的。
关系模型虽然冗余少,但是在大规模数据,跨表分析统计查询过程中,会造成多表关联,这会大大降低执行效率。所以通常我们采用维度模型建模,把相关各种表整理成两种:事实表(中间的表)和维度表(两边的表)两种。维度表中存储的是对事实表的描述信息。事实表一般存储的是业务信息,动词。
维度模型通常是以一个主题为单位,通常围绕一个主题进行建模。这样可以完整的描述用户的一个动作,而如果使用范式理论建模,那多个表之间join才可以描述清除。
比如再上面的模型中,中间的事实表是销售订单,如果我想看各个地区的销售情况,那么我就可以将销售表和Location表进行关联,然后以LocationId进行分区聚合,类似的,还可以以Gender进行聚合操作。
事实表和维度表
维度表
维度表本质上是我们分析数据的角度,根据每一个维度去聚合分析事实表数据,也可以理解为我们后期写sql分组的字段。
维度表:一般是对事实的描述信息。每一张维表对应现实世界中的一个对象或者概念。
例如:用户、商品、日期、地区等。可以看做是关系型数据库er图中的对象信息。
维表的特征:
- 维表的范围很宽(具有多个属性、列比较多)对象本身的属性以及和对象相关联的其他对象的属性。
- 跟事实表相比,行数相对较小:通常< 10万条,因为数对对象的描述性信息,不需要存储太多的数据
- 内容相对固定:编码表
时间维度表

事实表
整个业务系统中,有很多的业务,那么每一个业务都有一张事实表和其对应,比如下单事实表,支付事实表中一行表示一个支付事件,订单事实表中一行表示一个订单事件。
事实表中的每行数据代表一个业务事件(下单、支付、退款、评价等)。“事实”这个术语表示的是业务事件的度量值(可统计次数、个数、金额等),例如,2020年5月21日,宋宋老师在京东花了250块钱买了一瓶海狗人参丸。维度表:时间、用户、商品、商家。事实表:250块钱、一瓶。
每一个事实表的行包括:具有可加性的数值型的度量值、与维表相连接的外键,通常具有两个和两个以上的外键。
事实表的特征:
- 非常的大,每天都会增加很多数据
- 内容相对的窄:列数较少(主要是外键id和度量值)
- 经常发生变化,每天会新增加很多。
事务型事实表
以每个事务或事件为单位,例如一个销售订单记录,一笔支付记录等,作为事实表里的一行数据。一旦事务被提交,事实表数据被插入,数据就不再进行更改,其更新方式为增量更新。一行数据就是一个具体的事件。
事务性事实表对应mysql中的那张表是不会发生变化的,每一天只会新增数据,不会发生修改。
事务性事实表保留所有的数据。
周期型快照事实表
周期型快照事实表中不会保留所有数据,只保留固定时间间隔的数据,例如每天或者每月的销售额,或每月的账户余额等。离线数仓中一般周期是一天,也就是把mysql中一天的数据做一个快照。周期型快照事实表就是一个全量表。
例如购物车,有加减商品,随时都有可能变化,但是我们更关心每天结束时这里面有多少商品,方便我们后期统计分析。
累积型快照事实表
累计快照事实表用于跟踪业务事实的变化。
例如,数据仓库中可能需要累积或者存储订单从下订单开始,到订单商品被打包、运输、和签收的各个业务阶段的时间点数据来跟踪订单声明周期的进展情况。当这个业务过程进行时,事实表的记录也要不断更新。

将事实表和同步策略对比:
事务性事实表:增量同步,分区表。事务性事实表存储的就是增量同步数据,按照分区表存储,每一天存储当天新增的数据。
周期性快照事实表:对应全量同步,也是一个分区表,相当于每天做一个快照,每一个分区中存储mysql中一张表的快照。
累积性快照事实表:新增及变化同步,这一部分比较麻烦,因为要从Mysql中获取新增及变化然后和累积性快照事实表中的数据做一个整合。
维度模型分类
在维度建模的基础上又分为三种模型:星型模型、雪花模型、星座模型。
星型模型

雪花模型与星型模型的区别主要在于维度的层级,标准的星型模型维度只有一层,而雪花模型可能会涉及多级。
雪花模型

雪花模型,比较靠近3NF,但是无法完全遵守,因为遵循3NF的性能成本太高。
星座模型

星座模型与前两种情况的区别是事实表的数量,星座模型是基于多个事实表。也就是说多个事实表可以公用一个维度表。
模型选择
首先就是星座不星座这个只跟数据和需求有关系,跟设计没关系,不用选择。
基本上是很多数据仓库的常态,因为很多数据仓库都是多个事实表的。所以星座不星座只反映是否有多个事实表,他们之间是否共享一些维度表所以星座模型并不和前两个模型冲突
星型还是雪花,取决于性能优先,还是灵活更优先。目前实际企业开发中,不会绝对选择一种,根据情况灵活组合,甚至并存(一层维度和多层维度都保存)。但是整体来看,更倾向于维度更少的星型模型。尤其是Hadoop体系,减少Join就是减少Shuffle,性能差距很大。(关系型数据可以依靠强大的主键索引)
数据仓库建模
ODS层
HDFS用户行为数据
就是将hdfs上面的文件和表进行映射,中间做一个缓冲。对于日志,一般只建立一张表,将所有的日志放在一张表中(因为我们采集所有类型的日志都在一张表中),表只有一个字段。在DWD层对日志进行解析。
HDFS业务数据
因为mysql业务数据本身就是结构化的数据,所以我们只需将mysql数据库中的表数据导入hdfs中即可,数据原封不动。
针对HDFS上的用户行为数据和业务数据,我们如何规划处理?
- 保持数据原貌不做任何修改,起到备份数据的作用。
- 数据采用压缩,减少磁盘存储空间(例如:原始数据100G,可以压缩到10G左右)
- 创建分区表,防止后续的全表扫描
对于业务数据,hdfs上面有哪些数据文件,就建立那几张表,因为业务数据是从mysql中导入的,所以建立的表根据mysql表即可。
ods层的数据也需要进行分区,不管是日志还是业务数据,每一天需要导入一次,所以每天按照日期进行分区。
DWD层
DWD层需构建维度模型,一般采用星型模型,呈现的状态一般为星座模型。是最重要的一层。
维度建模一般按照以下四个步骤:
选择业务过程→声明粒度→确认维度→确认事实
- 选择业务过程
在业务系统中,挑选我们感兴趣的业务线,比如下单业务,支付业务,退款业务,物流业务,一条业务线对应一张事实表。
如果是中小公司,尽量把所有业务过程都选择。
如果是大公司(1000多张表),选择和需求相关的业务线。
- 声明粒度
数据粒度指数据仓库的数据中保存数据的细化程度或综合程度的级别。
声明粒度意味着精确定义事实表中的一行数据表示什么,应该尽可能选择最小粒度,以此来应各种各样的需求。
典型的粒度声明如下:
订单事实表中一行数据表示的是一个订单中的一个商品项。
支付事实表中一行数据表示的是一个支付记录。
声明粒度,一般是声明事实表的粒度,这个粒度要选择最小的粒度,也就是最明细的数据。
确定维度
维度的主要作用是描述业务是事实,主要表示的是“谁,何处,何时”等信息。我们后期就是根据维度进行主题的统计。
确定维度的原则是:后续需求中是否要分析相关维度的指标。例如,需要统计,什么时间下的订单多,哪个地区下的订单多,哪个用户下的订单多。需要确定的维度就包括:时间维度、地区维度、用户维度。(也就是确认每一张事实表和哪一张维度表有关系)
确定事实
此处的“事实”一词,指的是业务中的度量值(次数、个数、件数、金额,可以进行累加),例如订单金额、下单次数等。(事实表中的字段有两类,第一类是维度表的外键,另一类是度量值,也就是说每一张事实表都有一个度量值,维度外键在第三步中确定,不同的业务,有不同的度量值)
在DWD层,以业务过程为建模驱动,基于每个具体业务过程的特点,构建最细粒度的明细层事实表。事实表可做适当的宽表化处理。
事实表和维度表的关联比较灵活,但是为了应对更复杂的业务需求,可以将能关联上的表尽量关联上。如何判断是否能够关联上呢?在业务表关系图中,只要两张表能通过中间表能够关联上,就说明能关联上。

在这里有了订单明细,为什么还要订单表,这里是处于性能的考虑,假如有一些需求既可以从订单表中得到,还可以从订单明细中得到,那么选择哪种方法呢?应该选择订单表,因为订单明细表数据量更大,计算量就越大,订单表相对来说数据量少。
横向描述的是事实表,纵向描述的是维度表。
至此,数据仓库的维度建模已经完毕,DWD层是以业务过程为驱动。
DWS层、DWT层和ADS层都是以需求为驱动,和维度建模已经没有关系了。
DWS和DWT都是建宽表,按照主题去建表。主题相当于观察问题的角度。对应着维度表。

对于日志数据,根据内容对日志文件进行解析。按照内容解析,每一类日志文件的字段类型一致,方便进行查询。(我们的日志一般分为,普通页面日志,启动日志,曝光日志等)
维度表一般是名词,可以从关系型数据库的er图中去选择。一般都是描述性的表。
日志数据一般是按照内容进行解析,相同类型的日志放在一起。
DWS层与DWT层
DWS层和DWT层统称宽表层,这两层的设计思想大致相同,都是建立宽表,通过以下案例进行阐述。
- 问题引出:两个需求,统计每个省份订单的个数、统计每个省份订单的总金额
- 处理办法:都是将省份表和订单表进行join,group by省份,然后计算。同样数据被计算了两次,实际上类似的场景还会更多。那怎么设计能避免重复计算呢?
- 针对上述场景,可以设计一张地区宽表,其主键为地区ID,字段包含为:下单次数、下单金额、支付次数、支付金额等。上述所有指标都统一进行计算,并将结果保存在该宽表中,这样就能有效避免数据的重复计算。在宽表中,以维度为核心。
- 总结:
- 需要建哪些宽表:以维度为基准。有哪些维度,就建立那些宽表
- 宽表里面的字段:是站在不同维度的角度去看事实表,重点关注事实表聚合后的度量值。
- DWS和DWT层的区别:DWS层存放的所有主题对象当天的汇总行为,例如每个地区当天的下单次数,下单金额等,DWT层存放的是所有主题对象的累积行为,例如每个地区最近7天(15天、30天、60天)的下单次数、下单金额等。
宽表中的度量值是很重要的一点。
ADS层
对电商系统各大主题指标分别进行分析。
贡献者
版权所有
版权归属:codingLab
许可证:bugcode