数仓面试

什么叫数据仓库?如何构建数据仓库?

数据仓库是一个面向主题的(Subject Oriented)、集成的(Integrate)、相对稳定的(Non-Volatile)、反映历史变化(Time Variant)的数据集合,它用于支持企业或组织的决策分析处理。

数据仓库是为了便于多维分析和多角度展现而将数据按特定的模式进行存储所建立起来的关系型数据库,它的数据基于OLTP源系统。

首先,用于支持决策,面向分析型数据处理,它不同于企业现有的操作型数据库;

其次,对多个异构的数据源有效集成,集成后按照主题进行了重组,并包含历史数据,而且存放在数据仓库中的数据一般不再修改;

数仓分层

引入数据层(ODS)、公共维度模型层(CDM)和应用数据层(APP),其中公共维度模型层包括明细数据层(DWD和汇总数据层(DWS)

公共维度模型层(CDM):存放明细事实数据、维表数据及公共指标汇总数据,其中明细事实数据、维表数据一般根据ODS层数据加工生成:公共指标汇总数据一般根据维表数据和明细事实数据加工生成。

CDM层又细分为DWD层和DWS层,分别是明细数据层和汇总数据层,采用维度模型方法作为理论基础,更多地采用一些维度退化手法,将维度退化至事实表中,减少事实表和维表的关联,提高明细数据表的易用性:同时在汇总数据层,加强指标的维度退化,采取更多的宽表化手段构建公共指标数据层,提升公共指标的复用性,减少重复加工。

应用数据层(APP):存放数据产品个性化的统计指标数据,根据CDM层与ODS层加工生成。提供差异化的数据服务、满足业务方的需求;在该层级实现报表(数易、邮件报表)、数据产品、或者其他分析主题的需要

粒度

用于确定某一事实表中的行表示什么,是业务最小活动单元或不同维度组合,即业务细节程度。

 维度建模:

维度建模从分析决策的需求出发构建模型,为分析需求服务,因此 它重点关注用户如何更快速地完成需求分析,同时具有较好的大规模复 杂查询的响应性能。其典型的代表是星形模型,以及在一些特殊场景下 使用的雪花模型。其设计分为以下几个步骤:

1.选择需要进行分析决策的业务过程。业务过程可以是单个业务事 件,比如交易的支付、退款等;也可以是某个事件的状态,比如 当前的账户余额等;还可以是一系列相关业务事件组成的业务流 程,具体需要看我们分析的是某些事件发生情况,还是当前状态, 或是事件流转效率。· 2.选择粒度。在事件分析中,我们要预判所有分析需要细分的程度,从而决定选择的粒度。粒度是维度的一个组合。· 3.识别维表。选择好粒度之后,就需要基于此粒度设计维表,包括 维度属性,用于分析时进行分组和筛选。·4.选择事实。确定分析需要衡量的指标。

星型模型

星型模型主要是维表和事实表,以事实表为中心,所有维度直接关联在事实表上,呈星型分布。

雪花模型

雪花模型,在星型模型的基础上,维度表上又关联了其他维度表。

星座模型

星座模型,是对星型模型的扩展延伸,多张事实表共享维度表。

数据仓库、数据中台、数据湖的理解

https://mp.weixin.qq.com/s?__biz=Mzg3NjIyNjQwMg==&mid=2247484122&idx=1&sn=5cd900a39725a822b31b45be0e23a4eb&chksm=cf3430d7f843b9c166010933c06a112439d490d81a7b746699d1e8fa75467ae4f3c409826684&scene=21#wechat_redirect

司机画像

在数据资源管理层之上是系统的核心,画像标签生产层,包含ETL、IDM、数据聚合模块、标签提取模块和一些算法策略工具;

抽象业务行为,司机行为以多时间切片聚合,全量聚合(最早、最晚)、最近n天聚合

为什么要分层的思考?

空间换时间。通过建设多层次的数据模型供用户使用,避免用户直接使用操作型数据,可以更高效的访问数据。  把复杂问题简单化。讲一个复杂的任务分解成多个步骤来完成,每一层只处理单一的步骤,比较简单和容易理解。而且便于维护数据的准确性,当数据出现问题之后,可以不用修复所有的数据,只需要从有问题的步骤开始修复。  便于处理业务的变化。随着业务的变化,只需要调整底层的数据,对应用层对业务的调整零感知.

如何评价一个数据仓库的好坏?

数据准确性、时效性、健壮性。

各层级表的访问量,公共汇总层,减少数据库在响应查询时必须执行的工作量,快速响应用户的查询,同时减少不同用户访问明细数据带来的结果不一致问题。

缓慢变化维

维度的属性并不是静态的,它会随着时间发生缓慢的变化

第一种处理方式:重写维度值,始终取最新数据

第二张:插入新的维度行,维度变化前的事实和过去的维度值关联

第三:添加维度列,新旧类目

必须使用代理键作为每个维表的主键,用于处理缓慢变化维

不使用代理键,每天保留一份全量数据(滴滴)

拉链表

拉链历史表,既能满足反应数据的历史状态,又可以最大程度的节省存储;

据量有点大,表中某些字段有变化,但是呢变化的频率也不是很高,业务需求呢又需要统计这种变化状态,每天全量一份呢,有点不太现实,不仅浪费了存储空间,有时可能业务统计也有点麻烦,这时,拉链表的作用就提现出来了,既节省空间,又满足了需求。一般在数仓中通过增加begin_date,en_date来表示,如下例,后两列是start_date和end_date.

begin_date表示该条记录的生命周期开始时间,end_date表示该条记录的生命周期结束时间;

end_date = ‘9999-12-31’表示该条记录目前处于有效状态;

如果查询当前所有有效的记录,则select * from order_his where dw_end_date = ‘9999-12-31′

如果查询2016-08-21的历史快照,则select * from order_his where begin_date <= ‘2016-08-21′ and end_date >= ‘2016-08-21’

事实表有哪几种类型?

事务事实表 记录的是事务层面的事实,保留的是最原子的数据,每个事务一条记录。

周期快照事实表  以有规律性的时间间隔来记录事实,如余额、库存、层级等,记录的是这个时间段内的一些聚集事实值或状态度量。

一些状态度量,需要聚集与之相关的事务才能进行识别计算,累计支付金额;直接使用操作型系统的数据作为周期快照事实表的数据源进行加工,星级,增量更新

累积快照事实表 用来跟踪实体的一系列业务过程的进展情况。订单表 特点:数据不断更新、多业务过程时间,实现方式:全量表、以业务实体的结束时间分区

聚集型事实表

order by,sort by,cluster by,distribute by的区别

1)order by是全局排序,排序过程在一个reduce中进行,在数据量较大时就会很慢

2)sort by是局部排序,排序结果在同一个reduce中使有序的

3)distribute by是将数据按照字段划分到一个reduce中,一般与sort by连用进行分组排序的作用

4)cluster by除具有distribute by功能外还具有sort by的功能

order by优化:

1)开启严格模式,order by之后添加limit子句

2)利用sort by,在每个reduce中先排序取出top项,再把预处理结果order by输出

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

1)在创建表的时候,内部表是将数据移动到数据仓库指向的路径,外部表仅记录数据所在的路径,不对数据的位置做任何改变。

2)在删除表的时候,内部表会将元数据和数据都删除,外部表只删除元数据。

列转行、行转列

1)列转行:lateral view explode(split('column_name',','))作为一个新表

2)行转列:concat_ws(',',collect_set(column_name))

无事实的事实表

 在多维数据仓库建模中,有一种事实表叫做“无事实的事实表”。普通事实表中,通常会保存若干维度外键和多个数字型度量,度量是事实表的关键所在。然而在无事实的事实表中没有这些度量值,只有多个维度外键。表面上看,无事实事实表是没有意义的,因为作为事实表,毕竟最重要的就是度量。但在数据仓库中,这类事实表有其特殊用途。无事实的事实表通常用来跟踪某种事件或者说明某些活动的范围。

代驾数仓主题域

司机、乘客、交易(派单、订单、费用)、财务域、公共域(围栏信息)、营销域(券的发放、使用)

 同一指标不同需求方给出的统计口径不一样

udf

x='[{"a":0,"b":1,"c":2},{"a":0,"b":1,"c":2},{"a":0,"b":1,"c":2}]'

parm="a"

x=eval(x)

for i in range(len(x)):

    print(x[i].get(parm))


滴滴数仓指标体系建设方法论和搭建实践

https://www.sohu.com/a/419032959_411876

hadoop2 Mappers and Reducers 内存设置

https://my.oschina.net/OttoWu/blog/814585

Hadoop MapReduce执行过程详解(带hadoop例子)

https://blog.csdn.net/weixin_33937778/article/details/91798914?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase

hr面试         https://zhuanlan.zhihu.com/p/87908851

python udf      https://blog.csdn.net/qq_26937525/article/details/54136317

flink         https://www.bilibili.com/video/av75625853?p=1

打字练习  https://www.keybr.com/

排序算法 https://www.cnblogs.com/bobo-zhang/p/10574925.html

hive字符串函数 https://www.iteblog.com/archives/1639.html

如何在实际业务中合理评估渠道价值?    https://cloud.tencent.com/developer/article/1049995


1.6.2 Hive和数据库比较

Hive 和数据库除了拥有类似的查询语言,再无类似之处。

1)数据存储位置

Hive 存储在 HDFS 。数据库将数据保存在块设备或者本地文件系统中。

2)数据更新

Hive中不建议对数据的改写。而数据库中的数据通常是需要经常进行修改的,

3)执行延迟

Hive 执行延迟较高。数据库的执行延迟较低。当然,这个是有条件的,即数据规模较小,当数据规模大到超过数据库的处理能力的时候,Hive的并行计算显然能体现出优势。

4)数据规模

Hive支持很大规模的数据计算;数据库可以支持的数据规模较小。

1.6.3 内部表和外部表

1)管理表:当我们删除一个管理表时,Hive也会删除这个表中数据。管理表不适合和其他工具共享数据。

2)外部表:删除该表并不会删除掉原始数据,删除的是表的元数据

1.6.4 4个By区别

1)Sort By:分区内有序;

2)Order By:全局排序,只有一个Reducer;

3)Distrbute By:类似MR中Partition,进行分区,结合sort by使用。

4) Cluster By:当Distribute by和Sorts by字段相同时,可以使用Cluster by方式。Cluster by除了具有Distribute by的功能外还兼具Sort by的功能。但是排序只能是升序排序,不能指定排序规则为ASC或者DESC。

1.6.5 窗口函数

RANK() 排序相同时会重复,总数不会变

DENSE_RANK() 排序相同时会重复,总数会减少

ROW_NUMBER() 会根据顺序计算

1) OVER():指定分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变而变化

2)CURRENT ROW:当前行

3)n PRECEDING:往前n行数据

4) n FOLLOWING:往后n行数据

5)UNBOUNDED:起点,UNBOUNDED PRECEDING 表示从前面的起点, UNBOUNDED FOLLOWING表示到后面的终点

6) LAG(col,n):往前第n行数据

7)LEAD(col,n):往后第n行数据

8) NTILE(n):把有序分区中的行分发到指定数据的组中,各个组有编号,编号从1开始,对于每一行,NTILE返回此行所属的组的编号。注意:n必须为int类型。

1.6.6 自定义UDF、UDTF

在项目中是否自定义过UDF、UDTF函数,以及用他们处理了什么问题,及自定义步骤?

1)自定义过。

2)用UDF函数解析公共字段;用UDTF函数解析事件字段。

自定义UDF:继承UDF,重写evaluate方法

       自定义UDTF:继承自GenericUDTF,重写3个方法:initialize(自定义输出的列名和类型),process(将结果返回forward(result)),close

为什么要自定义UDF/UDTF,因为自定义函数,可以自己埋点Log打印日志,出错或者数据异常,方便调试.

举例:

Json数组 提取指定key的value

x='[{"a":0,"b":1,"c":2},{"a":0,"b":1,"c":2},{"a":0,"b":1,"c":2}]'

parm="a"

x=eval(x) 执行字符串表达式 这里返回一个字典列表

for i in range(len(x)):

      if parm in x[i].keys():

       print(x[i].get(parm))



1.6.7 Hive优化

1)MapJoin

如果不指定MapJoin或者不符合MapJoin的条件,那么Hive解析器会将Join操作转换成Common Join,即:在Reduce阶段完成join。容易发生数据倾斜。可以用MapJoin把小表全部加载到内存在map端进行join,避免reducer处理。

2)行列过滤

列处理:在SELECT中,只拿需要的列,如果有,尽量使用分区过滤,少用SELECT *。

行处理:在分区剪裁中,当使用外关联时,如果将副表的过滤条件写在Where后面,那么就会先全表关联,之后再过滤。

3)列式存储

orc 当查询只需要少数几个字段的时候,能大大减少读取的数据量

行存储 textfile sequencefile 查询满足条件的一整行数据的时候,其余值都在相邻的地方。

4)采用分区技术

5)合理设置Map

(1)通常情况下,作业会通过input的目录产生一个或者多个map任务。

主要的决定因素有:input的文件总个数,input的文件大小,集群设置的文件块大小。

(2)是不是map数越多越好?

答案是否定的。如果一个任务有很多小文件(远远小于块大小128m),则每个小文件也会被当做一个块,用一个map任务来完成,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的map数是受限的。

(3)是不是保证每个map处理接近128m的文件块,就高枕无忧了?

答案也是不一定。比如有一个127m的文件,正常会用一个map去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果map处理的逻辑比较复杂,用一个map任务去做,肯定也比较耗时。

针对上面的问题2和3,我们需要采取两种方式来解决:即减少map数和增加map数;

6)小文件进行合并

在Map执行前合并小文件,减少Map数:CombineHiveInputFormat具有对小文件进行合并的功能(系统默认的格式)。HiveInputFormat没有对小文件合并功能。

7)合理设置Reduce

Reduce个数并不是越多越好

(1)过多的启动和初始化Reduce也会消耗时间和资源;

(2)另外,有多少个Reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;

在设置Reduce个数的时候也需要考虑这两个原则:处理大数据量利用合适的Reduce数;使单个Reduce任务处理数据量大小要合适;

8)常用参数

// 输出合并小文件

SET hive.merge.mapfiles = true; -- 默认true,在map-only任务结束时合并小文件

SET hive.merge.mapredfiles = true; -- 默认false,在map-reduce任务结束时合并小文件

SET hive.merge.size.per.task = 268435456; -- 默认256M

SET hive.merge.smallfiles.avgsize = 16777216; -- 当输出文件的平均大小小于16m该值时,启动一个独立的map-reduce任务进行文件merge

9)开启map端combiner(不影响最终业务逻辑)

set hive.map.aggr=true;

10)压缩(选择快的)

设置map端输出、中间结果压缩。(不完全是解决数据倾斜的问题,但是减少了IO读写和网络传输,能提高很多效率)

压缩方式为Snappy,特点速度快,缺点无法切分(可以回答在链式MR中,Reduce端输出使用bzip2压缩,以便后续的map任务对数据进行split)

11)开启JVM重用


1.6.8 Hive解决数据倾斜方法

1)数据倾斜长啥样?

2)怎么产生的数据倾斜?

不同数据类型关联产生数据倾斜

情形:比如用户表中user_id字段为int,log表中user_id字段既有string类型也有int类型。当按照user_id进行两个表的Join操作时。

后果:处理此特殊值的reduce耗时;只有一个reduce任务默认的Hash操作会按int型的id来进行分配,这样会导致所有string类型id的记录都分配到一个Reducer中。

解决方式:把数字类型转换成字符串类型

select * from users a

left outer join logs b

on a.usr_id =cast(b.user_id as string)

3)解决数据倾斜的方法?

1group by

注:group by 优于distinct group

解决方式:采用sum() group by的方式来替换count(distinct)完成计算。

2mapjoin

3)开启数据倾斜时负载均衡

set hive.groupby.skewindata=true;

思想:就是先随机分发并处理,再按照key group by来分发处理。

操作:当选项设定为true,生成的查询计划会有两个MRJob。

第一个MRJob 中,Map的输出结果集合会随机分布到Reduce中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是相同的GroupBy Key有可能被分发到不同的Reduce中,从而达到负载均衡的目的;

第二个MRJob再根据预处理的数据结果按照GroupBy Key分布到Reduce中(这个过程可以保证相同的原始GroupBy Key被分布到同一个Reduce中),最后完成最终的聚合操作。

点评:它使计算变成了两个mapreduce,先在第一个中在 shuffle 过程 partition 时随机给 key 打标记,使每个key 随机均匀分布到各个 reduce 上计算,但是这样只能完成部分计算,因为相同key没有分配到相同reduce上。

所以需要第二次的mapreduce,这次就回归正常 shuffle,但是数据分布不均匀的问题在第一次mapreduce已经有了很大的改善,因此基本解决数据倾斜。因为大量计算已经在第一次mr中随机分布到各个节点完成。

4)控制空值分布

将为空的key转变为字符串加随机数或纯随机数,将因空值而造成倾斜的数据分不到多个Reducer。

注:对于异常值如果不需要的话,最好是提前在where条件里过滤掉,这样可以使计算量大大减少

实践中,可以使用case when对空值赋上随机值。此方法比直接写is not null更好,因为前者job数为1,后者为2.

使用case when实例1:

select userid, name fromuser_info a

join (

select

case when userid isnull  then  cast (rand(47)* 100000 as int )

else userid end from user_read_log

) b  on a.userid = b.userid

使用case when实例2:

select '${date}' as thedate,

    a.search_type,

    a.query,

    a.category,

    a.cat_name,

    a.brand_id,

    a.brand_name,

    a.dir_type,

    a.rewcatid,

    a.new_cat_name,

    a.new_brand_id,

    f.brand_name as new_brand_name,

    a.pv,

    a.uv,

    a.ipv,

    a.ipvuv,

    a.trans_amt,

    a.trans_num,

    a.alipay_uv

from fdi_search_query_cat_qp_temp a

left outer join brand f

on f.pt='${date}000000' and case whena.new_brand_id is null then concat('hive',rand() ) else a.new_brand_id end =f.brand_id;

如果上述的方法还不能解决,比如当有多个JOIN的时候,建议建立临时表,然后拆分HIVE SQL语句。

1.6.9 用的是动态分区吗?动态分区的底层原理是什么?

a. 静态分区与动态分区的主要区别在于静态分区是手动指定,而动态分区是通过数据来进行判断。

b. 详细来说,静态分区的列实在编译时期,通过用户传递来决定的;动态分区只有在 SQL 执行时才能决定。

c. 动态分区是基于查询参数的位置去推断分区的名称,从而建立分区

26. Hive里边字段的分隔符用的什么?为什么用\t?有遇到过字段里边有\t的情况吗,怎么处理的?

hive 默认的字段分隔符为ascii码的控制符\001(^A),建表的时候用fieldsterminated by '\001'


遇到过字段里边有\t的情况,自定义InputFormat,替换为其他分隔符再做后续处理

你可能感兴趣的:(数仓面试)