大数据相关高频问题

159 阅读42分钟

SQL的偏移量怎么实现,怎么排序?

在 SQL 中,排序通常通过 ORDER BY 实现,可以指定某一列按升序(ASC)或降序(DESC)排列,而偏移量(即从第几条记录开始返回)则通过 LIMIT 和 OFFSET 实现,常用于分页查询。例如,ORDER BY score DESC 表示按照成绩从高到低排序,LIMIT 10 OFFSET 10 表示跳过前 10 条记录,返回接下来的 10 条数据,从而实现第 11 到第 20 条记录的查询。需要注意的是,偏移量查询必须配合排序使用,否则结果顺序不确定。在 MySQL 中也可以使用简写形式 LIMIT 10, 10,意思是从第 11 条开始取 10 条。而在 Hive 中,由于 ORDER BY 会将所有数据集中到一个 reducer 上,效率较低,因此更常使用 DISTRIBUTE BY 配合 SORT BY 来实现更高效的排序和分页。

MapReduce中的数据倾斜你了解多少?如何解决数据倾斜?

在 MapReduce 中,数据倾斜是指某些 key 的数据量远大于其他 key,导致某些 reducer 处理时间远长于其他 reducer,从而拉低整体作业效率。数据倾斜常发生在 shuffle 阶段,原因包括 key 分布不均、热点 key 过多或 join 操作中某一表数据倾斜。为了解决数据倾斜,可以采用多种方法:

  1. 使用 随机前缀法 将热点 key 拆分为多个子 key,分散到不同 reducer,再在 reduce 端汇总;
  2. 对大表和小表 join 时使用 map 端 join 或 广播小表,避免 reducer 端数据倾斜;
  3. 通过 采样分析 key 分布,定位热点数据并做针对性优化;
  4. 对于极端倾斜的 key,也可以考虑 单独处理 或 排除后单独合并。

总之,关键在于打散热点 key,均衡 reducer 的数据负载。

Spark如何划分stage?

在 Spark 中,stage 的划分是基于 RDD 之间的依赖关系 来进行的,具体来说,只有遇到 宽依赖(wide dependency) 时才会划分新的 stage。窄依赖(如 map、filter)是上一个 RDD 的每个分区只依赖上一个 RDD 的一个分区,因此可以在一个 stage 内顺序执行;而宽依赖(如 groupByKey、reduceByKey)会导致某个分区依赖上一个 RDD 的多个分区,涉及到 shuffle 操作,所以需要划分新的 stage。Spark 会从最终的 action 操作开始,反向追踪 RDD 的依赖关系,当遇到宽依赖时就断开,形成一个新的 stage,直到最前面的数据源。划分完成后,Spark 会将每个 stage 中的任务分发给不同 executor 的 task 去执行。这样划分的目的是为了优化执行计划,提高并行度和执行效率。

数仓理论了解多少?简单说一下分层。

数仓理论主要是为了解决数据整合、清洗和高效分析的问题,它强调数据的统一口径、稳定性和可复用性。在数仓架构中,通常采用分层设计,常见的分层包括:ODS(操作数据层),用于存储最原始的、从业务系统同步过来的数据,基本不做处理;DWD(数据明细层),对 ODS 的数据进行清洗、去重、标准化,保留最详细的事实数据,是数据加工的基础层;DWS(数据汇总层),对 DWD 层的数据按主题和维度进行聚合、统计,适用于业务分析需求;ADS(应用数据层),面向具体的报表和场景需求进行加工,提供最终的分析结果或接口支持。通过分层设计可以提高数据质量、增强可维护性,并降低上下游系统之间的耦合。

给你一个数据需求,开发思路是怎么样的,简单描述一下。

面对一个数据需求,通常的开发思路可以分为五个步骤:(需求评审、业务调研、模型设计、数据开发、数据自测、数据验收)

  • 第一,明确业务需求,与需求方沟通清楚要解决的问题、核心指标和口径,确保对业务目标理解一致;
  • 第二,梳理数据来源,确定需要哪些原始表、字段、分区以及数据更新周期;
  • 第三,设计数仓模型,根据维度与事实的关系,确定建表逻辑,分清明细层(DWD)、汇总层(DWS)或应用层(ADS)的位置;
  • 第四,编写 ETL 脚本,使用 Hive、Spark 等工具编写 SQL 或代码实现数据的清洗、转换和加工,同时做好数据质量校验和分区管理;
  • 最后,输出数据结果,将结果表或接口对接给下游使用方,如报表系统、大屏展示或接口服务,并做好上线后的监控和维护。整个过程中要注重沟通、版本控制和数据口径的一致性,确保数据的准确性和稳定性。

常见的窗口排序函数你知道哪些?说一下他们各自作用。

常见的窗口排序函数主要包括 ROW_NUMBER()、RANK()、DENSE_RANK()、NTILE() 等,它们常配合窗口函数 OVER(PARTITION BY ... ORDER BY ...) 使用,作用是对分组内的数据进行排序和排名。

  • ROW_NUMBER() 是最常用的,给每一行按顺序分配唯一的序号,不会出现重复;
  • RANK() 会对相同值的记录赋相同名次,跳过后续名次(比如并列第 2,下一名是第 4);
  • DENSE_RANK() 同样对相同值的记录赋相同名次,但不会跳过名次(并列第 2,下一名是第 3);
  • NTILE(n) 是把分组内的记录按顺序平均分成 n 份,为每行分配一个从 1 到 n 的桶编号。

它们常用于如 TopN 查询、分组内排序、分位分析等场景,能在保留全部数据的同时进行灵活排名。

HDFS如何保证数据的可靠性和高可用性?它的副本机制是如何工作的?

HDFS 通过副本机制来保证数据的可靠性和高可用性,每个文件被切分成多个块(Block),每个块默认会在集群中存储 3 个副本,分别保存在不同的 DataNode 上,从而避免因单点故障导致数据丢失。当某个副本所在的节点宕机或数据损坏时,NameNode 会感知并在其他健康节点上重新复制该副本,自动修复数据,确保副本数维持在配置值以上;同时,HDFS 的 NameNode 虽然是管理元数据的核心,但通过配置 主备 NameNode(HA 高可用架构),结合 Zookeeper 实现故障自动切换,避免了单点故障风险。整体上,HDFS 通过副本冗余、故障感知、自愈机制和主备架构,保障了系统的稳定性与数据安全。

ES是如何保证数据高可用性的?如果一个节点宕机,系统会如何处理?

Elasticsearch 通过主分片和副本分片机制来实现数据的高可用性。每个索引的数据会被划分为多个主分片(primary shard),并为每个主分片配置一个或多个副本分片(replica shard),这些分片分布在不同的节点上。当一个节点宕机时,集群中的 Master 节点会立即检测到,并将其上的主分片的副本分片提升为新的主分片,继续对外提供读写服务,同时将缺失的副本在其他节点上重新分配和恢复,确保数据不丢失、服务不中断。此外,Elasticsearch 的分布式架构可以自动进行分片重分配、数据均衡和故障转移,从而实现系统的自我修复和持续可用。

Hive表里的分桶有哪几种形式,分桶与分区的区别?什么时候要去做分桶?

Hive 中的分桶是通过对某一列进行哈希计算,再将数据平均分配到固定数量的桶中,常见形式是在建表时使用 CLUSTERED BY (列名) INTO N BUCKETS 来定义分桶。分桶和分区的主要区别在于:分区是将数据按目录层级物理划分,适用于大范围数据的筛选,提升查询效率;而分桶是在分区或全表内部的进一步划分,提高了数据的分布均匀性和采样、Join 的效率。通常在以下场景下需要使用分桶:一是当需要对大表做 桶映射 Join(bucket map join),提升 Join 性能;二是在使用 抽样查询(tablesample) 时保证样本均匀性;三是为了优化数据倾斜问题或提升并行度。简而言之,分区适用于大粒度的数据划分,分桶适用于细粒度的数据优化。

Spark调优思路?

Spark 调优的整体思路可以从资源配置并行度控制Shuffle 优化缓存策略代码逻辑五个方面入手。首先是资源配置,要合理设置 executor 的数量、内存(executor-memory)和 CPU 核数(executor-cores),避免资源浪费或不足;其次是并行度控制,通过设置 spark.sql.shuffle.partitions 或 RDD 的 repartition 来调整任务并行度,防止过多或过少的分区导致性能问题;在 Shuffle 优化 方面,要尽量减少 shuffle 操作,比如用 map-side join 或 broadcast join 替代大表 shuffle;对于 缓存策略,使用 persist 或 cache 缓存重用的中间结果,减少重复计算;最后,优化 代码逻辑,如避免冗余转换、过滤尽量提前、少用宽依赖操作等,提升 DAG 执行效率。调优过程中还要结合 Spark UI 分析瓶颈,定位 Stage 执行慢的具体原因,做到有的放矢。

对ES的倒排索引了解多少?工作原理?如何提高其查询效率?

Elasticsearch 使用的是倒排索引(Inverted Index) ,这是实现全文检索的核心机制。其原理是:将每个文档中的字段内容进行分词处理,然后为每个词项建立一个词典(term),记录该词在哪些文档(文档 ID)中出现,以及出现的位置和频率。这样查询时不是遍历每个文档去找关键词,而是直接查词典获取相关文档列表,极大提升了检索效率。为了进一步提高查询性能,可以采取多种优化手段:

  • 一是合理设置字段类型和分词器,减少无效字段的索引;
  • 二是使用 keyword 类型字段做精确匹配,避免不必要的全文分词;
  • 三是通过设置合适的 filter 缓存结果、避免重复计算;
  • 四是开启 doc_values 支持聚合、排序等操作,减少内存消耗;
  • 五是避免过度嵌套结构,必要时使用 flattenedjoin 优化文档结构。 此外,合理的索引设计、分片分配和冷热数据分离也是保障查询效率的重要手段。

了解MySQL中的事务吗?它的ACID特性指的是什么?

MySQL 中的事务是指一组操作的集合,要么全部执行成功,要么全部回滚撤销,常用于保证数据的一致性与完整性。事务具有四大特性,也就是 ACID

  • 原子性(Atomicity) 指事务中的操作要么全部完成,要么全部不做,出现错误可以回滚;
  • 一致性(Consistency) 保证事务执行前后数据都满足数据库的约束规则,不会出现破坏数据逻辑的状态;
  • 隔离性(Isolation) 表示多个事务并发执行时互不干扰,常见的隔离级别包括读未提交、读已提交、可重复读、串行化;
  • 持久性(Durability) 指事务一旦提交,对数据的修改就是永久性的,即使系统宕机也不会丢失。

MySQL 中默认使用 InnoDB 引擎来支持事务,其通过 redo log、undo log 和锁机制来实现对 ACID 的保障。

简述数据仓库中“维度”和“事实”表的区别及其作用

在数据仓库中,维度表和事实表是构建星型或雪花模型的核心组成部分事实表(Fact Table) 用于存储与业务事件相关的度量值或指标数据,例如订单金额、销售数量等,通常数据量大,记录的是“发生了什么”;而 维度表(Dimension Table) 则存储描述事实的维度信息,比如时间、地区、产品、用户等,是对事实的多角度分析视角。二者的主要区别在于:事实表关注“数值和业务行为”,维度表关注“描述和分类信息”;事实表一般包含外键关联多个维度表,而维度表一般数据量小但字段丰富,便于查询和筛选。在实际应用中,通过维度表可以对事实表的数据进行分组、聚合、切片和钻取,是实现多维分析(OLAP)的基础。

构建数仓的模型有哪些?展开讲讲。

构建数仓的常见模型主要包括星型模型、雪花模型和事实星座模型

  • 星型模型是最常用的一种,其特点是一个中心事实表直接连接多个维度表,结构简单、查询高效,适合于查询频繁、维度较少的场景;
  • 雪花模型是在星型模型基础上对维度表进一步规范化,将维度表拆分为多个层级子表,形成树状结构,减少冗余、节省存储,但查询时关联复杂、效率较低;
  • 事实星座模型(也叫复合星型模型)是多个事实表共享维度表的结构,适用于存在多个业务过程(如订单、支付、退货等)且共享维度的复杂分析需求。选择哪种模型需根据业务需求、数据复杂度和查询性能权衡,实际开发中也常采用分层建模(ODS、DWD、DWS、ADS)与上述模型相结合,实现数据的稳定、复用与灵活分析。

HDFS 的写数据流程是怎样的?如果一个 DN在写入过程中发生故障,Hadoop 会如何处理?

 HDFS 中的写数据流程从客户端开始,首先客户端向 NameNode 请求文件写入权限。NameNode 确认文件不存在后,将文件写入的权限以及负责存储数据块的 DataNode 信息返回给客户端。客户端然后会与 DataNode 建立连接,并通过一个个数据块的方式进行写入。每个数据块(Block)会按照一定的副本策略(默认三个副本)分配到多个 DataNode 上。客户端向每个 DataNode 传输数据时,会将数据写入第一个数据块,完成后接着写第二个、第三个,以此类推。如果在写入过程中,某个 DataNode 发生故障,NameNode 会根据副本机制将数据复制到其他健康的 DataNode 上,确保数据不会丢失。通过心跳机制和副本机制,DataNode 故障会被检测到,并且数据会自动恢复到最新副本。故障发生时,HDFS 会根据剩余副本的数据恢复丢失的部分。

1Client通过Distributed FileSystem模块向NN请求上传文件,NN检查目标文件是否已存在,父目录是否存在
2、NN返回是否可以上传
3Client请求第一个block上传哪几个DN服务器上
4、NN返回3个DN节点,分别为DN1、DN2、DN3
5Client通过FSDataOutputStream模块请求DN1建立传输通道,DN1收到请求会继续调用DN2,DN2继续调用DN3
6、DN3应答DN2,DN2应答DN1,DN1应答Client
7Client开始往DN1上传第一个block(先读取数据放到一个本地磁盘缓存),以packet为单位,DN1收到一个packet就会上传给DN2,DN2传给DN3;DN1每传一个packet会放入到一个应答队列等待应答
8、当一个block传输完成后,Client会再次请求NN上传第二个block的服务器,重复执行3-7

对MySQL中的锁机制了解多少?展开说说。

MySQL 中的锁机制是保证并发事务数据一致性的重要手段,主要分为表级锁、行级锁和意向锁三类。其中,表级锁作用于整张表,粒度大、开销小,适用于读多写少的场景,如 MyISAM 引擎;行级锁作用于具体数据行,粒度小、并发高,是 InnoDB 的核心优势,适用于高并发写入场景。InnoDB 还引入了 意向锁(Intention Lock) ,用于协调多事务间的锁兼容性,避免全表扫描判断冲突。行锁又细分为 共享锁(S锁)排它锁(X锁) ,前者允许多个事务读取同一行,后者则独占修改。InnoDB 默认使用 两阶段锁协议,事务在需要时加锁,直到提交或回滚才释放。此外,为了防止死锁,InnoDB 会自动检测死锁并中断回滚其中一个事务。通过灵活的锁机制设计,MySQL 在保证数据一致性的同时,也兼顾了系统性能和并发效率。

要在MySQL数据库中优化一个慢查询,你会从哪些方面入手?

在优化 MySQL 中的慢查询时,我会从 SQL语句本身、索引设计、执行计划、数据库结构与配置 四个方面入手。首先检查 SQL 是否存在不必要的子查询、函数操作或全表扫描,尝试通过 **where 条件优化、减少数据量、避免 select *** 等方式提高效率;其次分析表的 索引是否合理建立,重点关注 where、join、group by 中的字段是否使用了合适的联合索引或覆盖索引;然后通过执行 EXPLAIN 分析 SQL 的执行计划,观察是否走索引、是否存在全表扫描、临时表或文件排序等性能瓶颈;再者可以考虑优化 表结构(如字段类型、分区等)或调整 数据库参数(如查询缓存、连接数、InnoDB buffer pool 大小等);最后建议开启 慢查询日志,配合工具如 pt-query-digest 定位频繁慢查询,持续监控和调优,从而系统性地提升查询性能。

了解“数据血缘” 吗?它对数据管理有什么帮助?

数据血缘(Data Lineage)是指数据从源头采集、经过清洗、加工、传输、存储、再到最终使用的全流程流转关系,可以追踪“数据从哪来、到哪去、怎么变”。 它在数据管理中具有重要作用:

  • 一是便于 数据追溯与问题定位,当发现数据异常时,可以快速找到上游数据源和处理环节;
  • 二是方便 数据影响分析,比如修改某张表或字段时,可根据血缘关系判断会影响哪些下游表、报表或系统,降低风险;
  • 三是提升 数据治理与合规性,实现数据全生命周期可视、可控、可审计;
  • 四是支持 运维与优化,帮助识别冗余链路和资源浪费,提高整体系统稳定性。

通过构建数据血缘图,企业能更科学高效地管理和利用数据资产,是现代数据仓库和数据中台建设的重要基础。

数据仓库中的OLAP和OLTP有什么区别?详细说说。

在数据仓库中,OLAP(联机分析处理)和 OLTP(联机事务处理)是两种截然不同的数据处理模式。OLTP 主要面向日常业务操作,如订单处理、用户注册等,特点是读写频繁、操作简单、并发量大、数据实时性强,数据结构高度规范化,通常运行在传统的关系型数据库上;而 OLAP 面向的是管理层或分析师的决策支持,特点是查询复杂、以读为主、数据量大、分析维度多、响应速度要求高,通常运行在数据仓库或分析型数据库中,采用星型、雪花模型等方式对数据建模。简单来说,OLTP 处理“业务操作”,强调“事务一致性和效率”;OLAP 处理“数据分析”,强调“多维聚合和查询性能”,两者服务于不同的业务目标和系统架构,常通过数据同步、ETL 等手段实现数据从 OLTP 到 OLAP 的流转。

谈谈你对Spark中Shuffle机制的理解。

在 Spark 中,Shuffle 是指数据在不同任务之间因依赖关系而产生的数据重分布过程,通常发生在需要跨分区聚合、排序或 join 操作时(如 groupByKeyreduceByKeyjoin 等)。Shuffle 会将上游任务输出的数据按照 key 分区打散,通过网络传输到下游不同的节点执行任务,这一过程会涉及磁盘读写和网络 IO,因此是 Spark 中最消耗性能的操作之一。Shuffle 会生成大量临时文件,并且在 Executor 之间建立数据拉取关系,存在资源开销和失败重试问题。为了优化 Shuffle,Spark 引入了多种机制,如使用 map-side combine 进行预聚合、合理设置 partition 数量、避免使用 groupByKey 等高开销算子,并通过 Tungsten 项目提升序列化与内存管理性能。理解 Shuffle 的原理和触发场景,有助于我们在编写 Spark 程序时减少 Shuffle 次数、优化 DAG 结构,从而提升整体计算效率。

数据仓库的基本概念是什么?它与传统的数据库系统有何不同?

数据仓库是面向主题的、集成的、相对稳定的、反映历史变化的数据集合,主要用于支持企业管理决策。它与传统数据库系统最大的不同在于:传统数据库(OLTP) 主要用于处理日常业务操作,如插入、修改、删除,关注的是单点数据的事务一致性与实时性;而 数据仓库(OLAP) 更关注对大规模历史数据的整合分析,支持多维度的查询、聚合、趋势分析等。数据仓库一般会经过 ETL 过程将来自多个系统的原始数据清洗、转换并统一存储,具有更强的数据集成性和可分析性;同时,它更注重查询性能优化,常用星型、雪花模型建模,牺牲部分写入效率以换取更高的读性能。简而言之,数据仓库是企业“做分析”的大脑,传统数据库则是“做业务”的心脏。


说一下ES中的核心组件以及各自关系作用。

Elasticsearch(ES)由多个核心组件构成,包括 索引(Index)文档(Document)分片(Shard)节点(Node)集群(Cluster) 。索引是逻辑上的数据集合,相当于数据库;文档是索引中的最小数据单元,相当于数据库中的一行;每个索引会被划分成多个主分片和副本分片,分片是实际存储和查询的基本单位;节点是 ES 的运行实例,负责管理分片和执行请求;多个节点组成一个集群,共同管理和协调数据存储与查询任务。在整个架构中,文档被存入索引,索引被切分为分片,分片分布在节点上,节点组成集群提供服务。这些组件协同工作,支持 ES 的高可用性、分布式存储与实时搜索能力,使其在海量数据处理和全文检索场景中表现出色。

MySQL中如何使用子查询?子查询和连接查询(JOIN)有什么区别?

在 MySQL 中,子查询是嵌套在另一个查询语句中的 SELECT 语句,通常用于 where、from 或 select 子句中,实现按条件筛选、计算或动态生成临时表的功能,比如 SELECT * FROM orders WHERE user_id IN (SELECT id FROM users WHERE age > 30)。子查询有标量子查询、行子查询、列子查询和表子查询几种形式。而 连接查询(JOIN) 是通过共享字段将多张表横向拼接,形成一张大的结果集,常用于跨表数据整合,如 INNER JOINLEFT JOIN 等。两者的主要区别在于:子查询是“先算后用”,适合层次关系和逻辑嵌套,代码结构清晰但可能效率较低JOIN 是“边算边用”,更适合大数据量下的高效关联,执行性能通常优于子查询。实际使用中需根据业务场景和性能需求选择合适方式。


谈谈MapReduce中的Task。

在 MapReduce 中,Task 是作业执行的最小计算单元,分为 Map TaskReduce Task 两类。Map Task 负责对输入数据进行切片(Split)后并行处理,将原始数据映射成键值对(key-value);Reduce Task 则在 Map 输出的 key 经过 Shuffle 和 Sort 过程后,对相同 key 的 value 进行归约操作,生成最终输出结果。每个 Task 都在集群中的某个节点上运行,Map Task 和 Reduce Task 是相互独立的,数量可根据数据量与集群规模动态配置。Map 任务完成后,Reduce 才开始,任务调度由 YARN 或 JobTracker 统一管理。合理设置 Task 数量、优化数据分区策略,有助于提升 MapReduce 的并行效率与容错能力,是大数据处理框架高性能的基础。


若MR中某个Task失败了,Hadoop会如何处理?

在 MapReduce 中,如果某个 Task(无论是 Map 还是 Reduce)失败了,Hadoop 会通过 任务重试机制 来自动处理。具体来说,任务失败后,JobTracker(或 YARN 的 ApplicationMaster)会在其他可用节点上重新调度该 Task 的副本重跑,默认最多重试 4 次(可通过参数 mapreduce.map.maxattemptsmapreduce.reduce.maxattempts 配置)。如果在限定次数内任务成功,则整个作业继续执行;若多次重试仍失败,整个 Job 会被标记为失败。此外,为了提高稳定性,Hadoop 会记录失败节点,并在短时间内避免再次调度任务到这些节点上。通过这种机制,MapReduce 保证了在节点宕机、数据异常等场景下的 容错性与任务的最终可完成性,体现了其在大数据处理中的鲁棒性设计。

MapReduce的Shuffle过程是怎样的?

在 MapReduce 中,Shuffle 是连接 Map 阶段和 Reduce 阶段的核心过程,主要包括数据的输出、传输与排序。具体来说,当 Map Task 处理完数据后,会将输出结果按照 key 进行分区(Partition),写入本地磁盘的环形缓冲区,同时进行 本地排序(Sort)和可选的 Combiner 本地聚合。随后 Reduce Task 会通过 HTTP 拉取(Fetch) 所有 Map 输出的属于自己分区的数据块,这就是跨节点的数据 Shuffle。拉取完成后,Reduce 端还会对所有数据进行 归并排序(Merge Sort) ,以保证相同 key 的 value 是聚集在一起的,便于后续聚合处理。Shuffle 涉及大量的磁盘 IO 和网络 IO,是整个 MapReduce 性能瓶颈所在。通过优化 partition 策略、开启 Combiner、本地聚合等手段,可以有效减少 Shuffle 开销,提升作业整体效率。


为什么Shuffle可能会成为MR任务的性能瓶颈?如何优化?

Shuffle 之所以可能成为 MapReduce 任务的性能瓶颈,是因为它涉及 大量的磁盘读写、网络传输和数据排序操作,尤其在数据量大或 key 倾斜时,会导致任务处理时间大幅增加。具体瓶颈包括:Map 端输出写磁盘、本地排序占内存、Reduce 端拉取数据的网络开销、以及大量小文件或数据倾斜造成负载不均等。为了优化 Shuffle,可以采取以下策略:一是使用 Combiner 减少中间数据量;二是调整 Partitioner 策略,避免 key 倾斜;三是合理设置 Map/Reduce 的个数内存参数,提升并行度和缓冲区容量;四是开启 压缩(如 Snappy) 减少 IO 量;五是优化 磁盘与网络资源配置,提升数据读写与传输效率。通过这些手段,可以显著缓解 Shuffle 对性能的影响,提高 MapReduce 任务整体的执行效率和稳定性。


MySQL的联合查询(JOIN)有哪几种类型,它们之间有什么区别?

MySQL 中常见的联合查询(JOIN)有四种类型:内连接(INNER JOIN)左连接(LEFT JOIN)右连接(RIGHT JOIN)全连接(FULL JOIN,MySQL 需通过 UNION 实现)INNER JOIN 只返回两张表中匹配的记录,常用于关联查询;LEFT JOIN 会返回左表的所有记录,即使右表无匹配,也会以 NULL 填充右表字段;RIGHT JOIN 与其相反,返回右表的所有记录,即使左表无匹配也会用 NULL 补齐左表字段;而 FULL JOIN 返回左右两表中所有记录,只要一方有匹配就保留,另一方没有则补 NULL。它们的主要区别在于是否保留未匹配的记录,使用时需根据业务需求选择合适的 JOIN 类型,从而确保查询结果的完整性和准确性。


MySQL中的GROUP BY和ORDER BY有什么区别?

在 MySQL 中,GROUP BY 和 ORDER BY 是两种不同的子句,分别用于分组聚合和排序操作GROUP BY 是将查询结果按指定字段分组,常与聚合函数(如 COUNT、SUM、AVG 等)配合使用,对每一组数据进行统计处理,比如统计每个部门的人数;而 ORDER BY 是用于将查询结果按指定字段进行升序(ASC)或降序(DESC)排列,控制结果的展示顺序。两者的区别在于:GROUP BY 是为了归类、合并记录,ORDER BY 是为了排序展示,不改变记录数量。它们也可以同时使用,比如先分组统计,再按统计结果排序,从而实现对聚合结果的有序输出。理解这两者的区别,有助于更灵活地组织查询逻辑。


什么是ETL过程?在数据仓库建设中,ETL的角色和重要性是什么?

ETL 是数据仓库建设中的核心过程,指的是 Extract(抽取)、Transform(转换)、Load(加载) ,即从多个异构数据源中抽取原始数据,经过清洗、格式转换、校验、整合等处理后,加载到数据仓库中。ETL 是数据从“原始混乱”到“统一规范”的关键桥梁,它决定了数据的质量、结构和可用性。抽取阶段关注数据获取的效率与完整性,转换阶段实现数据标准化与业务规则处理,而加载阶段则需保障数据一致性与加载效率。在数据仓库中,ETL 是数据生命周期的起点,其稳定性和准确性直接影响数据分析、报表和决策的可信度,因此在整个数据体系中具有举足轻重的作用。

宽窄依赖是什么?

  • 正确答案:宽窄依赖是分布式计算框架(如Apache Spark)中用于描述数据分区之间依赖关系的术语。宽依赖指的是父RDD的一个分区会被多个子RDD的分区所依赖,而窄依赖指的是每个父RDD的分区最多被一个子RDD的分区所依赖。
  • 解答思路:要理解宽窄依赖,首先需要明确RDD(弹性分布式数据集)的概念以及Spark中的数据处理流程。在Spark中,RDD之间的转换操作会产生依赖关系。这些依赖关系分为两种类型:窄依赖和宽依赖。窄依赖的特点是一个父RDD的分区最多对应一个子RDD的分区,因此数据可以在同一个节点上进行处理,不需要大量的shuffle操作。而宽依赖则涉及多个父RDD分区的数据被映射到一个子RDD分区,这通常会导致数据需要在不同节点之间进行shuffle,从而增加计算开销。
  • 深度知识讲解
  • 窄依赖:窄依赖的特点是一个父RDD的分区最多对应一个子RDD的分区。这种依赖关系可以是one-to-one(一对一)或者partition split(分区拆分)。由于窄依赖不涉及跨节点的数据移动,因此性能较高。例如,在map、filter等操作中产生的依赖关系通常是窄依赖。
  • 宽依赖:宽依赖的特点是一个父RDD的分区可能被多个子RDD的分区所依赖。这种依赖关系通常会导致shuffle操作,因为数据需要重新分布以满足新的分区要求。例如,在groupByKey、reduceByKey等操作中产生的依赖关系通常是宽依赖。
  • Shuffle操作的影响:宽依赖之所以性能较低,主要是因为它会触发shuffle操作。在shuffle过程中,数据需要从不同的节点传输到目标节点,这会带来较大的网络开销和磁盘I/O开销。因此,在设计Spark作业时,尽量减少宽依赖的使用是非常重要的。
  • 扩展知识
  • 优化策略:为了减少宽依赖带来的性能问题,可以采用一些优化策略,例如使用reduceByKey代替groupByKey,因为reduceByKey可以在本地合并数据,从而减少shuffle的数据量。此外,还可以通过调整分区数来优化shuffle操作。
  • Lineage与容错性:窄依赖和宽依赖还影响了RDD的lineage(血统)信息和容错性。对于窄依赖,由于数据不需要shuffle,因此可以通过简单的重算来恢复丢失的分区。而对于宽依赖,由于涉及到shuffle,恢复丢失的分区可能需要重新计算整个父RDD的分区。
  • 伪代码示例
// 窄依赖示例 
val rdd1 = sc.parallelize(List(1, 2, 3, 4)) 
val rdd2 = rdd1.map(x => x * 2) // map操作产生窄依赖 
// 宽依赖示例 
val rdd3 = sc.parallelize(List("a", "b", "a", "b")) 
val rdd4 = rdd3.groupByKey() // groupByKey操作产生宽依赖 

spark join的实现原理是什么?

  • 正确答案:Spark中的join操作是基于键的两个分布式数据集(RDD或DataFrame)进行合并的过程。其实现原理主要依赖于shuffle过程,将具有相同键的数据聚集到同一个分区中,然后在每个分区上执行具体的连接逻辑。根据不同的场景和数据分布,Spark提供了多种类型的join实现,包括broadcast join、shuffle hash join、sort merge join等。
  • 解答思路: 1. 首先理解Spark的分布式计算模型,数据被划分为多个分区,分布在集群的不同节点上。 2. 在join操作中,需要确保来自两个数据集且具有相同键的记录能够在同一个分区中相遇,以便进行匹配。 3. Spark通过shuffle阶段将数据重新分布,使得具有相同键的记录能够被分到同一个分区。 4. 根据数据量的大小和分布情况,Spark会选择不同的join策略以优化性能。例如,当一个数据集较小可以放入内存时,会使用broadcast join;而当两个数据集都较大时,则可能选择shuffle hash join或sort merge join。
  • 深度知识讲解
  • Shuffle过程:Shuffle是Spark中最耗时的操作之一,它涉及到数据在不同节点之间的重新分布。在join操作中,shuffle的作用是将具有相同键的记录从不同的分区移动到相同的分区中。这通常通过哈希函数实现,即根据键值计算哈希值,并将具有相同哈希值的记录分配到同一个分区。
  • Broadcast Join:当其中一个数据集较小,可以将其广播到所有节点的内存中时,可以直接在每个节点上与另一个数据集进行join操作,避免了大规模的shuffle操作。这种情况下,性能会显著提升。
  • Shuffle Hash Join:适用于两个数据集都较大但无法直接广播的情况。首先会对两个数据集分别进行shuffle,使得具有相同键的记录被分到同一个分区。然后在每个分区上构建哈希表进行join操作。
  • Sort Merge Join:当数据已经按照键排序时,可以使用sort merge join。这种方法不需要构建哈希表,而是通过排序后的顺序逐个比较键值来完成join操作。适用于数据量特别大且无法完全放入内存的情况。
  • 底层实现:在RDD层面,join操作可以通过cogroup实现,即将具有相同键的值对收集到一起,然后逐一匹配。在DataFrame/Dataset层面,Spark SQL优化器会根据统计信息自动选择合适的join策略。
  • 扩展知识
  • Join类型:除了常见的inner join外,还有left outer join、right outer join、full outer join等。每种类型决定了如何处理不匹配的记录。
  • 性能调优:为了减少shuffle带来的开销,可以考虑对数据进行预分区或预聚合。此外,合理设置shuffle分区数(spark.sql.shuffle.partitions)也会影响性能。
  • Skewed Join:如果某些键的分布非常不均匀(即存在数据倾斜),会导致某些任务处理的数据量远大于其他任务,从而成为性能瓶颈。为了解决这个问题,可以采用动态分区裁剪或对倾斜键进行特殊处理。
  • 伪代码
def join(rdd1, rdd2):
# 对rdd1和rdd2进行shuffle,将相同key的数据聚集到同一分区 
shuffled_rdd1 = rdd1.partitionBy(numPartitions) 
shuffled_rdd2 = rdd2.partitionBy(numPartitions) 
def process_partition(partition1, partition2):
# 构建哈希表或排序后进行join操作 
hashmap = {} 
for key,value in partition1: 
hashmap[key] = value 
result = [] 
for key, value in partition2: 
if key in hashmap: 
result.append((key, (hashmap[key], value))) 
return result
# 对每个分区执行join操作 
joined_result = shuffled_rdd1.cogroup(shuffled_rdd2).flatMap(process_partition) 
return joined_result 

Spark SQL的执行过程是怎样的?

  1. 当用户提交一个SQL查询时,Spark SQL会先通过SQL解析器将SQL语句转换为逻辑查询计划。这个逻辑查询计划是一个抽象的表示,描述了查询的操作意图。
  2. 接下来,Spark SQL的优化器(Catalyst Optimizer)会对逻辑查询计划进行优化。优化步骤包括规则优化和成本优化,目的是生成一个高效的物理查询计划。
  3. 最后,物理查询计划会被转换为Spark的任务,并通过Spark的分布式计算框架执行。这些任务会被分配到集群中的各个节点上并行运行。
  • 深度知识讲解: -
  • 逻辑查询计划:这是对SQL语句的一种内部表示形式。它描述了查询的基本操作,例如选择、投影、连接等。逻辑查询计划不包含具体的执行细节,只是提供了一个高层次的查询描述。
  • Catalyst Optimizer:这是Spark SQL的核心组件之一,负责优化逻辑查询计划。Catalyst使用了一种基于规则和成本的优化方法。规则优化会应用一些通用的优化策略,如谓词下推、列裁剪等。成本优化则会根据统计信息选择最有效的执行路径,例如选择合适的连接算法或数据分区方式。
  • 物理查询计划:这是经过优化后的具体执行计划,包含了所有必要的执行细节,例如数据如何在集群中分布、使用哪种算法来执行连接操作等。物理查询计划最终会被转换为Spark的任务。
  • 任务调度与执行:Spark将物理查询计划转换为一组任务,并通过其分布式计算框架执行。每个任务都在集群的一个节点上运行,处理一部分数据。任务之间可能会有依赖关系,这种依赖关系被表示为有向无环图(DAG),Spark会根据DAG来调度任务的执行顺序。

Spark任务的提交流程是怎样的?

  1. 用户通过spark-submit命令提交一个Spark应用程序。
  2. Spark根据配置选择合适的集群管理器(如YARN、Mesos等)。
  3. 集群管理器启动Driver程序,Driver负责解析用户代码,生成DAG(有向无环图),并将其转化为Stage(阶段)和Task(任务)。
  4. Driver向集群管理器申请资源,用于启动Executor进程。
  5. Executor在分配的资源上运行具体的任务,并将任务执行的结果反馈给Driver。
  6. 最后,Driver将整个任务的执行结果返回给用户。

常见的Spark算子有哪些?

首先明确Spark是基于RDD(弹性分布式数据集)的计算框架,所有的操作都可以归结为两种类型:转换行动。转换算子用于从已有的RDD生成新的RDD,而行动算子则用于触发实际的计算并将结果返回给驱动程序或保存到外部存储中。

  • 转换算子(Transformation):这类算子是懒加载的,意味着它们不会立即执行,而是记录下要执行的操作步骤。只有当行动算子被调用时,才会触发真正的计算。例如:
    • map(func):对RDD中的每个元素应用func函数,并返回一个新的RDD。
    • filter(func):根据func函数筛选出满足条件的元素,形成新的RDD。
    • reduceByKey(func):对键值对形式的RDD,按照相同的key进行聚合操作。
    • join(otherDataset):对两个RDD进行内连接操作,通常用于键值对数据集。
  • 行动算子(Action):这类算子会触发实际的计算,并将结果返回给驱动程序或者写入外部存储系统。例如:
    • collect():将RDD中的所有元素以数组的形式返回到驱动程序中。
    • count():返回RDD中的元素个数。
    • saveAsTextFile(path):将RDD的内容保存为文本文件。
    • foreach(func):对RDD中的每个元素应用func函数,通常用于副作用操作如打印日志。
  • 底层实现原理:在Spark中,RDD是一个不可变的分布式数据集合,所有的转换算子实际上是在构建一个逻辑执行计划(DAG图),而行动算子会触发这个计划的实际执行。Spark采用的是延迟计算机制,这意味着只有当行动算子被调用时,才会真正开始计算并生成结果。

Driver在什么时候运行?

要理解Driver的运行时机,首先需要明确Driver在分布式计算中的角色。Driver是用户程序的入口点,也是整个任务调度的核心组件。当用户通过命令行或API提交一个Spark应用时,Driver会被启动。它会解析用户定义的RDD操作,构建DAG(有向无环图),然后将任务分配给Executor执行。Driver贯穿于整个应用的执行过程,直到所有任务完成且结果被收集后才会停止。

  • Driver的主要职责:Driver负责解析用户代码、构建逻辑执行计划、生成物理执行计划以及监控和管理任务的执行状态。

Hadoop的文件存储格式有哪些?

Hadoop支持多种文件存储格式,常见的有TextFile(纯文本格式)、SequenceFile(序列文件格式)、Avro、Parquet、ORC等。每种格式适用于不同的场景和需求。

  1. TextFile:这是最简单的文件格式,数据以纯文本形式存储。易于理解和操作,但压缩效率低,不适合大规模数据存储。例如,CSV、JSON等常见文件都可以以TextFile的形式存储在HDFS中。
    • 优点:简单易用,可直接通过文本编辑器查看内容。
    • 缺点:无压缩或压缩效果差,占用空间大。
  1. SequenceFile:是一种二进制文件格式,用于存储键值对。它支持压缩,适合用于MapReduce作业中的中间数据存储。
  • -优点:支持压缩,适合存储大量小文件。
    • 缺点:相对于其他格式,查询性能较差。
  1. Avro:一种高效的序列化系统,支持模式演化,具有良好的跨语言兼容性。Avro文件通常包含一个模式(Schema)和数据部分。
    • 优点:支持模式演化,便于扩展,压缩率高。
    • 缺点:需要额外维护Schema信息。
  1. Parquet:一种列式存储文件格式,专为分析型查询设计,能够显著提高查询效率并减少I/O开销。
    • 优点:列式存储结构使得查询特定列时可以跳过其他列的数据,压缩率高。
    • 缺点:写入速度较慢,不适合频繁更新的场景。
  1. ORC:类似于Parquet,也是一种列式存储格式,但在某些方面进行了优化,如支持复杂的嵌套数据类型。
    • 优点:支持复杂的嵌套数据类型,查询性能优异。
    • 缺点:与Parquet类似,写入速度较慢。

选择依据:如果需要存储日志或简单文本数据,可以选择TextFile;如果需要高效存储键值对数据,SequenceFile是一个不错的选择;对于大数据分析场景,Parquet和ORC更为合适;而Avro则适合需要模式演化的场景。

MySQL的ACID特性是什么?

  1. 原子性(Atomicity):原子性表示一个事务要么完全执行,要么完全不执行。如果事务的一部分失败,则整个事务将回滚到初始状态。这是通过MySQL的InnoDB存储引擎中的回滚日志(undo log)来实现的。当事务开始时,所有更改首先记录在undo log中,只有当事务成功提交后,这些更改才会被永久写入磁盘。
  2. 一致性(Consistency):一致性确保事务执行前后数据库都处于一致的状态。这意味着事务不能破坏数据库的约束条件(如外键约束、唯一性约束等)。例如,如果一条记录插入违反了某个外键约束,那么整个事务将会被终止并回滚。这种一致性检查由数据库管理系统自动完成。
  3. 隔离性(Isolation):隔离性保证了多个事务同时运行时不会互相干扰。MySQL通过不同的隔离级别来控制事务之间的可见性。标准的隔离级别包括读未提交(Read Uncommitted)读已提交(Read Committed)可重复读(Repeatable Read)和串行化(Serializable)。InnoDB默认使用可重复读隔离级别,它利用MVCC(多版本并发控制)来实现高效的并发访问。
  4. 持久性(Durability):持久性意味着一旦事务提交,其结果就会被永久保存,即使系统出现故障也不会丢失。这通常通过重做日志(redo log)来实现。在事务提交之前,所有的修改都会被记录到redo log中,以便在系统崩溃后可以恢复这些更改。

底层原理:InnoDB存储引擎实现了ACID特性。它使用WAL(Write-Ahead Logging)技术来提高性能,即在数据真正写入磁盘之前,先将更改记录到日志文件中。此外,InnoDB还支持MVCC,允许读操作无需加锁,从而提高了并发性能。

MySQL的四种隔离级别分别是什么?

  1. 读未提交(Read Uncommitted):这是最低的隔离级别,在这种模式下,一个事务可以看到其他未提交事务的更改。这意味着可能会出现脏读(Dirty Read),即读取到其他事务尚未提交的数据。
  2. 读已提交(Read Committed):在这种隔离级别下,一个事务只能看到其他事务已经提交的数据。因此,它避免了脏读的问题,但可能会出现不可重复读(Non-repeatable Read),即同一个事务中多次读取同一数据可能得到不同的结果。
  3. 可重复读(Repeatable Read):这是MySQL的默认隔离级别。在这种模式下,保证了同一个事务中多次读取相同的数据会得到相同的结果,避免了不可重复读的问题。然而,幻读(Phantom Read)仍然可能发生,即在同一事务中,两次查询之间由于其他事务插入或删除记录而导致结果集不同。
  4. 串行化(Serializable):这是最高的隔离级别,通过强制事务串行执行来避免所有并发问题,包括脏读、不可重复读和幻读。这种方式通过加锁机制实现,性能开销较大,通常用于对数据一致性要求极高的场景。