美团

拉链表的制作,数据量有多少,为什么不用快照表呢

  • 拉链表会跟踪每个维度的变化,包括每次修改的历史,包含开始时间、结束时间、当前记录标识等字段
  • 快照表只保留某个特定时刻的维度数据快照,不追踪变更,仅包含某个时间点的数据

数仓分层有哪些,具体做了什么,数仓分层作用

  • 数仓分层架构包括ods、dwd、dwm、dws、ads
    • ODS层是接入层,从数据源(api、数据库等)将数据同步到数仓中,中间不做任何处理操作
    • DWD层是明细层,对ODS层的数据进行清洗、关联、转换、维度退化、建设主题域等操作
    • DWM层是轻度汇总层,是DWD和DWS层的过渡层次,对DWD层的生产数据进行轻度汇总和汇总统计,把复杂指标前置处理,提升公共指标的复用性,减少重复加工
    • DWS层汇总层,按主题域、颗粒度(如买卖家)划分,按照周期粒度、维度聚合,形成字段较多的宽表,用于提供后续的业务查询和应用。更重要的是要在DWS层完成指标口径统一和沉淀
    • ADS层是应用层,按照应用域,颗粒度划分(如买卖家)划分,按照应用主题将对应的数据标签补充至应用层,形成用户画像或专项应用
  • 价值:分层的核心就是以空间换时间
    • 使数据更清晰,简化复杂问题。将复杂需求简单化拆解成多个步骤解决,每层去完成各自对数据的处理
    • 减少重复开发,提升开发效率。通过大量的预处理,建设数仓中间层,提高数据复用性
    • 数据血缘追踪,便于定位问题。分层能够清晰表和任务的上下游,知道下游哪个模块在使用,便于排查问题和后期维护

怎么设计表,怎么建模,DIM

  • 表的设计
    • 符合业务需求
    • 数据冗余与性能优化:数据冗余有时是为了查询性能的优化,比如通过预先计算一些聚合数据,减少实时计算的压力
    • 规范化与反规范化:在设计时,常常需要权衡规范化(减少冗余,避免数据重复)与反规范化(提高查询效率)之间的平衡
  • 数仓建模
    • 星型模型
      • 以事实表为中心,所有维度直接关联在事实表上,这里的维表通常是未规范化的,有较大的冗余,这样可以减少表的关联,提高查询效率
    • 雪花模型
      • 雪花模型是星型模型的基础之上,维表又关联了其他维表,这里的维表通常是规范化的,减少了冗余,但是维护成本高,性能较差。如果以Hadoop体系来建设数仓,减少join就是减少shuffle
  • DIM表的设计
    • 包含业务属性
    • 历史数据跟踪,可以采用拉链表的方式
    • 避免过度冗余:虽然维度表可能会进行反规范化,以提高查询效率,但也需要避免过度冗余,影响存储空间和数据更新
    • 设计唯一标识:每个维度表应该有一个唯一标识符,通常是维度表的主键

什么是数据倾斜,数据倾斜的解决方案

  • 数据倾斜
    • 数据倾斜根本问题存在于key的分布不均,在进行shuffle的时候,必须将各个节点上相同的key拉取到某个节点上的一个task来进行处理,比如按照key进行聚合或join等操作。此时如果某个key对应的数据量特别大的话,就会发生数据倾斜
  • 解决方案
    • 设置参数
      • 开启map join,合并小文件,设置Map|Reduce内存,用Spark3计算引擎
    • Map阶段层面
      • 剪裁列和剪裁行,减少全表、全字段查询
      • 查询带分区字段
      • distribute by rand(),用来控制map输出结果的分发,保证每个分区的数据量基本一致
    • Reduce阶段层面
      • 将distinct 改成 group by
      • 笛卡尔积优化
      • 先过滤在计算
      • 将大key打上随机值计算,再去除进行重组
    • map join
      • map join会把小表全部读入内存中,在map阶段直接拿另外一个表的数据和内存中表数据做匹配,由于在map是进行了join操作,省去了reduce运行的效率也会高很多,mapjoin还有一个很大的好处是能够进行不等连接的join操作,如果将不等条件写在where中,那么mapreduce过程中会进行笛卡尔积,运行效率特别低,如果使用mapjoin操作,在map的过程中就完成了不等值的join操作,效率会高很多
    • 小文件优化
      • 对于分区较多使用Spark3进行动态分区刷新
      • 对千分区较少或未分区的表采用重建表,补数据方法回刷
    • z-order
    • Spark3切换使用aqe

Spark的shuffle流程是怎么样的

  • Spark 任务在RDD 之间存在宽依赖时,就会触发 Shuffle。这些操作会导致数据需要重新分区,从而在不同的 Executor 之间传输数据,这就是 Shuffle 过程,Spark Shuffle 主要分为两个阶段
  • Map端
    • 根据 Key 计算目标分区 ID
    • 数据写入内存缓冲区,当缓冲区满时触发溢写
    • 溢写时数据进行排序或直接写入
    • 数据写入磁盘,并为每个 Reduce 任务生成索引文件
  • Reduce端
    • Reduce 端任务会通过 HTTP 请求,从所有 Map 任务所在的节点上获取对应分区的数据
    • 合并数据
    • 如果使用的是 Sort Shuffle,数据会按照 Key 进行排序
    • 执行后续计算

Shuffle有哪几种类型

  • Hash Shuffle
    • 适用于小规模数据集,每个 Map 任务会为每个 Reduce 任务创建一个文件
    • 容易导致小文件过多,影响性能
    • 大规模数据处理时,磁盘 I/O 开销大
  • Sort Shuffle(默认)
    • 适用于大数据量场景,减少 Shuffle 过程中的小文件问题
    • Map 端数据在写入磁盘前按照分区 ID 进行排序,并存入单个文件
  • Bypass Merge Shuffle
    • 适用于小规模分区(小于 200 个分区)
    • 避免排序,提高效率

在shuffle的过程中会进行排序吗,有哪几种排序

  • Map端

    • Sort Shuffle 过程中,Map 任务在写入数据之前,会按照 Key 和分区 ID 进行排序
      • 内存中的排序:Tim-Sort(归并排序优化)
      • 如果数据量过大,则溢写到磁盘,如果有多个溢写文件,采用 External Merge Sort 合并数据,最终生成一个大文件。
  • Reduce端

    • Reduce 任务在拉取多个 Map 任务的数据后,会对数据进行合并和排序,以便进行聚合或 Join 操作
      • 归并排序(如果多个 Map 任务的数据块太多,会采用多路归并)

什么是快速排序,时间复杂度是多少,手撕快排代码题

  • 时间复杂度:O(nlog)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
int n;
int q[N];
void quick_sort(int q[],int l,int r)
{
if(l>=r) return;

int x=q[l], i=l-1,j=r+1;
while(i<j)
{
do i++; while(q[i]<x);
do j--; while(q[j]>x);
if(i<j) swap(q[i],q[j]);
}

quick_sort(q,l,j);
quick_sort(q,j+1,r);
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++) scand("%d",&q[i]);

quick_sort(q,0,n-1);

for(int i=0;i<n;i++) printf("%d",q[i]);

return 0;
}

Spark是如何划分stage阶段

  1. 生成DAG有向无环图之后,从最后一个rdd往前推,先创建一个stage,它是最后一个stage
  2. 如果遇到了窄依赖就把该rdd加入到stage中,如果遇到了宽依赖,就从宽依赖切开,最后一个stage也就划分结束了
  3. 后面重新创建一个新的stage,还是按照第二步操作继续往前推,一直推到最开始的rdd,整个划分stage也就结束了

Spark SQL的执行流程,如何将一个SQL语句转换为任务

  1. SQL 查询 → 解析树(解析)

    • SQL 字符串被解析成 抽象语法树
  2. 解析树 → 逻辑计划(分析)

    • 对解析树进行语法和语义分析,生成 逻辑计划
  3. 逻辑计划 → 优化后的逻辑计划(逻辑优化

    • 通过应用优化规则,生成优化后的 逻辑计划
  4. 优化后的逻辑计划 → 物理计划(物理规划

    • 生成多个物理执行计划,选择最优的执行策略
  5. 物理计划 → 代码生成(代码生成)

    • 生成高效的 Java 字节码,减少运行时开销
  6. 代码生成 → 执行任务(执行)

    • 将任务分配到 Spark 集群的 Executor,进行并行计算,最终返回结果

宽窄依赖,宽依赖阶段会发生什么

  • 窄依赖(Narrow Dependency):每个子RDD的一个分区只依赖于父RDD的一个或少数几个分区,可以直接在同一个Stage 内计算,不会触发Shuffle

  • 宽依赖(Wide Dependency):子RDD的一个分区可能依赖多个父RDD的分区需要跨节点拉取数据(Shuffle),因此会触发新的 Stage

中科智泓

如果直接筛,可能导致被锁住,有其他办法么

  • insert overwrite
  • 创建临时表,存储去重后的数据,再将该表覆盖到原表
  • 如果查询慢,考虑数据倾斜,可以用distribute by rand(),提高并行度,这样做的优势是可以避免reduce任务超载,更均匀的分配数据

SQL中几种删除方式的区别

  • delete,是按条件进行删除适合关系型数据库
  • drop,是直接删除表结构+表数据
  • insert overwrite,hive不支持delete,不会锁表性能更高

事实表和维度表有什么区别

  • 事实表的数据量大,其中包含度量数据(如销售额、库存数量、交易金额等)和外键,这些外键与维度表相连
  • 维度表的数据量比较小,包含的是描述性属性,例如日期、客户名称、产品类型等

Hive和Spark有什么区别

  • Hive 是一个数据仓库工具,主要用于数据的存储、查询和分析。它建立在 Hadoop 之上,提供一个类似 SQL 的查询语言,用于批处理大数据
  • Spark是一个分布式计算框架,它是基于内存计算的,速度比Hive用MapReduce作为计算引擎更快