大数据初级总结

Linux

一、说 10 个常用的 Linux 命令

ls: 列出目录内容,相当于Windows的dir命令。

cd: 切换目录,相当于Windows的cd命令。

pwd: 显示当前目录的路径。

mkdir: 创建目录,相当于Windows的md命令。

rm: 删除文件和目录,相当于Windows的del命令。

cp: 复制文件和目录,相当于Windows的copy命令。

mv: 移动或重命名文件和目录。

cat: 显示文件内容。

echo: 显示文字信息。

man: 获取Linux命令的帮助信息,类似Windows的help命令

- touch: 创建一个空文件。
- chmod: 改变文件或目录的权限。 
- grep: 在文件中搜索文本。
- sudo: 以超级用户身份运行命令。
- apt: Debian/Ubuntu系统中的软件包管理工具。
- yum: RedHat/CentOS系统中的软件包管理工具。
- df: 显示磁盘空间使用情况。
- du: 统计文件和目录的磁盘使用空间。
- ping: 测试主机之间网络连通性。
- ifconfig: 配置和显示Linux网络接口信息。
- ssh: 通过SSH协议登录远程主机。
- tar: 打包和压缩文件。

二、Linux 系统中创建用户,用户组的命令,赋权命令

1. 创建用户:

bash
sudo adduser name  # 创建一个名为name的用户

2. 创建用户组:

bash
sudo groupadd group_name  # 创建一个名为group_name的用户组

3. 将用户添加到用户组:

bash
sudo usermod -a -G group_name name  # 将用户name添加到group_name用户组

4. 删除用户:

bash 
sudo deluser name  # 删除名为name的用户

5. 删除用户组:

bash
sudo groupdel group_name  # 删除名为group_name的用户组

6. 设置用户密码:

bash
sudo passwd name  # 为用户name设置或修改密码

7. 修改用户和用户组信息:

bash 
sudo usermod options name   # 修改用户name的信息
sudo groupmod options group_name  # 修改用户组group_name的信息

8. 改变文件或目录权限:

bash
sudo chmod options filename  # 改变 filename的权限

9. 改变文件或目录所有者:

bash
sudo chown user:group filename  # 将filename 的所有者改为user,用户组改为group

1. 创建用户(useradd):
用法: useradd [选项] 用户名
示例: useradd john
2. 创建用户组(groupadd):
用法: groupadd [选项] 组名
示例: groupadd developers
3. 设置用户密码(passwd):
用法: passwd 用户名
示例: passwd john
4. 将用户添加到用户组(usermod):
用法: usermod -aG 组名 用户名
示例: usermod -aG developers john
5. 修改用户组(usermod):
用法: usermod -g 新组名 用户名
示例: usermod -g developers john
6. 赋予文件/目录权限(chmod):
用法: chmod [选项] 权限 文件/目录
示例: chmod u+rwx file.txt (赋予用户读、写、执行权限)
7. 更改文件/目录所有者(chown):
用法: chown [选项] 新所有者 文件/目录
示例: chown john file.txt
8. 更改文件/目录所属用户组(chgrp):
用法: chgrp [选项] 新用户组 文件/目录
示例: chgrp developers file.txt

三、用户目录在哪,环境变量有几种配置方式

1. 用户目录:

- 登录用户的主目录保存在环境变量HOME中,通常为/home/username。
- root用户的主目录为/root。
- 每个用户都有自己的主目录,用于存储个人文件、配置等。

2. 环境变量:

环境变量用于存储一些系统配置和信息,主要有以下几种配置方式:

- /etc/profile:系统的环境变量配置文件,对所有用户生效。
/.bash_profile或/.bashrc:个人环境变量配置文件,只对当前用户生效。
- /etc/environment:环境变量配置文件,对所有用户生效。
- export命令:用于设置临时环境变量,例如export PATH=$PATH:/home/john。   
- /etc/sysctl.conf:内核参数配置文件,可定义环境变量。   
- /proc文件系统: /proc/sys中包含很多内核变量,读写它们也可以定义环境变量。

常见的环境变量有:

- PATH:查找可执行文件的路径列表。
- HOME:当前用户主目录的路径。
- USER:当前用户名。
- SHELL:当前用户Shell类型。
- LANG:系统语言环境名称。
- LD_LIBRARY_PATH:查找动态链接库文件的路径列表。

查看环境变量可以使用以下命令:

bash
env  # 显示全部环境变量
echo $VAR_NAME  # 显示VAR_NAME变量的值 

 environment和.bash_profile是设置环境变量的最常用方式。需要修改这两个文件,并执行source 命令或重新登录使之生效。

//

linux用户目录在“/home”中,home目录下面存放的是非管理员用户的文件,每一个文件代表每一个用
户的区域,而linux管理员目录则是存放在“/root”中
关于环境变量的配置方式,Linux系统提供了多种方式来设置和配置环境变量,其中常见的包括:
1.通过用户配置文件:每个用户可以在自己的用户目录下的特定文件中配置环境变量。对于Bash
shell,通常使用 .bashrc 或 .bash_profile 文件,对于其他Shell可能使用不同的文件。可以在这些文件中使用 export 命令来设置环境变量,例如:

export PATH=/usr/local/bin:$PATH
export JAVA_HOME=/usr/lib/jvm/java-11


这样设置的环境变量仅对当前用户生效。
2.全局环境变量配置文件:系统范围的环境变量可以在全局配置文件中进行设置,对所有用户生效。常见的全局配置文件包括 /etc/profile 和 /etc/environment 。在这些文件中添加 export 语句来设置环境变量。
3.在命令行中临时设置环境变量:可以在命令行中直接设置环境变量,仅对当前会话生效。例如:

export PATH=/usr/local/bin:$PATH

这种方式设置的环境变量在当前会话结束后就会失效。
4.使用 export 命令临时设置环境变量:可以使用 export 命令在当前Shell会话中临时设置环境变量,仅对当前会话有效。例如:

export MY_VAR=value


这种方式设置的环境变量在当前会话结束后会被清除。
总的来说,根据使用的范围和持久性,环境变量可以通过用户配置文件、全局配置文件、命令行临时设置或使用 export 命令进行临时设置。具体选择哪种方式取决于需求和使用场景

四、Linux 安装软件的方式有几种,分别什么区别

软件的安装方式大致分为以下几种:
1.使用安装包安装。
        RedHat/CentOS: rpm
        Debain/Ubuntu: deb
        Windows: .exe , .msi
        macOS: dmg
        优点:简单易用、自动处理依赖关系、软件包版本管理、易于升级和卸载。
2.绿色版软件,解压即安装,解压就可以使用。
        优点:易于安装、更新和卸载、软件包版本管理。
        缺点:软件仓库可能不包含最新版本的软件、依赖关系处理有限。
3.使用yum 命令安装,例如yum install wget 。
        优点:提供了自定义的安装选项、适用于特定软件。
        缺点:依赖于软件的特定安装程序、更新和卸载可能需要额外步骤。
4.使用源码编译安装,例如:Redis、Nginx 等。
        优点:可以获取最新版本的软件、灵活性高、可定制性强。
        缺点:安装过程相对复杂、需要手动解决依赖关系、升级和卸载不方便。

五、如何选择 Linux 操作系统版本

1. 用途和需求:首先要考虑你的用途和需求是什么。不同的Linux发行版针对不同的用途和用户群体进行了优化和定位。例如,某些发行版适合服务器环境,而其他发行版则更适合桌面用户或开发
者。
2. 稳定性和支持:对于生产环境或企业级应用,通常建议选择稳定且得到广泛支持的Linux发行版。这些发行版经过长时间的测试和稳定性验证,并且有活跃的社区支持和更新。
3. 硬件兼容性:确保所选的Linux发行版与你的硬件兼容。某些发行版可能对特定硬件驱动支持不完整,因此在选择之前请查看硬件兼容列表或进行相应的研究。
4. 用户友好性:考虑你对Linux系统的熟悉程度和技术水平。如果你是初学者或对Linux不太熟悉,
可以选择一些易于使用且提供友好用户界面的发行版。
5. 软件生态系统:考虑所选Linux发行版的软件生态系统和软件包管理器。一些发行版提供了广泛的软件仓库和易于使用的包管理器,使你能够轻松安装和更新软件。
6. 安全性:关注所选Linux发行版的安全性和漏洞修复机制。一些发行版对安全性有着更高的关注,并且定期发布安全更新。
7. 社区和文档支持:选择拥有活跃的社区和丰富的文档支持的Linux发行版。这将有助于你在使用过程中获取支持、解决问题和学习

六、Shell 脚本第一行是什么,运行 Shell 脚本的方式有哪些,有什么区别

使用bash作为解释器的Shell脚本的第一行通常是 #!/bin/bash 。
运行Shell脚本的方式有以下几种:
1. 直接执行:
在命令行中直接输入脚本文件的路径,例如: ./script.sh (前提是脚本文件具有执行权限)。
2. 使用解释器执行:
在命令行中使用解释器来执行脚本,例如: bash script.sh 或 sh script.sh 。
3. 设置脚本为可执行并添加到PATH环境变量:
将脚本设置为可执行权限( chmod +x script.sh ),然后将脚本文件放置在PATH环境变量包含的目录中。这样就可以在命令行中直接输入脚本名称来执行。
4. 作为命令的参数传递给解释器:
在命令行中使用解释器作为参数来执行脚本,例如: bash -c "script.sh" 。

这些方式在执行脚本时有一些区别:
        直接执行和使用解释器执行方式需要指定脚本的路径,适用于临时执行或脚本存放在当前工作目录中的情况。
        设置脚本为可执行并添加到PATH环境变量的方式可以使脚本在任何目录下都可以直接执行,适用于经常使用的脚本。
        作为命令的参数传递给解释器的方式可以在不创建临时脚本文件的情况下执行一行或简短的脚本内容。
根据具体需求和使用场景,选择适合的运行方式即可。

ZooKeeper

一、说说 ZooKeeper 是什么

ZooKeeper是一个分布式协调服务,用于构建高可用、可靠的分布式系统。它提供了一组简单且健壮的原语,使得开发人员可以在分布式环境中实现协调、同步和配置管理等功能。
ZooKeeper的设计目标是提供高性能和强一致性。它通过在集群中的多个节点之间复制数据来实现容错性和可用性。当其中一个节点出现故障时,其他节点可以继续提供服务。
ZooKeeper的核心概念是Znode,它类似于文件系统中的节点,每个Znode都可以存储一些数据。
ZooKeeper提供了一组API,允许开发人员对Znode进行创建、读取、更新和删除操作。通过这些API,应用程序可以使用ZooKeeper来实现分布式锁、选举、通知等功能。
ZooKeeper的特点包括:
        1. 简单易用:提供了简洁的API和操作接口,易于理解和使用。
        2. 高性能:采用内存数据模型和异步通信机制,具有较高的性能和吞吐量。
        3. 强一致性:保证数据在整个集群中的一致性,所有节点都能看到相同的数据视图。
        4. 可靠性:通过数据复制和容错机制,提供了高可用性和可靠性。
        5. 扩展性:支持集群模式,可以通过添加更多的节点来扩展容量和吞吐量。
总之,ZooKeeper是一个可靠的分布式协调服务,为构建分布式系统提供了必要的基础设施和工具。它在很多大规模分布式系统中被广泛使用,如Hadoop、Kafka、HBase等。
ZooKeeper 具有以下特性:
        顺序一致性:所有客户端看到的服务端数据模型都是一致的;从一个客户端发起的事务请求,最终都会严格按照其发起顺序被应用到 ZooKeeper 中。具体的实现可见下文:原子广播。
        原子性:所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的,即整个集群要么都成功应用了某个事务,要么都没有应用。实现方式可见下文:事务。
        单一视图:无论客户端连接的是哪个 Zookeeper 服务器,其看到的服务端数据模型都是一致的。
        高性能:ZooKeeper 将数据全量存储在内存中,所以其性能很高。需要注意的是:由于 ZooKeeper 的所有更新和删除都是基于事务的,因此 ZooKeeper 在读多写少的应用场景中有性能表现较好,如果写操作频繁,性能会大大下滑。
        高可用:ZooKeeper 的高可用是基于副本机制实现的,此外 ZooKeeper 支持故障恢复,可见下文:选举 Leader。
https://blog.csdn.net/qq_34417408/article/details/124319778

二、ZooKeeper 集群中有哪些角色,分别有什么作用

1. Leader(领导者):
        选举:Leader负责协调和执行集群中的Leader选举过程。
        客户端请求处理:Leader负责处理所有客户端请求,并将结果通知给客户端。
        数据同步:Leader负责将更新操作广播给其他节点,确保集群中的数据一致性。
2. Follower(追随者):
        数据同步:Follower从Leader节点接收并同步更新操作,保持集群数据的一致性。
        客户端请求转发:Follower将客户端请求转发给Leader节点进行处理。
        参与Leader选举:Follower参与Leader选举过程,并根据选举结果进行状态切换。
3. Observer(观察者):
        数据同步:Observer从Leader节点接收并同步更新操作,保持集群数据的一致性。
        客户端请求转发:Observer将客户端请求转发给Leader节点进行处理。
        不参与Leader选举:Observer不参与Leader选举过程,只能被指定为Observer角色。
4. Client(客户端):
        向ZooKeeper集群发送请求:客户端通过与集群中的任何一个节点通信,向ZooKeeper集群发送读写请求。
        接收响应:客户端接收来自Leader节点或Follower节点的处理结果。
总体来说,Leader负责管理和协调整个集群的工作,处理客户端请求并保持数据一致性。Follower和Observer则负责接收并同步Leader节点的更新操作,为客户端提供读写服务。客户端通过与集群中的任何节点通信,与ZooKeeper集群进行交互。

三、说说 ZooKeeper Znode 的特点与监听机制

ZooKeeper中的Znode是ZooKeeper数据模型中的基本节点,它具有以下特点:
        1. 层次性:Znode以层次结构进行组织,类似于文件系统的目录结构,可以通过路径唯一标识每个Znode。
        2. 轻量级:Znode本身很小,通常只包含路径、数据和状态等基本信息。
        3. 有序性:Znode在创建时会被分配一个全局唯一的、带有顺序的标识,可以用于实现有序性的操作。
        4. 可监视性:ZooKeeper提供了监听机制,允许客户端在Znode上注册监听器,以便在Znode的状态发生变化时收到通知。


ZooKeeper的监听机制是其重要的特性之一,它允许客户端注册对Znode的监听器,以便在Znode的数据或状态发生变化时得到通知。监听机制具有以下特点:
        1. 单次触发:一旦监听器被触发,它只会通知一次,而不会持续监听。因此,客户端需要重新注册监听器以继续监听后续的变化。
        2. 数据和子节点监听:可以注册对Znode数据的变化进行监听,也可以注册对子节点的变化进行监听。
        3. 顺序通知:ZooKeeper保证在多个监听器中按照注册顺序触发通知,以确保有序性。

通过监听机制,客户端可以实时获取到Znode的变化,从而实现对分布式系统的实时感知和响应。例如,当某个配置信息发生变化时,客户端可以注册监听器,在配置节点上接收通知,并及时更新自己的配置,从而实现动态配置的管理和同步。

四、说一下 CAP 原则以及如何选择

CAP原则,也称为CAP定理,是分布式系统设计中的重要原则,它指出在一个分布式系统中,一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三个属性无法同时满足,只能在其中选择满足两个属性。
具体来说:
        1. 一致性(Consistency):在分布式系统中,一致性表示多个副本之间的数据保持一致,即对系统进行读取操作时,无论访问哪个节点,都能获取到最新的数据。
        2. 可用性(Availability):可用性表示系统能够对外提供正常的服务,即系统在面对请求时能够及时响应并返回正确的结果。
        3. 分区容错性(Partition tolerance):分区容错性表示系统能够容忍网络分区(节点之间的通信中断)的情况,即使系统中的一些节点无法通信,仍然可以继续运行。

根据CAP原则,无法同时满足一致性、可用性和分区容错性这三个属性,必须在其中进行选择。

在实际应用中,可以根据具体的业务需求和场景来选择满足的属性:
        1.如果业务对数据一致性要求非常高,可以选择满足一致性和分区容错性,牺牲一部分可用性。这种情况下,系统可能会对一些请求进行阻塞或延迟响应,以保证数据的一致性。(金融系统,股票系统)
        2.如果业务对系统的可用性要求非常高,可以选择满足可用性和分区容错性,牺牲一部分一致性。这种情况下,系统可能会在部分节点之间存在数据的不一致性,但可以保证系统的高可用性和及时响应。(互联网应用、社交媒体平台或者在线游戏平台)
        3.如果业务对网络分区容错性要求非常高,可以选择满足一致性和可用性,牺牲一部分分区容错性。这种情况下,系统可能会在网络分区时出现部分节点无法正常通信,但可以保证数据的一致性和及时响应。(电商平台)

选择CAP原则的依据应该基于具体的业务需求和对系统的优先级排序。不同的应用场景可能对一致性、可用性和分区容错性的要求有所不同,因此需要根据实际情况做出权衡和选择。

五、ZooKeeper 的选举过程

ZooKeeper的选举过程是为了选出一个Leader节点,该Leader节点负责协调和处理客户端的请求。以下是ZooKeeper的选举过程:
        1. 初始化:当一个ZooKeeper集群启动时,所有的节点都处于LOOKING状态,表示它们都希望成为Leader。
        2. 选举发起:每个节点会向集群中的其他节点发送选举通知(election notification)。
        3. 选举协议:节点收到选举通知后,会进入选举协议阶段。节点会生成一个自增的选举编号(epoch)来标识当前的选举轮次。节点将自己的编号和其它节点的编号进行比较。
        4. 选票投票:节点根据一定的规则(例如选举编号、节点ID等)为自己投票,并将选票发送给其他节点。
        5. 选票统计:每个节点收到选票后,会进行统计。如果一个节点收到了超过半数节点的选票,它将成为Leader。否则,进入下一轮选举。
        6. 选举结果通知:当一个节点成为Leader后,它会向其他节点发送选举结果通知(leadernotification)。
        7. 跟随者接受选举结果:收到选举结果通知的节点将更新自己的状态,并开始跟随新选出的Leader节点。
        8. 客户端连接:当Leader节点被选举出来后,客户端可以连接到该节点,并向它发送请求。

需要注意的是,在选举过程中可能发生网络分区或节点故障,这会导致选举过程的中断和重新开始。ZooKeeper采用了一些机制来处理这些情况,如超时设置、选举编号的比较等。
通过选举过程,ZooKeeper能够确保在集群中始终有一个Leader节点负责处理客户端请求,保证了系统的可用性和一致性

选主流程 (第一次选主,集群初始化启动时) :
        1.无脑选议员编号最大的为主 -- 为了快速选主
        2.加入投票机制,票数过半才可以成为主
        3.投票默认投票给议员编号最大的节点
        4.每次选主成功都会有一个朝代的标记
        5.每次选主成功后,其他节点都需要和主进行数据同步
集群运行过程中,主宕机后如何重新选主:
        1.先过滤出和公共 PID 相同的节点
        2.无脑选议员编号最大的为主 一- 为了快速选主
        3.加入投票机制,票数过半才可以成为主
        4.投票默认投票给议员编号最大的节点
        5.每次选主成功都会有一个朝代的标记
        6.每次选主成功后,其他节点都需要和主进行数据同步
奇数台 + 有主模式 + 角色:
        1.默认投票给议员编号最大的节点 -- 快速选主
        2. 选主需要过滤出PID与公共PID相同的节点 -- 确保数据完整
        3. 选主投票机制,票数过半为主 -- 解决出现多主的问题(脑裂问题)
角色:
领导者: 所有提议发起 (客户端的写请求,数据更改的操作) ,广播
跟随者:参与到提议的响应和选主的投票过程中,转发客户端写请求到领导者,响应客户端读请求
观察者: 只响应客户端读请求,转发客户端写请求到领导者,不参与提议的响应和选主的投票过程

Hadoop

一、HDFS 的读取流程

1、与NameNode通信查询元数据,找到文件块所在的DataNode服务器
2、挑选一台DataNode(网络拓扑上的就近原则,如果都一样,则随机挑选一台DataNode)服务器,请求建立socket流
3、DataNode开始发送数据(从磁盘里面读取数据放入流,以packet(一个packet为64kb)为单位来做校验)
4、客户端以packet为单位接收,先在本地缓存,然后写入目标文件

1. 客户端请求:应用程序或客户端向HDFS发起读取请求,指定要读取的文件的路径和偏移量。
2. NameNode查询:客户端首先向NameNode发送读取请求,询问该文件的块位置信息。
NameNode是HDFS的元数据管理节点,它维护了文件系统的元数据信息,包括文件与数据块的映
射关系。
3. NameNode响应:NameNode根据元数据信息,返回包含所需文件块位置的响应给客户端。这些块的位置信息包括数据块所在的DataNode节点的地址。
4. 客户端获取数据块:客户端根据收到的块位置信息,开始与所需数据块所在的DataNode节点建立通信连接。
5. 数据块传输:客户端通过网络与DataNode节点进行数据块的传输。如果所需数据块在多个DataNode上复制了多个副本,客户端可以选择与距离最近或网络延迟最低的DataNode节点进行通信。
6. 数据块读取:客户端从DataNode节点读取所需数据块的内容。数据块以流的形式传输,客户端逐块读取数据。
7. 完成读取:一旦客户端读取完所有所需的数据块,读取过程就完成了。客户端可以对数据进行进一步的处理或返回给应用程序使用。

值得注意的是,HDFS采用数据块划分的方式存储文件,将大文件切分为固定大小的数据块,并将这些数据块分布在不同的DataNode节点上。这种数据块的划分和分布策略使得HDFS可以实现高吞吐量的并行读取,从而提供高效的数据访问能力。
此外,HDFS还支持数据本地性优化,即将计算任务调度到与数据块所在节点相同的节点上执行,以减少数据传输开销,提高读取性能。

二、HDFS 的写入流程

1、跟NameNode通信请求上传文件,NameNode检查目标文件是否已经存在,父目录是否已经存在
2、NameNode返回是否可以上传
3、Client先对文件进行切分,请求第一个block该传输到哪些DataNode服务器上
4、NameNode返回3个DataNode服务器DataNode 1,DataNode 2,DataNode 3
5、Client请求3台中的一台DataNode 1(网络拓扑上的就近原则,如果都一样,则随机挑选一台
(DataNode)上传数据(本质上是一个RPC调用,建立pipeline),DataNode 1收到请求会继续调用DataNode 2,然后DataNode 2调用DataNode 3,将整个pipeline建立完成,然后逐级返回客户端
6、Client开始往DataNode 1上传第一个block(先从磁盘读取数据放到一个本地内存缓存),以pocket为单位。写入的时候DataNode会进行数据校验,它并不是通过一个packet进行一次校验而是以chunk为单位进行校验(512byte)。DataNode 1收到一个packet就会传给DataNode 2,DataNode 2传给DataNode 3,DataNode 1每传一个pocket会放入一个应答队列等待应答
7、当一个block传输完成之后,Client再次请求NameNode上传第二个block的服务器.

1. 客户端请求:应用程序或客户端向HDFS发起写入请求,指定要写入的文件路径和内容。
2. NameNode查询:客户端首先向NameNode发送写入请求,询问该文件的块分配情况以及可用的DataNode节点。
3. 数据块分配:NameNode根据文件的块大小和副本数等配置信息,为文件分配数据块并选择适当数量的DataNode节点作为数据块的副本存储节点。
4. 客户端与DataNode建立连接:客户端根据NameNode返回的数据块位置信息,与所选的DataNode节点建立连接。
5. 数据块传输:客户端将要写入的数据块通过网络传输到所选的DataNode节点。
6. 数据块写入:DataNode接收到数据块后,将其写入本地磁盘,并同时创建所需数量的副本。
7. 副本传播:DataNode将数据块的副本传播到其他指定的DataNode节点上,以实现数据的冗余存储和容错性。
8. 数据块写入确认:DataNode在完成数据块写入和副本传播后,向客户端发送写入确认。
9. 客户端向NameNode报告写入完成:客户端向NameNode报告写入操作已完成,以更新文件的元数据信息。
10. 写入完成:一旦客户端收到NameNode的写入确认,写入过程就完成了。

HDFS的写入流程是一个分布式的过程,涉及到客户端、NameNode和多个DataNode之间的通信和协作。每个步骤的具体细节可能会根据HDFS的实际配置和环境而有所不同。该流程确保了数据的可靠存储和冗余复制,以提供高可用性和容错性。

三、阐述 MapReduce 的计算流程

 

大致流程:

 

 https://blog.csdn.net/qq_43906881/article/details/117887103
详细阐述MapReduce的计算流程如下:
1. 输入数据切分:
        输入数据被切分成多个数据块,每个数据块的大小通常由配置参数确定。
        数据块的切分可以由InputFormat完成,将数据切分成适合并行处理的小块。
2. Map阶段:
        每个Mapper节点接收一个或多个数据块。
        对于每个输入键值对,Mapper节点应用映射函数,将其转换为中间键值对(IntermediateKey-Value pairs)。
        中间键值对不必与输入键值对具有相同的数据类型,但通常是一个键值对的形式。
3. 排序与分组:
        中间键值对根据Key进行排序。
        排序可以在Mapper节点上进行,也可以在MapReduce框架的Shuffle阶段中进行。
        排序后,相同Key的键值对会被分配给同一个Reducer节点。
4. Reduce阶段:
        每个Reducer节点接收一个或多个Mapper节点输出的中间键值对。
        Reduce节点对接收到的中间键值对进行归并和排序,以确保具有相同Key的键值对相邻。
        对于每个归并后的Key值及其对应的一组Values,应用Reduce函数,生成最终的输出键值
对。
5. 输出写入:
        Reduce节点将最终的输出键值对写入目标文件系统或数据库。
        输出可以保存为一个或多个文件,具体保存方式由OutputFormat指定。

整个MapReduce计算流程是由多个Mapper节点和多个Reducer节点协同工作完成的。每个Mapper节点独立处理输入数据的一个部分,将其转换为中间键值对。然后,中间结果根据Key进行排序和分组,并传递给相应的Reducer节点进行归并和最终的计算。最终的输出结果由Reducer节点写入目标存储。
MapReduce的计算流程可以自动处理并行化、容错性和数据局部性等细节,从而简化了大规模数据处理的任务。通过合理的数据切分、映射函数、排序和归并,MapReduce能够有效地处理大量的数据,并利用分布式计算资源提供高性能和可伸缩性。

四、阐述 YARN 集群的工作流程

 YARN(Yet Another Resource Negotiator)是Hadoop的资源管理器,用于管理和调度集群中的计算资源。YARN的工作流程如下:
1. 提交应用程序:
        应用程序开发者通过客户端工具或API将应用程序提交到YARN集群。
        应用程序包括应用程序主体(例如MapReduce、Spark等)和应用程序配置信息。
2. 资源管理器(ResourceManager)接收应用程序提交:
        ResourceManager接收应用程序提交请求,为应用程序分配一个应用程序ID。
        ResourceManager负责全局资源的管理和分配。
3. 应用程序主控节点(ApplicationMaster)启动:
        ResourceManager为应用程序分配资源后,应用程序主控节点(ApplicationMaster)被启
动。
        应用程序主控节点是应用程序特定的组件,负责协调应用程序的执行。
4. 任务分配:
        应用程序主控节点向ResourceManager请求资源,包括容器(Container)。
        ResourceManager根据可用资源情况,为应用程序分配容器。
5. 容器执行任务:
应用程序主控节点将任务分配给容器。
容器是资源的抽象,代表在集群中分配的计算资源,可以是独立的进程或线程。
6. 任务执行:
        容器内的任务开始执行,可以是Map任务、Reduce任务或其他任务类型,根据应用程序类型而定。
        任务会处理输入数据、执行计算逻辑,并生成输出数据。
7. 进度监控和状态更新:
        应用程序主控节点定期向ResourceManager报告任务的进度和状态。
        ResourceManager更新应用程序的状态,并提供给用户或监控工具。
8. 完成任务:
        任务执行完成后,应用程序主控节点将任务的输出数据保存到目标位置。
        应用程序主控节点通知ResourceManager任务已完成。
9.应用程序完成:
        当应用程序所有任务完成后,应用程序主控节点发送完成信号给ResourceManager。
        ResourceManager更新应用程序的状态为完成状态。

YARN的工作流程通过ResourceManager和应用程序主控节点(ApplicationMaster)的协作实现资源的分配和任务的执行。ResourceManager负责全局资源的管理和分配,应用程序主控节点负责协调应用程序的执行过程,并与ResourceManager进行通信。通过这种方式,YARN能够提供高效的资源管理和任务调度,使得大规模集群上的应用程序能够高效地运行。

五、为什么会产生 YARN 它解决了什么问题

https://blog.csdn.net/qq_41544550/article/details/127681920

YARN(Yet Another Resource Negotiator)产生的主要原因是为了解决Hadoop 1.x中的一个问题,即资源管理和作业调度的紧耦合性。在Hadoop 1.x版本中,资源管理和作业调度是由同一个组件JobTracker负责的,这种紧耦合的设计在大规模集群中面临一些挑战和限制,例如可伸缩性、灵活性和多样性方面的问题。
YARN的主要目标是将资源管理和作业调度分离,提供一个通用的资源管理框架,使得Hadoop能够支持更多类型的应用程序,并能够更好地适应不断增长的需求。YARN解决了以下问题:
        1. 资源管理:YARN提供了一个独立的组件ResourceManager,专门负责全局资源的管理和分配。ResourceManager负责接收应用程序的资源请求,并根据可用资源情况进行分配。这种分离的设计使得资源管理更加灵活和可扩展,并能够适应不同类型的应用程序的需求。
        2. 作业调度:YARN引入了应用程序主控节点(ApplicationMaster),它是每个应用程序的专用组件,负责协调应用程序的执行过程。应用程序主控节点向ResourceManager请求资源,并将任务分配给容器进行执行。这种分离的设计使得作业调度更加灵活,可以支持各种类型的应用程序,并能够更好地处理任务的并发和资源利用率的优化。
        3. 多样性支持:YARN的设计使得Hadoop能够支持更多类型的应用程序,不仅限于传统的
MapReduce计算模型。通过将资源管理和作业调度分离,YARN为不同的应用程序提供了灵活的编程模型和执行环境,使得开发者能够更自由地选择适合自己应用程序需求的计算框架和编程模型。

总的来说,YARN的出现解决了Hadoop 1.x中资源管理和作业调度的紧耦合性问题,提供了一个通用的资源管理框架,使得Hadoop能够更好地支持各种类型的应用程序,并能够更好地适应不断增长的需求。YARN的引入为Hadoop生态系统的发展和创新带来了更大的灵活性和可扩展性。

六、Hadoop MR 模型中数据倾斜一般是在 Mapper 端发生的还是在 Reducer 端发生的,为什么

在Hadoop MapReduce模型中,数据倾斜通常是在Reducer端发生的。

数据倾斜是指在数据处理过程中,某些特定的数据分区或键值对导致了不均衡的数据分布。这可能会导致某些Reducer节点处理的数据量远远大于其他节点,从而导致任务执行时间延长,性能下降。

在Mapper端,数据通常被分割为多个数据块,每个Mapper节点独立地处理其分配到的数据块。在这个阶段,数据倾斜的影响相对较小,因为数据已经被分割为小块并在各个节点上并行处理。

然而,在Reducer端,数据会按照键值对进行分组并发送到不同的Reducer节点。如果某些键值对的数量远远超过其他键值对,这就可能导致数据倾斜。当一部分Reducer节点负责处理大量的数据,而其他节点负责处理较少的数据时,负载不均衡会影响整体的性能和执行时间。

数据倾斜在Reducer端发生的主要原因可能包括:
1. 键值对分布不均:某些键值对的数量远远超过其他键值对,导致数据在Reducer节点之间的分布不均衡。
2. 数据分布不均匀:输入数据在某些特定的分区中聚集,使得某些Reducer节点处理的数据量远远大于其他节点。

为了解决数据倾斜问题,可以采取以下一些策略:
1. Combiner函数:在Mapper端使用Combiner函数进行部分数据聚合,减少传输到Reducer的数据
量。
2. 数据重分区:对于数据倾斜严重的情况,可以通过自定义Partitioner函数,将特定的键值对发送到不同的Reducer节点,以实现负载均衡。
3. 聚合操作优化:在Reducer端进行聚合操作时,可以使用合适的聚合策略,如使用
CombineInputFormat、使用Counters等来减少数据传输和聚合操作的开销。
4. 动态调整Reducer数量:根据数据分布情况,动态调整Reducer的数量,使得负载更均衡。

通过上述策略,可以减轻数据倾斜问题的影响,提高Hadoop MapReduce任务的性能和稳定性。

七、Hadoop 常用的压缩算法有哪些,有什么区别

1.Gzip:Gzip是一种通用的压缩算法,它可以将文件压缩为更小的大小以节省存储空间。它的压缩比较高,但压缩和解压缩速度相对较慢。适用于对压缩比要求较高,不太关心处理速度的场景。
        Gzip:Hadoop 内置支持,压缩比高,不支持 Split。
2.Snappy:Snappy是一种快速压缩算法,它的压缩和解压缩速度非常快。虽然它的压缩比不如Gzip高,但它适合对数据进行快速传输,特别是在网络传输和实时处理中表现出色。
        Snappy:压缩比一般,不支持 Split,压缩/解压速度快,支持 Hadoop Native 库,需要自己安装。
3.LZO:LZO是一种高性能的压缩算法,它具有较高的压缩比和快速的压缩解压缩速度。LZO适用于大规模数据处理,可以减小存储空间和加快数据传输速度。
        压缩比一般,支持 Split(需要建索引,文件修改后需要重新建索引),压缩/解压速度快,支持Hadoop Native 库,需要自己安装。
4.Bzip2:Bzip2是一种高压缩比的算法,对于某些数据类型能够实现很高的压缩比。它的压缩比最高,但压缩和解压缩速度较慢。适用于对压缩比要求较高的场景,例如长期存储或带宽有限的环境。
        BZip2:Hadoop 内置支持,压缩比高,支持 Split,支持多文件,缺点就是慢。
5.LZ4:压缩比一般,不支持 Split,压缩/解压速度快,支持 Hadoop Native 库,需要自己安装。
6.Zstd:压缩比高跟 Deflate(Gzip 算法)相当,不支持 Split,压缩/解压速度快,支持 Hadoop Native 库,需要自己安装。

八、Hadoop MR 模型中哪些地方可以进行优化

1. 输入输出格式优化:选择适合的输入输出格式可以显著影响作业的性能。Hadoop提供了多种输入输出格式,如Text、SequenceFile、Avro等。选择合适的格式可以减少数据解析和序列化的开销,并提高作业的执行效率。
2. 分区器优化:分区器决定了键值对如何分发到Reducer任务上。默认情况下,使用哈希分区器,但对于某些场景,可以自定义分区器。通过自定义分区器,可以将具有相同键的键值对发送到同一个Reducer任务,从而提高作业的负载均衡和减少数据倾斜问题。
3. Combiner函数优化:Combiner函数是在Mapper节点上进行的局部聚合操作。它可以减少Mapper输出的数据量,降低网络传输的负载。通过合理使用Combiner函数,可以减少数据传输和Reducer的输入数据量,提高作业的整体性能。
4. 数据压缩优化:选择适当的数据压缩算法和压缩比例可以减少磁盘存储空间和网络传输的开销。Hadoop支持多种压缩算法,如Gzip、Snappy、LZO等。根据数据的特点和性能需求,选择合适的压缩算法可以提高作业的执行效率。
5. 数据本地化优化:数据本地化是指将计算任务分配到存储数据的节点上,避免数据的网络传输。通过合理配置数据块大小、副本数和调度策略,可以最大限度地提高数据本地性,减少数据传输的开销,提高作业的处理速度。
6. 合理设置任务数和资源配置:根据集群的资源情况和作业的需求,合理设置任务数和资源配置。过多的任务数可能导致过度调度和资源竞争,而过少的任务数可能导致资源浪费和作业执行效率低下。根据实际情况进行调优,平衡资源利用和作业执行效率。
7. 使用压缩缓存:压缩缓存可以将中间结果进行压缩和存储,以减少磁盘IO和网络传输的开销。通过使用压缩缓存,可以避免重复计算和数据的重复传输,提高作业的执行效率。
8. 合理设置数据切片大小:数据切片的大小会影响作业的并行度和执行效率。切片过小会导致任务数过多,调度和管理的开销增加;切片过大会导致任务执行时间过长。

九、在MapReduce中如果任务卡住了如何定位问题且怎样解决

  1. 查看日志:查看任务的日志文件,特别是stdout和stderr文件,看是否有报错信息,可以通过Hadoop的Web UI或者命令行工具来查看日志文件。
  2. 检查输入输出:检查任务的输入和输出路径是否正确,是否存在权限问题,是否存在数据格式问题等。
  3. 检查配置:检查任务的配置文件是否正确,特别是hadoop-env.sh和mapred-site.xml等文件,是否存在配置错误或者配置缺失。
  4. 检查资源:检查任务的资源使用情况,特别是内存和磁盘空间,是否存在资源不足的问题。
  5. 检查网络:检查任务运行时的网络情况,特别是节点之间的网络连接是否正常,是否存在网络瓶颈等问题。
  6. 重启任务:如果以上步骤都没有找到问题,可以尝试重启任务,有时候任务会因为某些原因卡住,重启任务可以解决这个问题。 在解决问题时,可以采用以下一些常见的解决方法:
  7. 调整任务的配置参数,例如增加mapreduce.map.memory.mb和mapreduce.reduce.memory.mb等参数,使任务能够更好地利用资源。
  8. 增加任务的并行度,例如增加mapreduce.job.maps和mapreduce.job.reduces等参数,使任务能够更快地完成。
  9. 使用Combiner和Partitioner等技术来优化任务的性能。
  10. 对数据进行预处理,例如对数据进行压缩、格式转换等操作,减少任务的IO开销。
  11. 优化代码逻辑,例如避免使用全局变量、减少数据复制等操作,提高代码的执行效率。

        总之,定位和解决MapReduce任务卡住的问题需要一定的经验和技巧,需要结合具体情况进行分析和处理。

十、在MR中小文件存在的原因及解决方式。

HDFS中小文件问题是指在HDFS中存储大量小文件时,可能会导致存储空间的浪费、读写效率的降低等问题。

以下是几种解决方案:

1. 合并小文件:将多个小文件合并成一个大文件,可以通过Hadoop提供的SequenceFile格式来实现。这样有助于减少NameNode的内存占用和元数据的存储量。

2. 压缩文件:对小文件进行压缩,可以减少存储空间的占用。Hadoop提供了多种压缩算法,例如Gzip、Bzip2、Snappy等。

3. 使用Har文件:Har文件是一种特殊的文件格式,可以将多个小文件打包成一个文件,并提供了目录结构和文件元数据。使用Har文件可以有效地减少存储空间的占用。

4. 使用HBase存储小文件:HBase是一种分布式的NoSQL数据库,可以用来存储小文件。HBase可以通过多个RegionServer并行处理数据,可以提高读写效率。

综上所述,针对HDFS中小文件问题,可以采取多种解决方案,具体选择哪种方案需要考虑数据的特点和实际情况。

OR 在HDFS中,小文件问题是指存储大量小文件会占用过多的NameNode内存,降低系统的性能和可用性。解决小文件问题有以下几种方法:

1. 合并小文件:将小文件合并成一个大文件,可以减少NameNode内存的占用,提高系统的性能和可用性。可以使用Hadoop提供的SequenceFile来合并小文件。

2. 存储小文件集合:将小文件存储为一个集合,可以减少NameNode内存的占用。可以使用Hadoop提供的Har文件格式来存储小文件集合。

3. 数据块化:将小文件分成数据块,可以减少NameNode内存的占用。可以使用Hadoop提供的CombineFileInputFormat类来进行数据块化。

4. 增加NameNode的内存:如果以上方法无法解决问题,可以考虑增加NameNode的内存来提高系统的性能和可用性。

总之,在HDFS中解决小文件问题需要根据具体情况进行选择,可以通过合并小文件、存储小文件集合、数据块化和增加NameNode的内存等方式来提高系统的性能和可用性。

小文件问题的产生原因主要有以下几个方面:

1. 业务需求:某些业务需要存储大量的小文件,如日志文件、配置文件等。

2. 存储方式:某些应用或工具在存储数据时,可能会将数据分成较小的文件进行存储,如Hive的输出文件。

3. 数据源特性:某些数据源本身就是小文件,如图片、音频、视频等。

针对不同的产生原因,可以采取不同的解决方法:

1. 业务需求:对于需要存储大量小文件的业务,可以考虑在业务层面上进行优化,如将多个小文件合并成一个大文件、对小文件进行压缩等。

2. 存储方式:对于某些应用或工具在存储数据时,分成较小的文件进行存储,可以考虑使用更合适的存储格式或设置更大的块大小。

3. 数据源特性:对于一些本身就是小文件的数据源,如图片、音频、视频等,可以使用专门的存储系统进行存储,如HBase、Ceph等。

总之,根据小文件问题产生的原因,可以采取不同的解决方法,如在业务层面上进行优化、使用合适的存储格式或设置更大的块大小、使用专门的存储系统进行存储等,以提高系统的性能和可用性。

从技术层面和业务层面两个方向来解决小文件问题,具体如下:

1. 技术层面:

a. 合并小文件:可以使用Hadoop提供的SequenceFile来合并小文件,减少小文件的数量,从而降低NameNode内存的占用。

b. 压缩小文件:对于小文件,可以考虑使用压缩算法来减小文件的大小,如Gzip、Bzip2等。

c. 数据块化:将小文件分成数据块,可以减少NameNode内存的占用。可以使用Hadoop提供的CombineFileInputFormat类来进行数据块化。

d. 存储小文件集合:可以将小文件存储为一个集合,通过Hadoop提供的Har文件格式进行存储。

2. 业务层面:

a. 规范文件命名和存储:对文件进行规范的命名和存储,可以减少小文件的数量,从而降低NameNode内存的占用。

b. 建立文件分类和目录结构:针对不同类型的文件,建立不同的分类和目录结构,可以使小文件数量减少,从而降低NameNode内存的占用。

c. 建立文件管理流程:制定文件管理流程,包括文件上传、下载、删除、备份等,可以规范文件的使用,从而减少小文件的数量,降低NameNode内存的占用。

总之,从技术层面和业务层面两个方向去解决小文件问题,可以通过合并小文件、压缩小文件、数据块化、存储小文件集合等技术手段来提高系统的性能和可用性,同时也可以通过规范文件命名和存储、建立文件分类和目录结构、建立文件管理流程等业务手段来降低小文件的数量,减少NameNode内存的占用。

总结:

小文件问题是指存储大量小文件会占用过多的NameNode内存,降低系统的性能和可用性,主要有以下几个方面的原因:

1. 业务需求:某些业务需要存储大量的小文件,如日志文件、配置文件等。

2. 存储方式:某些应用或工具在存储数据时,可能会将数据分成较小的文件进行存储,如Hive的输出文件。

3. 数据源特性:某些数据源本身就是小文件,如图片、音频、视频等。

解决小文件问题可以从技术层面和业务层面两个方向入手:

1. 技术层面的解决方案:

a. 合并小文件:使用Hadoop提供的SequenceFile来合并小文件,减少小文件的数量,从而降低NameNode内存的占用。

b. 压缩小文件:对于小文件,可以考虑使用压缩算法来减小文件的大小,如Gzip、Bzip2等。

c. 数据块化:将小文件分成数据块,可以减少NameNode内存的占用。可以使用Hadoop提供的CombineFileInputFormat类来进行数据块化。

d. 存储小文件集合:可以将小文件存储为一个集合,通过Hadoop提供的Har文件格式进行存储。

2. 业务层面的解决方案:

a. 规范文件命名和存储:对文件进行规范的命名和存储,可以减少小文件的数量,从而降低NameNode内存的占用。

b. 建立文件分类和目录结构:针对不同类型的文件,建立不同的分类和目录结构,可以使小文件数量减少,从而降低NameNode内存的占用。

c. 建立文件管理流程:制定文件管理流程,包括文件上传、下载、删除、备份等,可以规范文件的使用,从而减少小文件的数量,降低NameNode内存的占用。

综上所述,解决小文件问题需要综合考虑技术层面和业务层面的因素,采取不同的解决方案。通过合并小文件、压缩小文件、数据块化、存储小文件集合等技术手段来提高系统的性能和可用性,同时也可以通过规范文件命名和存储、建立文件分类和目录结构、建立文件管理流程等业务手段来降低小文件的数量,减少NameNode内存的占用。

 Hive

一、Hive 中 SQL 如何解决数据倾斜

1. Distribute By语句:使用Distribute By语句按照某个字段对数据进行分发。这样可以将相同字段
值的数据发送到同一个Reducer上,减少数据倾斜的可能性。
2. Sort By语句:在Distribute By语句之后使用Sort By语句对数据进行排序。这样可以使数据在
Reducer上更均匀地分布,减少数据倾斜。
3. Cluster By语句:Cluster By语句是Distribute By和Sort By的结合,可以一次性按照指定字段对数据进行分发和排序,更好地处理数据倾斜。
4. 调整Reducer的数量:通过调整Hive配置参数 hive.exec.reducers.max 来增加或减少Reducer
的数量。增加Reducer数量可以提高并行处理的效率和均衡性,减少数据倾斜的影响。
5. 使用多个MapReduce作业:对于严重的数据倾斜情况,可以考虑使用多个MapReduce作业。首
先进行数据预处理,然后再进行实际的数据处理。通过这种方式,可以更好地解决数据倾斜问题。
6. 使用自定义逻辑处理:对于复杂的数据倾斜情况,可以编写自定义的逻辑处理代码。可以使用自定义的MapReduce程序、自定义函数等来根据具体的数据倾斜情况实现更灵活和定制化的解决方案。
这些方法提供了一些常用的技术和策略来解决Hive中的数据倾斜问题。根据具体情况选择合适的方法,以提高数据处理的效率和准确性。

RBO 优化:谓词下推,列裁剪&常量替换
CBO 优化:
JOIN 优化:Map Join,Reduce Join,Bucket Map Join,SMB Join
分区/分桶
https://blog.csdn.net/weixin_51981189/article/details/127419638

二、说一说 Hive 的分区分桶

当使用Hive进行数据处理和查询时,我们经常会遇到数据倾斜的问题,即某些数据分布不均匀,导致查询性能下降。为了解决这个问题,Hive提供了两种优化方式:分区和分桶。
        分区是将数据按照特定的列值进行划分,以实现更快的查询和过滤操作。例如,可以将数据按照日期、地区等特征进行分区。分区的优化方式是选择适合查询频率高的列作为分区列,并在查询时指定特定的分区,从而只扫描所需的数据分区,提高查询性能。
        分桶是将数据按照哈希值分配到多个桶中,实现数据的均匀分布。分桶主要用于随机采样和等值查询,可以减少查询的数据量,提高查询效率。在创建表时,需要选择合适的分桶列,通常是具有较高基数的列。同时,指定分桶的数量,并通过哈希函数将数据分配到不同的桶中。在查询时,通过指定分桶列的值,只查询特定的桶,避免全表扫描。

通过合理地使用分区和分桶,我们可以有效地优化Hive查询性能。分区和分桶的选择取决于具体的业务需求和查询模式。合适的分区和分桶策略可以减少不必要的数据扫描,提高查询效率,尤其适用于大型数据集和复杂查询场景。因此,在设计和配置表结构时,我们应根据实际情况选择合适的分区和分桶策略,以获得最佳的查询性能。

三、Hive SQL 的执行流程

1. 解析和语法分析:Hive接收到用户提交的SQL语句后,首先进行解析和语法分析。在这个步骤中,Hive会检查SQL语句的语法是否正确,并构建相应的语法树。
2. 逻辑计划生成:在逻辑计划生成阶段,Hive将语法树转换为逻辑执行计划。逻辑执行计划是一个
抽象的逻辑操作序列,描述了SQL语句的执行顺序和操作。
3. 物理计划生成:在物理计划生成阶段,Hive将逻辑执行计划转换为物理执行计划。物理执行计划
定义了具体的物理操作和数据流,以实现SQL语句的执行。
4. 查询优化:在查询优化阶段,Hive对物理执行计划进行优化,以提高查询性能。这包括重新排序
操作、选择合适的连接算法、消除冗余计算等优化技术。
5. 任务划分和调度:在任务划分和调度阶段,Hive将物理执行计划划分为一系列任务,并将这些任
务分配给可用的执行节点。任务调度器负责将任务分配给节点,并管理任务的执行顺序和并发度。
6. 数据读取和处理:在任务执行阶段,每个任务在分配的执行节点上读取数据,并按照物理执行计划中定义的操作进行处理。这包括数据的读取、过滤、连接、聚合等操作。
7. 结果返回和输出:当所有任务完成后,Hive将计算结果从各个节点收集回来,并将结果返回给用
户或写入到指定的输出位置。

需要注意的是,Hive SQL的执行过程中,会根据数据的存储格式、表的分区和分桶等特性进行优化,以提高查询性能和效率。此外,Hive还支持基于Tez、Spark等执行引擎,可以进一步提升查询的执行速度和并行处理能力。

四、说一说你知道的 Hive 优化

一. 表连接优化
1. 将大表放后头
        Hive假定查询中最后的一个表是大表。它会将其它表缓存起来,然后扫描最后那个表。因此通常需要将小表放前面,或者标记哪张表是大表:/*streamtable(table_name) */
2. 使用相同的连接键
        当对3个或者更多个表进行join连接时,如果每个on子句都使用相同的连接键的话,那么只会产生一个MapReduce job。
3. 尽量尽早地过滤数据
        减少每个阶段的数据量,对于分区表要加分区,同时只选择需要使用到的字段。
4. 尽量原子化操作
        尽量避免一个SQL包含复杂逻辑,可以使用中间表来完成复杂的逻辑
二.用insert into替换union all (SQL优化)
如果union all的部分个数大于2,或者每个union部分数据量大,应该拆成多个insert into 语句,实际测试过程中,执行时间能提升50%。
三. order by & sort by
order by : 对查询结果进行全局排序消耗时间长,需要set hive.mapred.mode=nostrict
sort by : 局部排序,并非全局有序,提高效率。
四. transform+python
一种嵌入在hive取数流程中的自定义函数,通过transform语句可以把在hive中不方便实现的功能在
python中实现,然后写入hive表中
五. limit 语句快速出结果
一般情况下,Limit语句还是需要执行整个查询语句,然后再返回部分结果。有一个配置属性可以开启,避免这种情况—对数据源进行抽样
缺点:有可能部分数据永远不会被处理到
六. 本地模式
对于小数据集,为查询触发执行任务消耗的时间>实际执行job的时间,因此可以通过本地模式,在单台机器上(或某些时候在单个进程上)处理所有的任务。
七. 并行执行
Hive会将一个查询转化为一个或多个阶段,包括:MapReduce阶段、抽样阶段、合并阶段、limit阶段等。默认情况下,一次只执行一个阶段。 不过,如果某些阶段不是互相依赖,是可以并行执行的。
八. 调整mapper和reducer的个数
九. 严格模式
十. 数据倾斜
表现:
任务进度长时间维持在99%(或100%),查看任务监控页面,发现只有少量(1个或几个)reduce子任务未完成。因为其处理的数据量和其他reduce差异过大。单一reduce的记录数与平均记录数差异过大,通常可能达到3倍甚至更多。 最长时长远大于平均时长。
原因:

(1)key分布不均匀

(2)业务数据本身的特性

(3)建表时考虑不周

(4)某些SQL语句本身就有数据倾斜

解决方案:参数调节

五、Hive 和传统数据库的区别

hive是基于Hadoop的一个数据仓库工具,用来进行数据提取、转化、加载,这是一种可以存储、查询和分析存储在Hadoop中的大规模数据的机制。
1.查询语言。专门针对Hive的特性设计了类SQL的查询语言HQL。。
2.数据存储。传统数据库则可以将数据保存在块设备或者本地文件系统中。Hive 是建立在 Hadoop之上的,所有 Hive的数据都是存储在 HDFS中的。
3.数据格式。传统数据库数据格式由系统决定,而Hive 中没有定义专门的数据格式,数据格式可以由用户指定。
4.数据更改。传统数据库中的数据通常是需要经常进行修改的。而Hive中不支持对数据的改写和添加,所有的数据都是在加载的时候中确定好的。
5.执行引擎。传统数据库通常有自己的执行引擎。Hive 中大多数查询的执行是通过 Hadoop提供的
MapReduce来实现的。
6.特性。传统数据库执行延迟低、可扩展性低、数据规模小,而Hive执行延迟高、可扩展性高、数据规模大

六、什么是物化视图和视图有什么区别

物化视图:查询重写
物化视图的查询重写(Query Rewrite)是指数据库系统在执行查询时,根据物化视图的定义和查询语句的结构,自动替换查询语句中的部分或全部内容,将其转化为对物化视图的查询操作,从而加速查询执行的过程。
通过查询重写,数据库系统能够利用物化视图的计算结果,避免了对底层数据的重复计算,提高了查询的执行效率。这种自动的查询优化过程可以显著减少查询的响应时间,提升系统性能,并降低了数据库系统的负载。
需要注意的是,查询重写并非适用于所有情况。它要求物化视图的定义与查询语句的结构匹配,并且需要确保物化视图的数据与底层数据的一致性。此外,查询重写的可行性还受限于数据库系统的实现和配置。因此,在使用物化视图进行查询优化时,需要仔细考虑物化视图的定义、查询语句的结构和数据库系统的支持程度,以确保查询重写能够发挥最佳效果。

物化视图(Materialized View)是一种预先计算和存储的数据视图,它将查询的结果以物理形式存储在磁盘上,以提高查询性能。与普通视图不同,物化视图中的数据是实际存储的,而不是在每次查询时动态计算的。
视图(View)是一个虚拟表,它是根据查询定义的一组结果数据。视图本身并不存储数据,而是在查询时动态生成结果。视图可以简化复杂的查询操作,隐藏数据的细节,提供更简洁和易于理解的查询接口。
区别:
1. 存储方式:物化视图将查询的结果存储在磁盘上,而视图不存储任何数据,只是定义了一组查询结果。
2. 数据计算:物化视图中的数据是预先计算和存储的,因此查询物化视图时不需要重新计算;而视图在每次查询时会动态计算结果。
3. 查询性能:由于物化视图已经预先计算和存储了结果数据,所以在查询时可以获得更快的响应速
度,尤其适用于复杂的查询操作或聚合计算;而视图每次查询都需要动态计算结果,可能会导致性
能较低。
4. 数据一致性:由于物化视图中的数据是预先计算的,所以在原始数据发生变化时,需要保证物化视图的数据与源数据保持一致;而视图则是动态计算的,每次查询都会实时从源数据中获取最新的结果。
总体而言,物化视图通过提前计算和存储结果数据来优化查询性能,适用于对查询性能要求较高,且数据更新频率较低的场景;而视图则是动态生成结果,适用于查询较简单、数据更新频率较高的场景。

七、Hive 内部表和外部表的区别

1. 存储位置:内部表的数据存储在Hive默认指定的存储位置中,通常是在HDFS上的一个指定目录。而外部表的数据存储在用户自定义的位置,可以是HDFS上的任意路径或者本地文件系统中的路径。
2. 数据管理:内部表的数据由Hive完全管理,包括数据的创建、删除和清理。外部表的数据则由用户自行管理,Hive仅对元数据进行管理,不会对数据进行删除或清理操作。
3. 数据持久性:内部表的数据是持久的,即当表被删除时,其数据也会被删除。而外部表的数据是非持久的,即当表被删除时,其数据仍然保留在存储位置中。
4. 数据导入:对于内部表,可以使用Hive的加载命令或INSERT语句将数据导入表中。而外部表可以通过直接将数据文件放置在指定位置来导入数据,也可以使用LOAD命令将数据加载到表中。
5. 数据丢失:对于内部表,如果数据文件被意外删除或损坏,数据将会丢失。而外部表在数据文件被删除或损坏时,可以重新导入数据或将数据文件还原到指定位置来恢复数据。

总的来说,内部表适用于Hive完全管理数据的情况,它提供了更高的数据一致性和可靠性。而外部表适用于需要与其他系统共享数据或希望直接访问数据文件的情况,它提供了更大的灵活性和数据自主性。选择内部表还是外部表取决于数据的使用方式、管理需求和数据的持久性要求。

八、Hive 排序的几个 BY 的具体使用与区别

1. ORDER BY : 使用 ORDER BY 子句对查询结果进行排序。语法如下:

SELECT * FROM table_name ORDER BY column_name [ASC|DESC];


ORDER BY 子句默认按升序排序,可以通过添加 ASC (默认)或 DESC 关键字来指定升序或降序排序。
2. DISTRIBUTE BY : 使用 DISTRIBUTE BY 子句将数据分发到不同的Reducer任务中。语法如下:

SELECT * FROM table_name DISTRIBUTE BY column_name;


DISTRIBUTE BY 子句根据指定的列对数据进行哈希分发,确保具有相同值的数据被发送到同一个
Reducer任务进行处理。
3. CLUSTER BY : 使用 CLUSTER BY 子句将数据按指定列进行排序和分区。语法如下:

SELECT * FROM table_name CLUSTER BY column_name;


CLUSTER BY 子句首先对数据按指定列进行排序,然后将具有相同值的数据放在同一组中。这样可以更有效地执行后续的聚合操作。
4. SORT BY : 使用 SORT BY 子句对结果进行排序,但不保证分区和全局排序。语法如下:

SELECT * FROM table_name SORT BY column_name [ASC|DESC];


SORT BY 子句对结果进行排序,但不会显式地保证分区和全局排序。在Hive中,当使用 SORT BY时,数据会先按指定列排序,然后每个Reducer任务会按照排序结果处理自己的数据。

需要注意的是, ORDER BY 、 DISTRIBUTE BY 和 SORT BY 可以组合使用,以达到更复杂的排序和分区需求。它们的具体使用方式取决于查询的要求和预期的结果。

九、Hive 自定义 UDF 函数的流程,以及为什么要自定义

自定义UDF(User-Defined Function)函数是为了在Hive中扩展其功能,以满足特定的业务需求。下面是自定义UDF函数的一般流程:
1. 编写UDF函数的代码:使用Java编写UDF函数的逻辑,可以通过继承Hive提供的UDF基类或实现相关接口来定义自定义函数。在代码中,需要定义输入参数和返回值类型,并实现具体的逻辑操作。
2. 编译UDF函数代码:将编写好的UDF代码进行编译,生成可执行的字节码文件(.jar文件)或编译后的类文件。
3. 部署UDF函数:将编译后的UDF函数部署到Hive服务器所在的机器上,可以将生成的.jar文件放置在Hive的UDF目录下或指定的类路径中。
4. 注册UDF函数:在Hive中注册自定义UDF函数,使其能够在Hive查询中使用。可以通过 CREATE FUNCTION 语句或在Hive配置文件中进行注册。
5. 使用UDF函数:在Hive查询中可以像使用内置函数一样使用自定义UDF函数。通过在查询中调用自定义函数,并传递相应的参数,可以对数据进行特定的处理和计算。

自定义UDF函数的优势在于可以满足特定的业务需求,增加了Hive的灵活性和扩展性。通过自定义UDF函数,可以实现一些Hive内置函数不支持的特定逻辑和计算,扩展了Hive的功能。例如,可以自定义字符串处理函数、日期函数、数学函数等,以满足特定业务场景下的数据处理需求。

此外,自定义UDF函数还可以提高查询的性能和效率。通过编写自定义函数,可以针对特定的数据处理需求进行优化,减少不必要的数据转换和计算步骤,提高查询的执行效率和速度。

总之,自定义UDF函数能够满足特定的业务需求,并且能够提高查询性能,使Hive更加灵活和强大。

十、简述 Hive 支持的写出文件的格式与区别

Hive支持多种写出文件的格式,每种格式都有不同的特点和适用场景。下面是一些Hive支持的常见写出文件格式及其区别:
1. 文本文件格式(TextFile):以文本形式存储数据,每条记录占用一行。文本文件格式是通用的格式,易于阅读和处理,但不适用于大规模数据存储和高性能需求。
2. 序列文件格式(SequenceFile):以二进制形式存储数据,具有压缩和高效的序列化特性。序列文件格式适用于大规模数据存储和高性能读写操作,可用于MapReduce作业的输入和输出。
3. Avro文件格式(Avro):使用Avro数据序列化系统进行编码的数据文件格式。Avro文件格式支持动态数据类型、架构演化和数据压缩,适用于复杂数据结构和数据格式变化频繁的场景。
4. Parquet文件格式(Parquet):一种列式存储格式,将数据按列存储,提供高效的数据压缩和查
询性能。Parquet文件格式适用于大规模数据存储和高性能分析查询。
5. ORC文件格式(ORC):一种列式存储格式,具有高压缩率和高性能的查询特性。ORC文件格式适用于大规模数据存储和高性能分析查询,通常比其他格式占用更少的存储空间。

每种文件格式都有其适用的场景和优势。选择文件格式时,需要考虑数据的规模、查询性能需求、数据结构复杂性和数据格式变化频率等因素。在特定的应用场景下,选择合适的文件格式可以提高查询性能、降低存储成本,并提供更好的数据管理和分析能力。

十一、行式文件和列式文件的区别

行式文件和列式文件是两种常见的数据存储格式,它们在数据的组织方式上有所不同,具有不同的特点和适用场景。
行式文件(Row-oriented):
        行式文件以行为单位存储数据,每行包含一个完整的记录。所有字段按照记录的顺序存储在一起,相邻记录的相同字段值通常是连续存储的。
        优点:行式文件适合整行数据的读取和写入操作,适用于事务处理和实时查询。由于记录存储在一起,读取单个记录的开销较低,适合于顺序访问。
        缺点:当查询需要涉及大量字段时,行式文件的查询性能可能较低,因为需要读取大量不必要的字段数据。此外,由于每行数据的长度不同,可能导致存储空间的浪费。

列式文件(Column-oriented):
        列式文件以列为单位存储数据,每列包含同一类型的数据,相同列的数据存储在一起。不同记录的同一字段值通常是连续存储的。
        优点:列式文件适合于分析查询,特别是需要聚合计算和跨多个列的查询。由于只读取所需的列数据,可以减少不必要的数据传输,提高查询性能。此外,由于相同类型的数据连续存储,可以实现更高的数据压缩比,减少存储空间占用。
        缺点:列式文件对于整行数据的读取和写入操作较慢,因为需要跨多个列进行读取和写入。此外,由于数据按列存储,如果需要查询整行数据,需要进行列的合并操作。

总结:
        行式文件适合事务处理和实时查询,对于整行数据的读写操作效率较高。
        列式文件适合分析查询,对于聚合计算和跨多个列的查询具有优势,并能提供更好的压缩比和存储效率。

在实际应用中,可以根据数据的访问模式和查询需求选择合适的存储格式,或者根据具体情况使用行式和列式文件的混合存储方式,以达到最佳的数据存储和查询性能。

十二、在Hive中动态分区和静态分区分别是什么 区别是什么

在Hive中,动态分区和静态分区都是用于对表进行分区的方式。 静态分区是在创建表时就指定分区的方式,例如:

CREATE TABLE my_table (
  col1 string,
  col2 int
)
PARTITIONED BY (
  year int,
  month int,
  day int
);

 这里的yearmonthday就是静态分区,每个分区对应一个文件夹,数据只能存储在对应的文件夹中。 动态分区则是在数据导入时,根据数据中的特定列的值自动创建分区,例如:

INSERT INTO my_table
PARTITION (year=2022, month=10, day=20)
VALUES ('value1', 1);

这里的yearmonthday是动态分区,如果对应的分区文件夹不存在,则会自动创建。动态分区可以更灵活地处理数据,避免创建大量的空分区,同时也可以提高查询效率。 静态分区和动态分区的区别在于静态分区需要在创建表时就确定好分区,而动态分区则是在数据导入时根据特定列的值动态创建分区。 

十三、Hive中分区表和分桶表分别的使用场景和它们的区别

  1. 分区表 分区表是将表按照某一列进行分区,每个分区对应一个文件夹,数据只能存储在对应的文件夹中。分区表适用于按照某一列进行频繁的过滤和查询,例如按照日期、地区等列进行分区。使用分区表可以减少扫描的数据量,提高查询效率。
  2. 分桶表 分桶表是将表按照某一列进行分桶,每个分桶对应一个文件,数据可以存储在多个文件中。分桶表适用于按照某一列进行频繁的连接操作,例如按照用户ID进行分桶。使用分桶表可以减少数据的移动和网络传输,提高查询效率。 它们的区别在于:
  3. 存储方式不同:分区表按照某一列进行分区,每个分区对应一个文件夹,数据只能存储在对应的文件夹中;分桶表按照某一列进行分桶,每个分桶对应一个文件,数据可以存储在多个文件中。
  4. 查询方式不同:分区表可以使用分区字段进行过滤和查询,例如WHERE year = 2022 AND month = 10;分桶表需要使用取模等方式进行过滤和查询,例如WHERE user_id % 100 = 0
  5. 适用场景不同:分区表适用于按照某一列进行频繁的过滤和查询,分桶表适用于按照某一列进行频繁的连接操作。

        总之,分区表和分桶表是Hive中提高查询效率的常用技术,根据具体的使用场景选择合适的技术可以提高查询效率。

HBase

一、RowKey 如何设计,设计不好会产生什么后果

HBase由于其存储和读写的高性能,在OLAP及时分析中发挥重要作用,HBase的查询只能通过rowkey来查询(rowkey便表示唯 一 一 行记录)
rowkey设计的优劣直接影响读写性能。HBase中的数据是按照rowkey的ASCII字典书序来进行全局排序HBase提出的设计原则主要有:长度原则,唯一原则,排序原则和散列原则。

长度原则:
RowKey是一个二进制码流,可以是任意字符串,最大长度为64kb,实际应用中一般为10-100bytes,以byte[]形式保存,一般设计成定长,建议越短越好,不要超过十六个字节,原因是:
在 HBase 的底层存储 HFile 中,RowKey 是 KeyValue 结构中的一个域。假设 RowKey 长度 100B,那么 1000 万条数据中,光 RowKey 就占用掉 100*1000w=10亿个字节 将近 1G 空间,这样会极大影响HFile 的存储效率。

唯一原则:
由于RowKey用来唯一标识一行记录,所以必须在设计上保证RowKey的唯一性。
由于 HBase 中数据存储的格式是 Key-Value 对格式,所以如果向 HBase 中同一张表插入相同 RowKey的数据,则原先存在的数据会被新的数据给覆盖掉(和 HashMap 效果相同)。

排序原则:
RowKey是按照字典顺序排序存储的,所以设计RowKey时,利用排序特性,将经常读取的数据存储到一起,将最近可能访问的数据放到一起。
一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为 RowKey 的一部分对这个问题十分有用,可以用 Long.Max_Value-timestamp追加到key的末尾。
例如 [key][reverse_timestamp] , [key]的最新值可以通过scan [key]获得[key]的第一条记录,因为
HBase 中 RowKey 是有序的,第一条记录是最后录入的数据。

散列原则:
散列原则就是设计出来的RowKey需要能均匀的分布到各个RegionServer上。
比如设计RowKey时,当RowKey是按时间戳的方式递增,就不要将时间放在二进制码的前面,可以将RowKey的高位作为散列字段,由程序循环生成,可以在低位放时间字段,这样就可以提高数据均衡分布在每个RegionServer实现负载均衡的几率。
如果没有散列字段,首字段只有时间信息,那就会出现所有新数据都在一个 RegionServer 上堆积的热点现象,这样在做数据检索的时候负载将会集中在个别 RegionServer 上,降低查询效率。
解决办法solution:
        1、reverse反转
例子:手机号反转后的字符串作为rowkey,这样避免了以手机号开头比较固定引起的热点问题 (如152、185等),但后半部分变化很多 如果将它反转过来,可以有效地避免热点
缺点:牺牲了rowkey的有序性

反转时间这个操作严格来讲不算“打散”,但可以调整数据的时间排序。如果将时间按照字典序排列,最近产生的数据会排在旧数据后面。如果用一个大值减去时间(比如用99999999减去yyyyMMdd,或者Long.MAX_VALUE减去时间戳),最新的数据就可以排在前面了。
        2、salt加盐

在RowKey前添加一些前缀,加盐的前缀种类越多,RowKey被打的越散。
比如在一个有4个Region的HBase表中,加salt前的rowkey:abc001,abc002,abc003,分别加上a、b、c前缀后的rowkey为a-abc001、b-abc002、c-abc003;可以看到,salting前数据分布在两个rowkey里,salting后数据分布在三个rowkey钟,避免热点现象 ,增加了读写的吞吐量
缺点:增加了读写开销

        3.Hash散列或者Mod
例子:将上述的rowkey经过hash处理,采用md5散列算法取前4位做前缀
如果 RowKey 的设计不合理,可能会导致以下后果:
        1. 数据倾斜:不均匀的 RowKey 设计可能导致数据在 RegionServer 上分布不均,一些
RegionServer 的负载过重,而其他的负载较轻,导致性能下降和不平衡。
        2. 数据冗余:设计不好的 RowKey 可能导致相同或类似的数据分布在不同的 Region 中,造成数据冗余,浪费存储空间。
        3. 查询效率低下:如果 RowKey 设计不符合查询模式,可能会导致查询时需要扫描大量的数据,降低查询效率。
        4. 存储空间浪费:不合理的 RowKey 设计可能会导致存储空间的浪费,不必要地增加数据的存储量。

因此,在设计 HBase 表时,需要仔细考虑 RowKey 的设计,综合考虑数据的唯一性、分布均匀性、查询需求和性能优化,选择合适的 RowKey 设计方案。根据具体的业务需求和数据访问模式,可以采用不同的策略,如哈希分区、时间戳作为前缀、字符拼接等,以实现高效的数据存储和查询。
https://blog.csdn.net/m0_66882197/article/details/130137806

二、为什么 HBase 百亿数据可以做到秒级查询

HBase适合存储PB级别的海量数据(百亿千亿量级条记录),如果根据记录主键Rowkey来查询,能在几十到百毫秒内返回数据。
那么HBase是如何做到的呢?
接下来,简单阐述一下数据的查询思路和过程。
第1步:
项目有100亿业务数据,存储在一个HBase集群上(由多个服务器数据节点构成),每个数据节点上有若干个Region(区域),每个Region实际上就是HBase中一批数据的集合(一段连续范围rowkey的数据)。
我们现在开始根据主键RowKey来查询对应的记录,通过meta表可以帮我们迅速定位到该记录所在的数据节点,以及数据节点中的Region,目前我们有100亿条记录,占空间10TB。所有记录被切分成5000个Region,那么现在,每个Region就是2G。
由于记录在1个Region中,所以现在我们只要查询这2G的记录文件,就能找到对应记录。
第2步:
由于HBase存储数据是按照列族存储的。比如一条记录有400个字段,前100个字段是人员信息相关,这是一个列簇(列的集合);中间100个字段是公司信息相关,是一个列簇。另外100个字段是人员交易信息相关,也是一个列簇;最后还有100个字段是其他信息,也是一个列簇

这四个列簇是分开存储的,这时,假设2G的Region文件中,分为4个列族,那么每个列族就是500M。
到这里,我们只需要遍历这500M的列簇就可以找到对应的记录。
第3步:
如果要查询的记录在其中1个列族上,1个列族在HDFS中会包含1个或者多个HFile。
如果一个HFile一般的大小为100M,那么该列族包含5个HFile在磁盘上或内存中。
由于HBase的内存进而磁盘中的数据是排好序的,要查询的记录有可能在最前面,也有可能在最后面,按平均来算,我们只需遍历2.5个HFile共250M,即可找到对应的记录。
第4步:
每个HFile中,是以键值对(key/value)方式存储,只要遍历文件中的key位置即可,并判断符合条件可以了。
一般key是有限的长度,假设key/value比是1:24,最终只需要10M的数据量,就可获取的对应的记录。
如果数据在机械磁盘上,按其访问速度100M/S,只需0.1秒即可查到。
如果是SSD的话,0.01秒即可查到。
当然,扫描HFile时还可以通过布隆过滤器快速定位到对应的HFile,以及HBase是有内存缓存机制的,如果数据在内存中,效率会更高。

总结
正因为以上大致的查询思路,保证了HBase即使随着数据量的剧增,也不会导致查询性能的下降。
同时,HBase是一个面向列存储的数据库(列簇机制),当表字段非常多时,可以把其中一些字段独立出来放在一部分机器上,而另外一些字段放到另一部分机器上,分散存储,分散列查询。
正由于这样复杂的存储结构和分布式的存储方式,保证了HBase海量数据下的查询效率。

三、Hive 和 HBase 的区别

Hive和HBase是两个在大数据领域中常用的开源工具,它们有以下几个主要区别:
1. 数据存储方式:Hive是基于Hadoop的分布式文件系统存储数据,将数据存储为文件,通常使用类似于SQL的查询语言进行数据查询和分析。而HBase是基于Hadoop的分布式数据库,采用键值对存储数据,适用于实时读写大规模结构化数据。
2. 数据模型:Hive是基于表的数据模型,支持类似于关系型数据库的表结构,可以定义表、列、分
区等,使用SQL语句进行查询和分析。HBase是基于列族的数据模型,数据存储为键值对,支持按
行和列进行快速访问,适用于灵活的数据模式和高速读写。
3. 数据处理方式:Hive主要用于批量数据处理和分析,适合处理大规模数据集,支持MapReduce等并行计算框架。HBase则更适合实时数据访问和随机读写,支持高吞吐量和低延迟的数据操作。
4. 查询语言:Hive使用类似于SQL的查询语言(HiveQL)进行数据查询和分析,具有较高的用户友好性和易用性。HBase则提供了Java API和Shell命令行工具,对开发者的技术要求较高。
5. 数据一致性:Hive保证数据的一致性和可靠性,适用于对数据进行大规模的离线处理和分析。
HBase提供了强一致性和高可用性,适用于实时读写和需要快速响应的应用场景。

总的来说,Hive适用于大规模数据的离线处理和分析,提供类似于SQL的查询语言和灵活的数据处理能力;而HBase适用于实时读写和随机访问大规模结构化数据,提供高吞吐量和低延迟的数据操作能力。根据具体的需求和场景,选择适合的工具来处理和存储数据。

Hive和HBase在现实生活中可以应用于不同的场景,具体如下:
Hive的应用场景:
1. 数据仓库和大数据分析:Hive适合处理大规模的结构化数据,可以用于构建数据仓库和进行复杂
的数据分析,例如基于用户行为数据的推荐系统、用户画像分析等。
2. 数据清洗和转换:Hive可以进行ETL(抽取、转换、加载)操作,对数据进行清洗、格式转换和数据集成,为后续的数据分析和挖掘提供清洗过的数据。
3. 日志分析:Hive可以用于对大量日志数据的分析,例如网站访问日志分析、广告点击日志分析
等,从中提取有价值的信息和指标。

HBase的应用场景:
1. 实时数据存储与访问:HBase适用于需要实时读写和随机访问大规模结构化数据的场景,例如在线金融交易系统、社交媒体应用中的用户数据管理等。
2. 时序数据存储:HBase提供了按时间排序的功能,适合存储和查询时间序列数据,例如传感器数
据、设备监控数据等。
3. 分布式缓存:HBase可以作为分布式缓存使用,将热点数据存储在内存中,提供快速的数据访问。

需要注意的是,Hive和HBase并非互斥的,它们可以结合使用,根据具体的需求和场景选择适当的工具进行数据处理和存储。例如,可以使用Hive进行离线数据处理和分析,然后将结果存储到HBase中以供实时查询和访问。

四、HBase 写入数据的流程

HBase数据的写入过程:
1、Client访问zookeeper,获取元数据存储所在的regionserver
2、通过刚刚获取的地址访问对应的regionserver,拿到对应的表存储的regionserver
3、去表所在的regionserver进行数据的添加
4、查找对应的region,在region中寻找列族,先向memstore中写入数据
5、当memstore写入的值变多,触发溢写操作(flush),进行文件的溢写,成为一个StoreFile
6、当溢写的文件过多时,会触发文件的合并(Compact)操作,合并有两种方式(major,minor)
多个StoreFile合并成一个StoreFile,同时进行版本合并和数据删除
minor compaction:小范围合并,默认是3-10个文件进行合并,不会删除其他版本的数据。
major compaction:将当前目录下的所有文件全部合并,一般手动触发,会删除其他版本的数据(不同时间戳的)
7、当region中的数据逐渐变大之后,达到某一个阈值,会进行裂变(一个region等分为两个region,并分配到不同的regionserver),原本的Region会下线,新Split出来的两个Region会被HMaster分配到相应的HRegionServer上,使得原先1个Region的压力得以分流到2个Region上。

由此可知HBase只是增加数据,所有的更新和删除操作,都是在Compact阶段做的,所以用户写操作只需要进入到内存即可立即返回,从而保证I/O高性能读写

五、HBase读写流程

  

六、为什么要使用 Phoenix

Phoenix是一个基于HBase的关系型数据库,它提供了在HBase上快速、高效地执行SQL查询的能力。
使用Phoenix的主要原因如下:
1. SQL兼容性:Phoenix提供了SQL语法和函数的支持,使得开发人员可以使用熟悉的SQL语句来操作HBase中的数据。这降低了学习成本,并且可以直接利用现有的SQL工具和技能来进行数据分析和查询。
2. 快速查询:Phoenix通过将SQL查询转换为HBase的原生查询,利用HBase的分布式存储和索引能力,可以实现高性能的数据查询。它采用了基于索引的查询优化策略,支持复杂的查询操作,并且能够在大规模数据集上实现快速响应。
3. 实时性能:Phoenix具有低延迟和高吞吐量的特性,适合处理实时数据和需要快速响应的应用场景。它利用HBase的分布式架构和列存储的优势,可以进行高效的数据插入、更新和删除操作。
4. 数据一致性:Phoenix提供了ACID事务的支持,保证了数据的一致性和可靠性。它通过HBase的事务机制来确保数据的原子性、一致性、隔离性和持久性。
5. 生态系统整合:Phoenix与Hadoop生态系统中的其他工具和技术无缝集成,例如Hive、Spark、
Pig等。它可以作为这些工具的数据源,使得数据分析和处理更加便捷和高效。

总之,使用Phoenix可以让开发人员在HBase上获得更好的查询性能和数据操作能力,同时通过SQL语法的支持,降低了使用HBase的学习和开发成本。它适用于需要在HBase上进行高性能数据查询和实时数据处理的场景,为HBase提供了更加友好和强大的接口。

七、HBase 的热点 Key 会产生什么问题

检索habse的记录首先要通过row key来定位数据行,当大量的Cient访问Hbase集群的一个或少数几个节点,会造成少数RegionServer的读/写请求过多、负载过大,而其他RegionServer负载却很小,就造成了“热点”现象.
(1)加盐
这里所说的加盐不是密码学中的加盐,而是在rowkey的前面增加随机数,具体就是给rowkey分配一个随机前缀以使得它和之前的rowkey的开头不同。给多少个前缀? 这个数量应该和我们想要分散数据到不同的region的数量一致(类似hive里面的分桶)。
( 自己理解: 即region数量是一个范围,我们给rowkey分配一个随机数,前缀(随机数)的范围是region的数量)
加盐之后的rowkey就会根据随机生成的前缀分散到各个region上,以避免热点。
(2)哈希
哈希会使同一行永远用一个前缀加盐。哈希也可以使负载分散到整个集群,但是读却是可以预测的。使用确定的哈希可以让客户端重构完整的rowkey,可以使用get操作准确获取某一个行数据。
(3)反转
第三种防止热点的方法是反转固定长度或者数字格式的rowkey。这样可以使得rowkey中经常改变的部分(最没有意义的部分)放在前面。这样可以有效的随机rowkey,但是牺牲了rowkey的有序性。
反转rowkey的例子:以手机号为rowkey,可以将手机号反转后的字符串作为rowkey,从而避免诸如139、158之类的固定号码开头导 致的热点问题。
(4)时间戳反转
一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为rowkey的一部分对这个问题十分有用,可以用Long.Max_Value - timestamp追加到key的末尾,例如[key][reverse_timestamp] ,[key] 的最新值可以通过scan [key]获得[key]的第一条记录,因为HBase中rowkey是有序的,第一条记录是最后录入的数据。
(5)尽量减少行和列的大小
在HBase中,value永远和它的key一起传输的。当具体的值在系统间传输时,它的rowkey,列名,时间戳也会一起传输。
如果你的rowkey和列名很大,HBase storefiles中的索引(有助于随机访问)会占据HBase分配的大量内存,因为具体的值和它的key很大。可以增加block大小使得storefiles索引再更大的时间间隔增加,或者修改表的模式以减小rowkey和列名的大小。压缩也有助于更大的索引。
(6)其他办法
列族名的长度尽可能小,最好是只有一个字符。冗长的属性名虽然可读性好,但是更短的属性名存储在HBase中会更好。也可以在建表时预估数据规模,预留region数量,例如create 'myspace:mytable’,SPLITS => [01,02,03,,...99]

八、为什么不建议 HBase 设计过多列族

在HBase中,列族是数据模型的一部分,用于组织和管理列的集合。尽管HBase支持列族的创建和使用,但并不建议设计过多的列族,原因如下:
        1. 存储效率:HBase以列族为单位进行数据存储和管理。当设计过多的列族时,每个列族都需要占用额外的存储空间和资源。这可能导致存储效率的降低,增加磁盘使用和维护成本。
        2. 写入性能:在HBase中,写入操作是针对列族进行的。当设计过多的列族时,每次写入操作都需要同时涉及多个列族,增加了写入的负载和复杂性。这可能导致写入性能的下降,尤其是在高并发写入的情况下。
        3. 读取性能:虽然HBase具有高度可扩展性和灵活的数据访问模型,但在读取数据时,需要跨越列族进行扫描和检索操作。当设计过多的列族时,读取操作可能需要涉及多个列族,增加了数据的读取复杂性和访问成本。
        4. 维护复杂性:设计过多的列族会增加HBase集群的维护复杂性。每个列族都需要管理和维护索引、数据分布、存储空间等方面的资源。随着列族数量的增加,集群管理和维护的工作也会变得更加繁琐和困难。

综上所述,建议在HBase设计中避免过多的列族。合理的设计应该根据数据的特点和访问模式,将相关的列组织在一个或少数几个列族中。这样可以提高存储效率、写入性能和读取性能,并降低系统的维护复杂性。

九、说一说 HBase 的数据刷写与合并

在HBase中,数据的写入和合并是两个不同的过程。

数据刷写:

当用户通过HBase客户端向表中添加、修改或删除某个数据时,HBase会在内存中创建一个称为“MemStore”的数据结构,该结构用于存储刚刚进行的修改操作,同时也会将修改操作写入WAL(Write Ahead Log)日志文件中。当MemStore中的数据量达到一定阈值时,HBase会将其刷写到磁盘上,并生成一个称为“HFile”的文件,这个过程称为数据刷写。

数据合并:

HBase中数据刷写的过程中,会产生多个HFile文件,这些文件会不断地累加,最终可能会导致读取性能下降。为了避免这种情况,HBase会定期执行数据合并操作。数据合并的过程中,HBase会将多个HFile文件中的数据合并成一个新的HFile文件,并清除旧的HFile文件,以达到数据整合和减少存储空间的目的。这个过程称为数据合并。

总结:

数据刷写和数据合并是HBase中非常重要的两个过程,数据刷写用于将内存中的数据刷写到磁盘上,防止数据丢失;数据合并用于整合数据文件,避免数据量过大导致读取性能下降。

https://blog.csdn.net/weixin_50627985/article/details/125510408
8.7.1触发时机
1.Region中所有的MemStore占用的内存超过相关阈值
        hbase.hregion.memstore.flush.size 参数控制,默认为128MB
        如果我们的数据增加得很快,达到了 hbase.hregion.memstore.flush.size *
        hbase.hregion.memstore.block.multiplier的大小hbase.hregion.memstore.block.multiplier 默
认值为4,也就是128*4=512MB的时候,那么除了触发 MemStore 刷写之外,HBase 还会在刷写
的时候同时阻塞所有写入该 Store的写请求!
2.整个RegionServer的MemStore占用内存总和大于相关阈值
        HBase 为 RegionServer 所有的 MemStore 分配了一定的写缓存(),大小等于hbase_heapsize(RegionServer 占用的堆内存大小)* hbase.regionserver.global.memstore.size (默认值是 0.4)。
        如果整个 RegionServer 的 MemStore 占用内存总和大于阈值将会触发 MemStore 的刷写。
        hbase.regionserver.global.memstore.size.lower.limit (默认值为 0.95)* MAX_SIZE
        例如:HBase 堆内存总共是 32G ,MemStore 占用内存为:32 * 0.4 * 0.95 = 12.16G将触发刷写
        如果达到了 RegionServer 级别的 Flush,当前 RegionServer 的所有写操作将会被阻塞,这个阻塞可能会持续到分钟级别。
3.WAL数量大于相关阈值
        数据到达 Region 的时候是先写入 WAL,然后再被写到 Memstore 。
        如果 WAL 的数量越来越大,这就意味着 MemStore 中未持久化到磁盘的数据越来越多。
        当 RS 挂掉的时候,恢复时间将会变得很长,所以有必要在 WAL 到达一定的数量时进行一次刷写操作
4.定期自动刷写
        默认值 3600000(即 1 小时),HBase 定期 Flush 所有 MemStore 的时间间隔。
        一般建议调大,比如 10 小时,因为很多场景下 1 小时 Flush 一次会产生很多小文件,一方面导致Flush 比较频繁,另一方面导致小文件很多,影响随机读性能
5.数据更新超过一定阈值
        如果 HBase 的某个 Region 更新的很频繁,而且既没有达到自动刷写阀值,也没有达到内存的使用限制,但是内存中的更新数量已经足够多,也会触发刷写的
        比如超过 hbase.regionserver.flush.per.changes 参数配置,默认为30000000,那么也是会触发刷写的。
6.手动触发刷写
Shell 中通过执行 flush 命令
        1. hbase> flush 'TABLENAME'
        2. hbase> flush 'REGIONNAME'
        3. hbase> flush 'ENCODED_REGIONNAME'
        4. hbase> flush 'REGION_SERVER_NAME'
7.特别注意:
        以上所有条件触发的刷写操作最后都会检查对应的 HStore 包含的 StoreFiles 文件数是否超过hbase.hstore.blockingStoreFiles 参数配置的个数,默认值是16。
        如果满足这个条件,那么当前刷写会被推迟到hbase.hstore.blockingWaitTime 参数设置的时间后再刷写。
        在阻塞刷写的同时,HBase 还会请求 Compaction 或者Split 操作。
8.7.2 刷写策略
1.HBASE1.1之前:
        MemStore 刷写是 Region 级别的。就是说,如果要刷写某个 MemStore ,MemStore 所在的
Region 中其他 MemStore 也是会被一起刷写的
2.HBASE2.x之后
        FlushAllStoresPolicy
        每次刷写都是对 Region 里面所有的 MemStore 进行的
        FlushAllLargeStoresPolicy
        判断 Region 中每个 MemStore 的使用内存是否大于某个阀值,大于这个阀值的MemStore 将会被刷写。
        flushSizeLowerBound = max((long)128 / 3, 16) = 42
        FlushNonSloppyStoresFirstPolicy
8.7.3 刷写流程
1.prepareFlush 阶段:
        刷写的第一步是对 MemStore 做 snapshot(快照)
        为了防止刷写过程中更新的数据同时在 snapshot 和 MemStore 中而造成后续处理的困难
        所以在刷写期间需要持有 updateLock 。持有了 updateLock 之后,这将阻塞客户端的写操作。
        所以只在创建 snapshot 期间持有 updateLock
        而且 snapshot 的创建非常快,所以此锁期间对客户的影响一般非常小。
        对 MemStore 做 snapshot 是 internalPrepareFlushCache 里面进行的。
2.flushCache 阶段:
        如果创建快照没问题,那么返回的 result.result 将为 null。
        这时候我们就可以进行下一步 internalFlushCacheAndCommit。
        其实 internalFlushCacheAndCommit 里面包含两个步骤:flushCache 和 commit 阶段。
flushCache 阶段:
其实就是将 prepareFlush 阶段创建好的快照写到临时文件里面,临时文件是存放在对应 Region
文件夹下面的 .tmp 目录里面。
commit 阶段:
将 flushCache 阶段生产的临时文件移到(rename)对应的列族目录下面,并做一些清理工作,
比如删除第一步生成的 snapshot。
8.8.1 合并分类
HBase 根据合并规模将 Compaction 分为了两类:MinorCompaction 和 MajorCompaction
Minor Compaction
是指选取一些小的、相邻的StoreFile将他们合并成一个更大的StoreFile,在这个过程中不会处理已经Deleted或Expired的Cell,但是会处理超过TTL的数据
一次Minor Compaction的结果是让小的storefile变的更少并且产生更大的StoreFile。
Major Compaction
是指将所有的StoreFile合并成一个StoreFile
清理三类无意义数据:被删除的数据、TTL过期数据、版本号超过设定版本号的数据。
一般情况下,Major Compaction时间会持续比较长,整个过程会消耗大量系统资源,对上层业务有比较大的影响。因此线上业务都会将关闭自动触发Major Compaction功能,改为手动在业务低峰期触发。
8.8.3 合并时机
触发compaction的方式有三种:Memstore刷盘、后台线程周期性检查、手动触发。
Memstore刷盘
memstore flush会产生HFile文件,文件越来越多就需要compact。
每次执行完Flush操作之后,都会对当前Store中的文件数进行判断,一旦文件数大于配置3,就会触发compaction。
compaction都是以Store为单位进行的,而在Flush触发条件下,整个Region的所有Store都会执行
compact
后台线程周期性检查
后台线程定期触发检查是否需要执行compaction,检查周期可配置。
hbase.server.thread.wakefrequency(默认10000毫秒)
*hbase.server.compactchecker.interval.multiplier(默认1000)
CompactionChecker大概是2hrs 46mins 40sec 执行一次
小文件周期性合并成大文件
线程先检查小文件数是否大于配置3,一旦大于就会触发compaction。
大文件周期性合并成Major Compaction
如果不满足,它会接着检查是否满足major compaction条件,
如果当前store中hfile的最早更新时间早于某个值mcTime,
就会触发major compaction(默认7天触发一次,可配置手动触发)。
手动触发
一般来讲,手动触发compaction通常是为了执行major compaction,一般有这些情况需要手动触发合并,是因为很多业务担心自动major compaction影响读写性能,因此会选择低峰期手动触发;
也有可能是用户在执行完alter操作之后希望立刻生效,执行手动触发major compaction;
是HBase管理员发现硬盘容量不够的情况下手动触发major compaction删除大量过期数据;

8.8.4 合并策略
承载了大量IO请求但是文件很小的HFile,compaction本身不会消耗太多IO,而且合并完成之后对读的性能会有显著提升。

线程池选择
HBase CompacSplitThread类内部对于Split、Compaction等操作专门维护了各自所使用的线程池和Compaction相关的是如下的longCompactions和shortCompactions
前者用来处理大规模compaction,后者处理小规模compaction
默认值为2 * maxFlilesToCompact * hbase.hregion.memstore.flush.size
如果flush size 大小是128M,该参数默认值就是2 * 10 * 128M = 2.5G

合并策略选择
HBase 主要有两种 minor 策略: RatioBasedCompactionPolicy (0.96.x之前)和
ExploringCompactionPolicy(当前默认)
1.RatioBasedCompactionPolicy(基于比列的合并策略)
从老到新逐一扫描HFile文件,满足以下条件之一停止扫描
当前文件大小<比当前文件新的所有文件大小总和*ratio(高峰期1.2,非高峰期5)
当前所剩候选文件数<=阈值(默认为3)
2.ExploringCompactionPolicy策略(默认策略)
基于Ratio策略,不同之处在于Ratio策略找到一个合适文件集合就停止扫描,而Exploring策略会记录所有合适的文件集合,然后寻找最优解,待合并文件数最多或者待合并文件数相同的情况下文件较小的进行合并
3.FIFO Compaction策略
收集过期文件并删除,对应业务的列簇必须设置有TTL
4.Tier-Based Compaction策略(分层策略)
针对数据热点情况设计的策略,根据候选文件的新老程度将其划分为不同的等级,每个等级都有对应的Ratio,表示该等级文件比选择为参与Compation的概率
5.Stripe Compation策略(条纹策略)
将整个Store中的文件按照key划分为多个range,此处称为stripe,一个Stripe内部就类似于一个小
Region,可以执行Minon Compation和major Compation

执行文件合并
1.分别读出待合并hfile文件的KV,并顺序写到位于./tmp目录下的临时文件中
2.将临时文件移动到对应region的数据目录
3.将compaction的输入文件路径和输出文件路径封装为KV写入WAL日志,并打上compaction标记,最后强制执行sync
4.将对应region数据目录下的compaction输入文件全部删除

Scala

一、Scala 中,函数是一等公民具体体现在哪里

在Scala中,函数被视为一等公民,这意味着函数在语言中拥有与其他实体(如变量、类等)相同的地位和权力。具体体现在以下几个方面:
1. 函数可以被赋值给变量:在Scala中,可以将函数赋值给变量,就像赋值其他类型的值一样。这样可以通过变量名来引用函数,并将其传递给其他函数或方法。
2. 函数可以作为参数传递:在Scala中,函数可以作为参数传递给其他函数或方法。这种能力称为高阶函数,它使得编写灵活的、可复用的代码成为可能。
3. 函数可以作为返回值:Scala中的函数可以作为其他函数或方法的返回值。这种能力使得可以根据条件或上下文动态地生成函数,并返回给调用方进行使用。
4. 函数可以匿名定义:Scala支持匿名函数的定义,也就是不需要给函数命名,直接定义函数体即
可。这种方式常用于简洁地表示一次性的、简单的函数逻辑。
5. 函数可以存储在数据结构中:在Scala中,函数可以存储在数据结构(如列表、数组、映射等)
中。这样可以创建函数的集合,对集合中的函数进行遍历、筛选、映射等操作。

通过这些特性,Scala中的函数可以像其他数据类型一样进行操作和传递,使得函数可以灵活地用于各种编程场景,提供更高层次的抽象和表达能力。

二、说说 Scala 函数的至简原则


(1)return可以省略,Scala会使用函数体的最后一行代码作为返回值
(2)如果函数体只有一行代码,可以省略花括号
(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
(4)如果有return,则不能省略返回值类型,必须指定
(5)如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
(6)Scala如果期望是无返回值类型,可以省略等号
(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略

Spark

一、说 10 个常用的 Spark 转换算子

map(func): 将原 RDD 中的每个元素传递给函数 func,得到一个新的 RDD。
flatMap(func): 与 map 类似,但每个元素都可以生成多个输出,这些输出被平铺(flattening)成
一个新的 RDD。
filter(func): 返回输入 RDD 中通过函数 func 的筛选结果为 true 的元素。
distinct([numTasks])): 返回输入 RDD 中所有不同的元素,可选参数 numTasks 指定任务的数量。
union(otherRDD): 返回对输入 RDD 和参数 RDD 执行联合操作的结果,生成一个新的 RDD,不去
重。
intersection(otherRDD)): 返回对输入 RDD 和参数 RDD 执行交集操作的结果,生成一个新的
RDD。
subtract(otherRDD): 返回对输入 RDD 和参数 RDD 执行差集操作的结果,生成一个新的 RDD。
cartesian(otherRDD): 返回对输入 RDD 和参数 RDD 执行笛卡尔积的结果,生成一个新的 RDD。

sample():从 RDD 中随机抽样一些数据,并将其作为新 RDD 返回。
sortBy():根据给定的键对 RDD 进行排序,并将排序后的 RDD 返回。

二、说几个高效算子,并说明为什么高效

以下是几个高效算子及其高效之处的简要说明:
1. Map:Map 算子是一种转换算子,它将输入的每个元素映射为一个输出元素。Map 算子的高效性在于它的并行化处理能力。输入的数据可以被切分成多个分片,每个分片可以在独立的计算资源上进行处理,从而提高整体的处理速度。
2. Filter:Filter 算子用于过滤满足特定条件的元素。它只保留满足条件的元素,而丢弃不满足条件的元素。Filter 算子的高效性在于它能够减少处理的数据量,从而提高计算的效率。
3. Reduce:Reduce 算子是一种聚合算子,它将输入的元素进行聚合操作,生成一个输出结果。
Reduce 算子的高效性在于它的并行化处理能力和分布式计算能力。Reduce 操作可以在多个计算
节点上并行执行,每个节点处理一部分数据,最后将各个节点的结果进行合并,从而实现高效的聚
合计算。
4. Join:Join 算子用于合并两个或多个数据集中的相关数据,基于特定的关联条件进行合并。高效的Join 算子通常利用了数据的分区和排序,从而避免了全局排序和全局扫描的开销。它可以将数据按照关联条件进行分区,并在分区内进行局部的合并操作,从而提高效率。
5. Aggregate:Aggregate 算子用于对数据进行聚合操作,例如计算平均值、求和等。高效的
Aggregate 算子通常使用了基于采样和预聚合的技术。通过对数据进行采样,并在局部进行预聚合
操作,可以减少数据的传输量和计算量,从而提高聚合的效率。

这些高效算子的效率取决于它们的并行化能力、数据处理方式、数据分区和排序策略等因素。通过合理设计和优化这些算子的实现,可以提高计算的速度和效率

三、如果让你编码,如何实现 distinct 算子的功能

要实现 distinct 算子的功能,可以采用以下步骤:
        1. 读取输入数据:首先,从输入数据源(如文件、数据库表等)读取数据。
        2. 建立哈希表:创建一个空的哈希表用于存储已经出现过的元素。
        3. 迭代处理数据:对于每个输入元素,判断该元素是否已经存在于哈希表中。
                如果该元素已经存在于哈希表中,表示重复元素,跳过该元素。
                如果该元素不在哈希表中,表示新的元素,将该元素添加到哈希表中,并输出该元素。
        4. 输出结果:将去重后的元素作为输出结果。

这个过程可以通过编程语言(如Python、Java、Scala等)来实现。具体的实现方式会根据编程语言和数据存储方式的不同而有所差异。例如,在使用Python语言的情况下,可以使用字典(Dictionary)来实现哈希表,通过遍历输入数据的方式来判断元素是否已经存在于字典中。而在使用Java语言的情况下,可以使用HashSet或HashMap来实现哈希表,通过遍历输入数据并调用相应的方法来判断和添加元素。

需要注意的是,distinct 算子的实现需要考虑数据规模和性能问题。对于大规模数据集,可以采用分布式计算框架(如Hadoop、Spark等)来实现并行处理,以提高算法的效率和可伸缩性。同时还可以根据具体情况进行优化,例如使用布隆过滤器等数据结构来减少内存占用和提高查询效率。

四、说几个 spark-submit 提交应用的参数,并说明其作用

在使用 spark-submit 提交Spark应用程序时,可以使用一些参数来配置应用的执行方式和环境。以下是几个常用的 spark-submit 参数及其作用:
        1. --class :指定应用程序的主类。主类是Spark应用程序的入口点,包含 main 方法。
        2. --master :指定应用程序的运行模式和资源管理器。可以设置为 local (本地模式)、yarn (YARN集群模式)或 spark://host:port (独立模式)等。
        3. --deploy-mode :指定应用程序的部署模式,可以是 client 或 cluster 。在 client 模式下,驱动程序运行在提交的机器上;在 cluster 模式下,驱动程序运行在集群中的一个节点上。
        4. --executor-memory :指定每个Executor的内存大小。可以使用带有单位的数字(如 1g 表示1GB)。
        5. --num-executors :指定Executor的数量。可以设置为整数值。
        6. --executor-cores :指定每个Executor的CPU核心数。
        7. --conf :用于设置其他Spark配置属性,如 --conf spark.driver.memory=2g 用于设置驱动程序的内存大小。
        8. --files :指定要传递给应用程序的文件。可以是本地文件或HDFS文件。
        9. --jars :指定要传递给应用程序的依赖的JAR文件。
        10. --packages :指定要传递给应用程序的外部依赖的Maven坐标。
这里只是列举了一些常用的 spark-submit 参数,实际上还有其他更多的参数可供使用,可以根据具体的需求和环境进行配置。可以通过运行 spark-submit --help 命令来获取完整的参数列表和说明。

五、Hadoop MR 的 Shuffle 和 Spark Shuffle 的区别

https://blog.csdn.net/weixin_43542605/article/details/122899574

Hadoop MapReduce (MR)的Shuffle和Spark的Shuffle是两个不同的概念,尽管它们都涉及到数据的重新分区和传输,但在实现和处理方式上有一些区别。
Hadoop MR的Shuffle是指在Map和Reduce阶段之间进行数据的重新分区和排序操作。在Map阶段,数据被划分为多个分区,并根据分区键进行排序。然后,Shuffle阶段将每个Map任务的输出按照分区键进行合并和排序,并将结果传递给Reduce任务进行聚合和计算。Hadoop MR的Shuffle过程需要将大量的数据从Map任务传输到Reduce任务,涉及磁盘IO和网络传输,因此在大规模数据处理时可能成为性能瓶颈。
Spark的Shuffle也涉及到数据的重新分区和传输,但与Hadoop MR的Shuffle相比,Spark的Shuffle具有一些优化和改进。Spark的Shuffle采用了内存和磁盘的混合存储方式,尽量减少了磁盘IO的开销。
Spark还引入了更高效的数据结构和算法,如基于排序的Shuffle和聚合缓冲区,以减少数据的传输量和网络开销。此外,Spark还支持在Shuffle过程中进行数据压缩和序列化,进一步减少了数据的传输量和存储开销。
另一个区别是,Spark的Shuffle并不仅限于Map和Reduce阶段,它还可以在其他转换操作中发生,例如groupByKey、join和sortBy等。这使得Spark能够更灵活地处理数据重分区和传输的需求,并在处理复杂任务时提供更高的性能和效率。

总的来说,Hadoop MR的Shuffle是在Map和Reduce阶段之间进行数据分区和传输的过程,而Spark的Shuffle更加灵活,可以在不同的操作中发生,并采用了一些优化技术来提高性能和效率。

六、说一说你知道的 Spark 优化

Spark提供了多种优化技术和策略,以提高作业的性能和效率。以下是一些常见的Spark优化技术:
1. 数据本地性优化:Spark通过将计算任务与数据尽可能地放置在同一节点上来减少数据传输开销。它可以利用数据本地性优化来最大化数据的局部性,减少网络传输。
2. 延迟执行:Spark采用了延迟执行(Lazy Evaluation)的机制,在必要时才执行作业,以减少不必要的计算开销。通过构建RDD的转换操作图,Spark可以优化执行计划并避免重复计算。
3. 内存管理和缓存:Spark通过在内存中缓存数据来提高访问速度。通过合理配置内存大小、使用持久化缓存和广播变量等方法,可以最大化地利用内存资源,减少磁盘IO开销。
4. 数据分区和并行度控制:Spark允许开发人员对数据进行合理的分区和控制并行度,以最大程度地利用集群资源。通过合理设置分区数和并行度,可以平衡作业的负载,提高作业的并行度和执行效率。
5. 窄依赖和宽依赖优化:Spark通过区分窄依赖和宽依赖来进行优化。窄依赖表示父RDD的每个分区仅依赖于一个或多个特定的子RDD分区,这可以实现高效的并行计算。宽依赖表示父RDD的每个分区可能依赖于多个子RDD的分区,这可能会引入数据的洗牌操作,需要更多的计算和传输开销。
6. 数据压缩和序列化:Spark支持对数据进行压缩和序列化,以减少数据的传输量和存储开销。选择适当的压缩算法和序列化方式,可以提高作业的性能和效率。
7. 广播变量:Spark提供了广播变量机制,可以将较小的数据集广播到所有的工作节点上,减少数据传输开销和内存占用。
8. 动态分区和分桶:Spark支持动态分区和分桶操作,可以更加灵活地控制数据的组织和存储方式,提高查询和计算的效率。
9. 硬件和资源配置:合理配置集群的硬件资源,如内存、CPU和磁盘,以满足作业的需求。此外,还可以使用资源管理器来控制任务的调度和分配,避免资源竞争和冲突。

以上是一些常见的Spark优化技术和策略,通过合理应用这些技术,可以提高作业的性能和效率,并充分发挥Spark在大数据处理中的优势。

七、Spark SQL 的执行流程

无论是使用 SQL语句还是直接使用 DataFrame 或者 DataSet 算子,都会经过Catalyst一系列的分析和优化,最终转换成高效的RDD的操作,主要流程如下:

1. sqlParser 解析 SQL,生成 Unresolved Logical Plan(未解析的逻辑计划)
2. 由 Analyzer 结合 Catalog 信息生成 Resolved Logical Plan(解析的逻辑计划)
3. Optimizer根据预先定义好的规则(RBO),对 Resolved Logical Plan 进行优化并生成Optimized Logical Plan(优化后的逻辑计划)
4. Query Planner 将 Optimized Logical Plan 转换成多个 Physical Plan(物理计划)。然后由CBO 根据 Cost Model 算出每个 Physical Plan 的代价并选取代价最小的 Physical Plan 作为最终的Physical Plan(最终执行的物理计划)
5. Spark运行物理计划,先是对物理计划再进行进一步的优化,最终映射到RDD的操作上,和Spark Core一样,以DAG图的方式执行SQL语句。 在最新的Spark3.0版本中,还增加了Adaptive Query Execution功能,会根据运行时信息动态调整执行计划从而得到更高的执行效率

整体的流程图如下所示:

八、讲讲 Spark 的通用运行流程

Spark作业运行流程
① 构建Spark Application的运行环境(启动SparkContext),SparkContext向资源管理器(YARN)注册并申请运行Executor资源;
② 资源管理器分配并启动Executor,Executor的运行情况将随着心跳发送到资源管理器上;
③ SparkContext构建成DAG图,将DAG图分解成Stage(Taskset),并把Taskset发送给TaskScheduler。Executor向SparkContext申请Task;
④ Task Scheduler将Task发放给Executor运行,同时SparkContext将应用程序代码发放Executor;
⑤ Task在Executor上运行,运行完毕释放所有资源。

 第一部分:Spark基础篇_spark运行基本流程_奔跑者-辉的博客-CSDN博客

九、RDD,DataFrame,DataSet 的区别

概念:
1.DataSet和RDD
 大数据的框架许多都要把内存中的数据往磁盘里写,所以DataSet取代rdd和dataframe。因为,现阶段底层序列化机制使用的是java的或者Kryo的形式。但是,java序列化出来的数据很大,影响存储Kryo对于小数据量的处理很好,但是数据量一大,又会出现问题,所以官方的解决方法是使用自定义的编码器(Encoder)去序列化
2.DataSet和DataFrame
DataSet跟DataFrame还是有挺大区别的,DataFrame开发都是写sql,但是DataSet是使用类似RDD的API。所以可以理解成DataSet就是存了个数据类型的RDD
3.DataSet\DataFrame\RDD的区别:
(1)相同点:
        都是分布式数据集
        DataFrame底层是RDD,但是DataSet不是,不过他们最后都是转换成RDD运行
        DataSet和DataFrame的相同点都是有数据特征、数据类型的分布式数据集(schema)
(2)不同点:
        (a)schema信息:
                RDD中的数据是没有数据类型的
                DataFrame中的数据是弱数据类型,不会做数据类型检查
                        虽然有schema规定了数据类型,但是编译时是不会报错的,运行时才会报错
                DataSet中的数据类型是强数据类型
        (b)序列化机制:
                RDD和DataFrame默认的序列化机制是java的序列化,可以修改为Kyro的机制
                DataSet使用自定义的数据编码器进行序列化和反序列化

十、Spark 的控制算子有哪些,有什么区别

Spark中的控制算子是一类用于控制数据流和作业执行的算子,它们可以影响数据的流向、分区、聚合等。以下是Spark中常见的控制算子及其区别:
1. repartition():用于重新分区数据,可以增加或减少分区数量。repartition()会产生shuffle操作,
即数据的重新分发和排序。
2. coalesce():用于减少分区数量,但不会产生shuffle操作。coalesce()只能将较少的分区合并到较多的分区中,不能进行分区的扩展。
3. distinct():用于去重操作,返回去重后的数据集。distinct()操作会重新分区数据,产生shuffle操
作。
4. sortBy():用于对数据集按指定的字段进行排序。sortBy()操作会产生shuffle操作,将数据重新分
区并按照指定的字段进行排序。
5. repartitionAndSortWithinPartitions():用于重新分区并按指定字段进行排序。与repartition()和
sortBy()不同,repartitionAndSortWithinPartitions()在分区内部进行数据排序,而不会全局排序。
6. partitionBy():用于按照指定字段对数据进行分区。partitionBy()操作会将数据按照指定的字段值
进行哈希分区,不会产生shuffle操作。
7. foreach():用于对数据集中的每个元素应用指定的操作。foreach()操作是一个行动算子,它对数
据集中的每个元素进行遍历并执行指定的操作,没有返回结果。

这些控制算子在Spark中具有不同的作用和行为。有些算子会产生shuffle操作,涉及数据的重新分区和排序,可能会产生性能开销。而有些算子则是针对特定的操作需求,如去重、排序和分区等。在使用这些算子时,需要根据具体的场景和需求选择合适的算子,避免不必要的数据重组和性能损耗。

十一、如何在 Spark SQL 运行过程中再分区

在 Spark SQL 运行过程中进行再分区可以使用以下几种方法:
        1. 使用repartition():在DataFrame或Dataset上调用repartition()方法,可以对数据进行重新分区。repartition()接受一个整数参数,表示要重新分区的目标分区数。该操作会产生shuffle,并将数据重新分区到指定的分区数。

val repartitionedDF = df.repartition(4) // 将DataFrame重新分区为4个分区

        2.使用coalesce():coalesce()方法与repartition()类似,但不会产生shuffle。它可以用于减少分区数量,但不能增加分区数量。

val coalescedDF = df.coalesce(2) // 将DataFrame减少为2个分区

3.使用SQL语句中的PARTITION BY子句:在使用Spark SQL执行SQL查询时,可以使用PARTITION BY子句指定分区字段,按照指定的字段进行数据分区。

val query = "SELECT * FROM table PARTITION BY column"
val result = spark.sql(query)

 注意:使用PARTITION BY子句只在查询过程中实现临时的数据分区,不会对原始数据进行物理分区修改。
这些方法可以根据具体的需求选择合适的再分区策略。需要注意的是,再分区操作可能会引发数据重组和shuffle操作,因此需要根据数据量、计算资源和性能需求来评估是否需要进行再分区,并选择合适的分区数。

十二、YARN 的 Client 提交和 Cluster 提交的区别

在YARN中,有两种方式可以提交应用程序:Client提交和Cluster提交,它们之间有以下区别:
        1. Client提交:在Client提交模式下,应用程序的驱动程序运行在客户端机器上,负责向YARNResourceManager提交应用程序,并监控应用程序的运行状态。Client提交模式适用于交互式的、需要即时获取应用程序执行结果的场景。在Client提交模式下,应用程序的生命周期与客户端程序紧密相关,如果客户端程序退出或断开连接,应用程序也会被终止。
        2. Cluster提交:在Cluster提交模式下,应用程序的驱动程序运行在YARN集群的一个容器中,由YARN ResourceManager管理和监控应用程序的运行状态。Cluster提交模式适用于长期运行的、不需要与客户端交互的应用程序。在Cluster提交模式下,应用程序的生命周期与YARN集群的生命周期相互独立,即使客户端程序退出或断开连接,应用程序仍然可以继续在集群上运行。

总结:
        Client提交模式适用于交互式和即时响应的场景,应用程序的生命周期与客户端程序相关联。
        Cluster提交模式适用于长期运行的、不需要与客户端交互的应用程序,应用程序的生命周期与YARN集群的生命周期相互独立。

选择Client提交还是Cluster提交取决于应用程序的性质和需求。如果需要与客户端进行交互并实时获取结果,则使用Client提交模式;如果是长期运行的批处理作业或服务,不需要与客户端交互,则使用Cluster提交模式。

十三、Spark 为什么比 MapReduce 快

1、基于内存
学过Spark的应该都知道,Spark是基于内存进行数据处理操作的,而MapReduce则是基于磁盘进行数据处理。
MR的设计:将MapTask的输出作为中间结果,保存到文件当中,随后作为ReduceTask的输入。这样可以提高可靠性,减少了内存的占用,但是牺牲了性能。
Spark的设计:数据在内存当中进行交换(注意是交换,也就是转换算子的操作),但是内存可靠性不如磁盘,所以性能方面比MR要好。
这里需要补充说明一下。
MR只有两种操作:map和reduce;
Spark的操作分为两类:transform和action。

Transform包括:map,filter, flatMap, mapPartitions,mapPartitionsWithIndex, sample, pipe, union, intersection,distinct, groupByKey, reduceByKey,sortByKey, join, cogroup, Cartesian,coalesce,repartition。

Action包括:reduce, collect, count,take,first, takeSample, saveAsTextFile, saveAsSequenceFile,saveAsObjectFile,countByKey,foreach。

而Spark中transform的返回值都是新的RDD,只有执行action操作才会返回结果或者将RDD数据写到存储系统中。即transform操作都是“懒执行”,仅仅只会记录,到action的时候才会真正进行执行。
2、DAG有向无环图
Spark中具有DAG有向无环图,在这个过程中减少了shuffle以及落盘的次数。(快的根本原因)

DAG 相比MapReduce 在大多数情况下可以减少 shuffle 次数。Spark 的 DAGScheduler 相当于一个改进版的 MapReduce,如果计算不涉及与其他节点进行数据交换,Spark 可以在内存中一次性完成这些操作,也就是中间结果无须落盘,减少了磁盘 IO 的操作。但是,如果计算过程中涉及数据交换,Spark也是会把 shuffle 的数据写磁盘的!有一个误区,Spark 是基于内存的计算,所以快,这不是主要原因,要对数据做计算,必然得加载到内存,Hadoop 也是如此,只不过 Spark 支持将需要反复用到的数据给 Cache 到内存中,减少数据加载耗时,所以 Spark 跑机器学习算法比较在行(需要对数据进行反复迭代)。Spark 基于磁盘的计算也是比 Hadoop 快。刚刚提到Spark 的 DAGScheduler 是个改进版的 MapReduce,所以 Spark天生适合做批处理的任务。Hadoop 的 MapReduce 虽然不如 spark 性能好,但是 HDFS 仍然是业界的大数据存储标准。
补充说明一下shuffle
Hadoop中MapReduce 将处理流程划分为:map, spill, merge, shuffle, sort, reduce等阶段,shuffle
是位于map和reduce中间的一个阶段。在 Spark 中,没有这样功能明确的阶段。Spark将用户定义的计算过程转化为一个被称作Job逻辑执行图的有向无环图(DAG),图中的顶点代表RDD,边代表RDD之间的依赖关系。再将这个逻辑执行图转化为物理执行图,具体方法是:从逻辑图后往前推算,遇到ShuffleDependency 就断开,最后根据断开的次数n,将其化分为(n+1)个stage。每个 stage 里面 task 的数目由该 stage 最后一个 RDD 中的 partition 个数决定。因此,Spark的Job的shuffle数是不固定的。
3、粗粒度资源申请
这里简单区分一下粗粒度和细粒度的区别。
粗粒度:静态分配资源:在application 启动时前已经为其分配好了所需的资源,后续不需要再分配资源。
细粒度:按需、动态分配资源:task自己去申请资源,task完成后就立即回收资源。
由于Spark是粗粒度资源申请,所以只有在申请到了资源的情况下,才会执行application,在task执行的过程当中就不需要进行资源申请,task执行快。不过缺点就是不能使集群得到充分的利用。
举个例子:有1000个task,其中999个task已经完成了,1个task特别的慢,这样就相当于999个task的资源空闲浪费了。
而MapReduce是细粒度资源申请,当提交application的时候,task执行时,自己申请资源,自己释放资源,task执行完毕之后,资源立即会被释放,task执行的慢,application执行的相对比较慢。优点是集群资源得到充分利用,缺点是application执行的相对比较慢。

十四、RDD 中 reduceBykey 与 groupByKey 的区别,reduceBykey 底层如何实现的

reduceByKey 和 groupByKey 都是Spark中的转换操作,用于对RDD中的键值对数据进行聚合操作。它们的区别如下:
1. 数据处理方式:
        reduceByKey :按键对数据进行分组,并对每个键对应的值进行聚合操作,将相同键的值通过指定的聚合函数进行合并。它在每个分区内进行局部聚合,然后在全局范围内进行合并。
        groupByKey :按键对数据进行分组,将相同键的值放入一个迭代器中,形成键值对的序
列。它不进行值的聚合操作,只是简单地将相同键的值放在一起。
2. 性能和资源消耗:
        reduceByKey :由于在每个分区内进行局部聚合,它可以减少数据的传输量,提高性能,并
且适用于大规模数据集。它在进行聚合时可以并行处理不同的分区。
        groupByKey :将相同键的值放入一个迭代器中,不进行聚合操作,因此可能导致在处理大
规模数据集时出现数据倾斜问题,增加网络传输和内存消耗。

底层实现: reduceByKey 的底层实现使用了MapReduce中的Combiner函数,在每个分区内进行局部聚合,并生成局部聚合结果。然后将各个分区的局部聚合结果进行全局合并,得到最终的聚合结果。
groupByKey 的底层实现是将数据按键进行哈希分区,并将相同键的值放入同一个分区。然后对每个分区进行迭代,将键值对存储在内存中的迭代器中,形成键值对的序列。

总结:
        reduceByKey 适合进行聚合操作,并在每个分区内进行局部聚合和全局合并,适用于大规模数据集。
        groupByKey 适合按键对数据进行分组,不进行聚合操作,仅将相同键的值放在一起,可能会导致数据倾斜问题,在处理大规模数据集时需要注意。

十五、简述 Spark 的宽窄依赖,以及 Spark 如何划分 Stage,每个 Stage 又根据什么决定 Task 个数

Spark的宽窄依赖(Wide and Narrow Dependencies)是用来描述RDD之间的依赖关系,它对Spark的调度和执行有重要影响。
1. 窄依赖(Narrow Dependency):
        窄依赖指的是父RDD的每个分区只被子RDD的一个或多个分区所使用,即父RDD的每个分区
只对应于子RDD的一个或多个分区。
        在窄依赖下,Spark可以将父RDD的分区直接映射到子RDD的分区,无需进行数据的全局洗
牌(shuffle),提高了计算效率。
2. 宽依赖(Wide Dependency):
        宽依赖指的是父RDD的每个分区被子RDD的多个分区所使用,即父RDD的每个分区对应于子
RDD的多个分区。
        在宽依赖下,Spark需要进行数据的全局洗牌(shuffle),即将数据重新分区和排序,以满
足子RDD的计算需求。

Spark根据依赖关系将任务划分为Stage。一个Stage是一组任务(Task)的集合,这些任务可以并行执行,而且在同一个Stage中的任务都具有相同的依赖关系。
Spark划分Stage的方式是基于宽窄依赖的原则:
        窄依赖:如果一个RDD的所有父RDD都是窄依赖,那么这个RDD将与其父RDD在同一个Stage中。
        宽依赖:如果一个RDD有一个或多个宽依赖的父RDD,那么这个RDD将作为一个新的Stage。

每个Stage的任务个数由Spark根据数据量和任务的执行成本等因素决定。Spark会根据任务的执行时间和资源利用情况等综合因素,动态调整Stage中任务的个数,以提高整体的性能和资源利用率。

总结:
        窄依赖和宽依赖描述了RDD之间的依赖关系,窄依赖下无需进行全局洗牌,宽依赖下需要进行全局洗牌。
        Spark根据宽窄依赖将任务划分为Stage,窄依赖的RDD在同一个Stage中,宽依赖的RDD作为一个新的Stage。
        每个Stage的任务个数由Spark根据数据量和任务的执行成本等因素动态决定。

十六、简述 Spark 中广播变量和累加器的基本原理与用途

Spark中的广播变量(Broadcast Variable)和累加器(Accumulator)是用来进行分布式计算的两个重要工具。
1. 广播变量:
        广播变量是将一个只读的大对象(如大型数据集、共享变量)在集群中的每个节点上缓存一
份,以便在任务执行期间共享和重用。
        广播变量在任务运行过程中只需传输一次,减少了数据传输的开销。
        广播变量的值在集群中的每个节点上保持不变,可以在任务中直接引用,避免了数据的多次
传输和拷贝。
        广播变量常用于将大型的只读数据分发给任务,例如广播一个共享的配置文件、字典、模型
等。
        使用 broadcast() 方法可以将数据转化为广播变量,并在任务中使用。
2. 累加器:
        累加器是一种特殊的变量,可以在分布式计算中进行可并行的累加操作,例如计数、求和等。
        累加器只能进行加法操作,并且只能在驱动程序中读取结果,无法在任务中读取。
        累加器用于收集任务中的统计信息、计数器等,可以在任务执行过程中更新累加器的值,但
只能在任务执行完成后读取。
        累加器的更新是原子操作,可以保证在分布式环境下的线程安全性。
        使用 accumulator() 方法可以创建一个累加器,并在任务中使用。
广播变量和累加器在Spark中的使用可以提高分布式计算的效率和灵活性:
        广播变量通过减少数据传输和拷贝的开销,提高了任务执行的性能。
        累加器可以方便地在任务中进行可并行的累加操作,收集统计信息和计数等。
        广播变量和累加器是分布式计算中常用的工具,可以在处理大规模数据时提供更高效的计算和统计功能。

十七、Spark 数据倾斜处理

Spark数据倾斜是指在分布式计算过程中,某些特定的数据分区或键值对集中在少数任务上,导致计算不均衡,影响整体性能。为了解决数据倾斜问题,可以采取以下几种常见的处理方法:
1. 随机前缀(Random Prefix):
        对于容易发生倾斜的键值对,可以在键的前面添加随机前缀,使其均匀分布到不同的分区
上,减少倾斜程度。
        这种方法通过增加键的唯一性来提高分布性,但可能会导致一些查询操作的复杂性增加。
2. 增加粒度(Increase Granularity):
        将原本倾斜的大粒度数据切分成多个小粒度数据,使其分散到不同的分区上进行并行处理。
        例如,将一个倾斜的键值对拆分为多个具有相同前缀的键值对,通过增加键的细粒度来提高
数据的均衡性。
3. 聚合转换(Aggregation and Transformation):
        针对倾斜键值对,可以通过聚合转换操作将其转化为其他形式的数据结构,减少对应任务的
负载。
        例如,将倾斜的键值对进行聚合操作,将其合并为一个单独的键值对或使用其他数据结构来
存储。
4. 分桶(Bucketing):
        对于倾斜的数据,可以采用分桶的方式将其均匀分散到多个桶(bucket)中,然后对每个桶
进行独立处理。
        分桶可以有效地减少数据倾斜的影响,并提高计算的并行性。
5. 倾斜数据单独处理(Skewed Data Handling):
        针对倾斜的数据,可以将其单独提取出来,采用特定的处理逻辑进行处理。
        例如,可以为倾斜的键值对分配专门的任务进行处理,或者使用单独的算法对其进行处理。

以上方法可以单独使用或结合使用,根据具体情况选择最适合的方法来处理数据倾斜问题。处理数据倾斜需要根据实际情况进行分析和调优,以提高Spark作业的性能和稳定性。

十八、Spark 数据本地化级别与区别

Spark数据本地化级别是指Spark作业在执行时数据存放在哪个位置以提高计算性能和效率。Spark提供了以下几种数据本地化级别:
1. PROCESS_LOCAL(进程级本地化):
        数据本地化在同一个进程内完成。即将需要计算的数据存放在同一个Executor进程的内存中,避免网络传输开销。
        这是最高级别的本地化级别,适用于数据量较小,不需要跨节点传输的情况。
2. NODE_LOCAL(节点级本地化):
        数据本地化在同一个节点内完成。即将需要计算的数据存放在同一个节点的内存中,减少跨节点的数据传输。
        适用于数据量较大,但仍可以容纳在一个节点的内存中的情况。
3. RACK_LOCAL(机架级本地化):
        数据本地化在同一个机架内完成。即将需要计算的数据存放在同一个机架的节点内存中,减少跨机架的数据传输。
        适用于数据量很大,无法完全存放在一个节点内存中的情况。
4. ANY(任意位置本地化):
        数据可以存放在任意节点的内存中。这是最低级别的本地化级别,不要求数据在特定位置本地化。
        适用于数据量非常大,无法在一个节点或机架内存放的情况。

通过设置不同的数据本地化级别,Spark可以根据数据的位置和调度策略来选择最适合的数据本地化方式,减少数据传输开销,提高计算性能和效率。在实际使用中,可以根据集群的网络拓扑和数据分布情况来选择合适的数据本地化级别,以达到最佳的性能优化效果。

十九、看到一个陌生的算子,在不运行代码的情况下,怎么区分它是转换算子和还是行动算子

在Spark中,通常可以通过查看算子的返回类型来区分转换算子和行动算子。Spark中的转换算子返回的是一个新的RDD或DataFrame,而行动算子返回的是具体的结果或触发作业执行的动作。

例如,假设有一个名为 data 的RDD,你看到了以下代码:

val result = data.filter(_ > 0).map(_ * 2).collect()


通过观察代码可以发现, filter 和 map 是连续调用的转换算子,它们返回一个新的RDD,并没有立即执行计算。而最后的 collect 是一个行动算子,它触发了实际的计算并返回最终的结果。
另外,一些常见的行动算子还有 count 、 first 、 take 等,它们都会触发作业的执行并返回相应的结果。
因此,当你遇到一个陌生的算子时,可以查看其返回类型来判断它是转换算子还是行动算子。转换算子返回的是新的RDD或DataFrame,行动算子返回的是具体的结果或触发作业执行的动作。

二十、Spark中的血缘关系

        Spark 根据用户 Application 中的 RDD 的转换算子和行动算子,会生成 RDD 之间的依赖关系,多个 RDD 之间的关系又 形成一条关系链叫做 RDD 的血统(Lineage)。如下图,图中每个猿人都可以看做是一个 RDD 。同时也生成了逻辑上的 DAG(有向无环图)。每一个 RDD 都可以根据其依赖关系一级一级向前回溯重新计算,这便是 Spark 实现容错的一种手段 (因为 RDD 不会保存数据),如果某个 RDD 丢失了,则可以根据血缘关系,从父 RDD 计算得来。

        在Spark中,血缘关系(Lineage)是指每个RDD(弹性分布式数据集)之间的依赖关系,即每个RDD是如何从其他RDD中转换而来的。血缘关系是Spark实现容错机制的核心,可以帮助Spark在出现故障时进行数据恢复。 Spark中的每个RDD都有一个或多个父RDD,每个父RDD都是RDD之间转换的来源。RDD之间的转换包括两种类型:窄依赖和宽依赖。窄依赖是指每个父RDD的一个分区最多只被一个子RDD的一个分区所依赖,例如map、filter等操作;宽依赖是指每个父RDD的一个分区可能被多个子RDD的分区所依赖,例如groupByKey、reduceByKey等操作。 当RDD的一个分区丢失时,Spark可以根据血缘关系重新计算该分区的数据。Spark会根据RDD的转换操作,从父RDD中获取数据并进行计算,直到计算出该分区的数据为止。因此,血缘关系是Spark实现容错机制的关键,保证了Spark的数据可靠性和容错性。 总之,Spark中的血缘关系是指每个RDD之间的依赖关系,是Spark实现容错机制的核心。通过血缘关系,Spark可以在出现故障时进行数据恢复,保证了Spark的数据可靠性和容错性。

二十、Spark的资源或内存管理策略

  1. 资源调度器:Spark支持多种资源调度器,例如Standalone、YARN和Mesos等,可以根据实际情况选择合适的资源调度器。资源调度器可以管理集群中的资源,协调不同应用程序之间的资源竞争。
  2. 内存管理:Spark将内存分为内存池和堆外内存两部分。内存池包括堆内内存和堆外内存,可以用于存储RDD、Shuffle等数据。堆外内存用于存储Spark的网络缓冲区和序列化缓冲区等。Spark通过调整内存池的大小和使用策略来优化内存使用效率。
  3. 内存使用策略:Spark采用了多种内存使用策略,例如Storage Memory和Execution Memory等。Storage Memory用于存储缓存的RDD和Shuffle数据,Execution Memory用于存储执行中的任务所需的内存。Spark可以根据任务的需求动态调整内存的使用策略。
  4. 垃圾回收:Spark使用JVM的垃圾回收机制来回收内存。Spark针对垃圾回收的特点,采用了多种优化策略,例如调整堆内内存的大小、采用G1垃圾回收器等。
  5. 磁盘管理:Spark可以将数据存储在内存中和磁盘中,可以根据数据的大小和使用频率等因素来决定数据存储在内存中还是磁盘中。Spark使用了多种磁盘管理策略,例如内存和磁盘的协同使用、数据的压缩和序列化等。
  6. 总之,Spark的资源或内存管理策略是非常重要的,可以影响到Spark的性能和稳定性。在实际使用中,需要根据具体的场景和需求选择合适的策略,并进行适当的调优和优化。

SQL

一、求部门前三薪资 SQL 实现思路

要实现查询部门前三薪资的 SQL,可以按照以下思路进行实现:
        1. 使用 GROUP BY 子句按照部门进行分组,计算每个部门的平均薪资。
        2. 使用子查询或公用表表达式 (CTE) 获取每个部门的平均薪资,并按照平均薪资进行降序排序。
        3. 使用窗口函数或子查询为每个部门的薪资排名。
        4. 使用条件筛选保留排名前三的记录。
下面是一个示例 SQL 查询语句的实现:

WITH department_avg_salary AS (
    SELECT
        deptno,
        AVG(sal) AS avg_salary
    FROM
        emp
    GROUP BY
        deptno
)
SELECT
    empno,
    ename,
    sal,
    deptno
FROM
    (
        SELECT
            emp.*,
            ROW_NUMBER() OVER (PARTITION BY emp.deptno ORDER BY emp.sal DESC) AS rank
        FROM
            emp   
            JOIN department_avg_salary ON emp.deptno = department_avg_salary.deptno
    ) ranked_employees
WHERE
rank <= 3;

在这个示例中,首先使用 WITH 子句定义了一个名为 department_avg_salary 的公用表表达式,计
算每个部门的平均薪资。然后,使用子查询将每个员工与其所属部门的平均薪资进行连接,并使用窗口函数 ROW_NUMBER() 计算每个部门内员工的薪资排名。最后,在外层查询中筛选出排名前三的员工记录。 

二、连续 7 天登录 SQL 实现过程

1. 首先,假设有一个名为 user_login 的表,其中包含了用户的登录记录,包括用户ID( user_id )和登录日期( login_date )字段。
2. 使用子查询或公用表表达式 (CTE) 来获取每个用户连续登录的天数。在子查询中,使用窗口函数和日期函数来计算每个登录日期与前一天日期的差值(间隔天数),并将结果作为新的列。
3. 使用外层查询来筛选出连续登录天数达到7天的用户记录。
以下是一个示例 SQL 查询语句的实现:

WITH consecutive_logins AS (
    SELECT
        user_id,
        login_date,
        login_date - LAG(login_date) OVER (PARTITION BY user_id ORDER BY login_date)
AS day_diff
    FROM
        user_login
)
SELECT
    user_id,
    MIN(login_date) AS start_date,
    MAX(login_date) AS end_date,
    COUNT(*) AS consecutive_days
FROM
    consecutive_logins
WHERE
    day_diff = 1
GROUP BY
    user_id
HAVING
    COUNT(*) >= 7;

在这个示例中,使用 WITH 子句定义了一个名为 consecutive_logins 的公用表表达式,计算了每个用户的连续登录天数。窗口函数 LAG() 用于获取前一天的登录日期,从而计算出当前登录日期与前一天的差值(间隔天数)。然后,在外层查询中筛选出连续登录天数为1的记录,并使用 GROUP BY 子句按用户ID进行分组。最后,使用 HAVING 子句筛选出连续登录天数达到7天及以上的用户记录。 

三、统计每个员工前三个月的薪水,每月统计一次

1. 确定数据表结构:首先,确定包含员工薪水信息的数据表,表中应至少包含员工编号、薪水日期和薪水金额等字段。
2. 确定统计时间范围:确定需要统计的时间范围,即前三个月的时间段。
3. 筛选数据:使用SQL的 WHERE 子句筛选出在统计时间范围内的薪水记录。可以使用日期函数和比较操作符来筛选出符合条件的记录。
4. 分组和聚合:使用SQL的 GROUP BY 子句按照员工编号进行分组,并使用聚合函数(如 AVG )计算每个员工在每个月的平均薪水。
5. 排序:根据需要,可以使用 ORDER BY 子句对结果进行排序,例如按照员工编号和月份进行排序。

SELECT
    empno,
    YEAR(payment_date) AS year,
    MONTH(payment_date) AS month,
    AVG(amount) AS avg_salary
FROM
    salary
WHERE
    payment_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 3 MONTH)
GROUP BY
    empno, YEAR(payment_date), MONTH(payment_date)
HAVING
    YEAR(payment_date) = YEAR(CURRENT_DATE()) AND MONTH(payment_date) <=
MONTH(CURRENT_DATE())
ORDER BY
    empno, YEAR(payment_date), MONTH(payment_date)

在这个查询中,假设存在名为 salary 的表,包含员工的薪水信息,包括员工编号( empno ),薪水支付日期( payment_date )和薪水金额( amount )等字段。
查询逻辑如下:
        1. 使用 WHERE 子句筛选出支付日期在过去三个月内的薪水记录。
        2. 使用 GROUP BY 子句按照员工编号、年份和月份进行分组。
        3. 使用 HAVING 子句限制结果只包含当前年份以及当前月份或之前的记录,确保只统计前三个月的数据。
        4. 使用 ORDER BY 子句按照员工编号、年份和月份排序结果。
这样可以得到每个员工在前三个月内每个月的平均薪水数据。
请注意,上述查询中使用的表名和字段名是根据你之前提供的表结构假设的,你需要根据实际情况进行相应的调整。另外,该查询假设薪水支付日期是以日期格式存储的,如果实际情况不同,你可能需要调整日期函数的使用方式。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值