Hive SQL 优化大全(参数配置、语法优化)

文章目录

    • 参数配置优化
      • [yarn-site.xml 配置文件优化](#yarn-site.xml 配置文件优化)
      • [mapred-site.xml 配置文件优化](#mapred-site.xml 配置文件优化)
    • [分组聚合优化 ------ Map-Side](#分组聚合优化 —— Map-Side)
    • [Join 优化](#Join 优化)
      • [Map Join](#Map Join)

服务器环境说明

机器名称 内网IP 内存 CPU 承载服务
master 192.168.10.10 8 4 NodeManager、DataNode、NameNode、JobHistoryServer、Hive、HiveServer2、MySQL
slave1 192.168.10.11 8 4 NodeManager、DataNode、ResourceManager
slave2 192.168.10.12 8 4 NodeManager、DataNode、SecondaryNameNode

操作系统均为:CentOS 7.5

组件版本

  • jdk 1.8
  • mysql 5.7
  • hadoop 3.1.3
  • hive 3.1.2

参数配置优化

下面以我的集群配置为例来进行优化,请按说明根据实际需求、节点情况进行灵活调整。

yarn-site.xml 配置文件优化

参数一

该参数指定了 NodeManager 可以分配给该节点上的 YARN 容器的最大内存量(以 MB 为单位),默认 8G

xml 复制代码
<property>
    <name>yarn.nodemanager.resource.memory-mb</name>
    <value>6144</value>
</property>

我的每台服务器内存为 8 G,这里给 NodeManager 分配 6 G 内存,我们必须考虑给系统以及其它服务预留内存。

注意,该参数不能超过单台服务器的总内存。

参数二

该参数指定了 NodeManager 在 YARN 集群中的每个节点上可以分配给容器的虚拟 CPU 核心数量,默认值为: 8

增加它可以提高容器的并行性和性能,但也可能导致 CPU 资源过度分配。减小它可能会限制容器的性能,但可以确保更多的容器在集群上同时运行。

xml 复制代码
<property>
    <name>yarn.nodemanager.resource.cpu-vcores</name>
    <value>6</value>
</property>

我的每台服务器物理 CPU 核数为 4 ,这里虚拟为 6 核,提高并发度。

参数三

该参数定义了 YARN 调度器允许的单个容器的最大内存分配。

这有助于确保在集群中合理分配内存资源,以防止某个应用程序或容器占用过多的内存,导致性能问题或资源争用。

该参数配置一般为 yarn.nodemanager.resource.memory-mb 的四分之一,结果最好能被 1024 整除。

xml 复制代码
<property>
    <name>yarn.scheduler.maximum-allocation-mb</name>
    <value>2048</value>
</property>

上面设置 yarn.nodemanager.resource.memory-mb 的配置是 6G6144 / 4 = 1536,显然 1536 无法被 1024 整除,所以这里直接设置为 2G,向上取整。

参数四

该参数定义了 YARN 调度器允许的单个容器的最小内存分配,默认为 1G

xml 复制代码
<property>
    <name>yarn.scheduler.minimum-allocation-mb</name>
    <value>512</value>
</property>

这里直接调为 512MB 就行了,如果内存很多,可以往上调。

参数五

分配给单个容器的最小与最大虚拟核心数量。

xml 复制代码
<!-- 容器最小虚拟核心数 -->
<property>
        <name>yarn.scheduler.minimum-allocation-vcores</name>
        <value>1</value>
</property>

<!-- 容器最大虚拟核心数 -->
<property>
        <name>yarn.scheduler.maximum-allocation-vcores</name>
        <value>2</value>
</property>

根据单节点虚拟总核心数来进行配置,最小设为 1 个,最大设置为虚拟总核心的四分之一,上面设置虚拟核心为 6 个,这里向上取整,所以最大设置为 2 个。

扩展配置1

设置 NodeManager 是否启用虚拟内存检查,默认值:true(启用虚拟内存检查)。

xml 复制代码
<property>
	<name>yarn.nodemanager.vmem-check-enabled</name>
	<value>false</value>
</property>

当设置为 true 时(默认值),NodeManager 将启用虚拟内存检查。这意味着 YARN 应用程序的每个容器将受到虚拟内存限制的限制,一旦超过就会直接 kill 掉该容器。

当设置为 false 时,NodeManager 将禁用虚拟内存检查。这意味着容器将不会受到虚拟内存的限制,容器可以使用尽其所能的虚拟内存,但这可能会增加系统的风险,因为应用程序可以在不受约束的情况下使用虚拟内存,可能导致系统不稳定。

根据当前集群环境用途自行决断吧,学习阶段尽量设置为 false,不然可能会导致很多任务都跑不了,直接被 kill 掉。

扩展配置2

用于设置虚拟内存与物理内存之间的比率,默认为 2.1 倍。

这个参数的目的是限制应用程序可以使用的虚拟内存量,以避免某个应用程序无限制地占用虚拟内存资源,导致其他任务和应用程序受影响。

xml 复制代码
<property>
	<name>yarn.nodemanager.vmem-pmem-ratio</name>
	<value>2.1</value>
</property>

扩展配置应用场景

未关闭虚拟内存检查之前,由于虚拟内存不足,在运行任务时,你可能会看到如下所示的 Hive SQL 报错信息:

Execution Error,return code 2 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask

在历史服务器中,查看详细报错信息如下:

2023-09-01 20:39:05.542\]Container \[pid=64762,containerID=container_1693562800213_0002_01_000006\] is running 324684288B beyond the 'VIRTUAL' memory limit. Current usage: 237.6 MB of 1 GB physical memory used; 2.4 GB of 2.1 GB virtual memory used. Killing container. 提示,虚拟内存超出限制,当前容器正在使用 `1G` 物理内存中的 `237.6MB` 内存,正在使用 `2.1G` 虚拟内存中的 `2.4G` 虚拟内存,显然这超出了限制,那么为什么会出现这种情况呢? 这是因为我只给单个 Map 和 Reduce 任务分配了 `1G` 内存,所以这 `1G` 内存按照默认物理内存与虚拟内存转化率(`yarn.nodemanager.vmem-pmem-ratio`)来算,`1024 * 2.1 = 2150.4`,所以对应着虚拟内存最大为 `2.1G`,但是由于这个任务需要 `2.4G` 虚拟内存才可以运行,所以导致容器被直接 `kill` 掉。 这里不建议直接将虚拟内存比率调大,可以直接关闭虚拟内存检查来进行解决(实战别关,实战内存一般都很大,关了反而会影响系统稳定性)。 ### mapred-site.xml 配置文件优化 **参数一** 定义了单个 Map 与 Reduce 任务使用的最大内存分配量,以 `MB` 为单位,默认值都为 `1024`。 注意,这两项参数都不可以超过单个容器的最大内存分配量(`yarn.scheduler.maximum-allocation-mb`),避免单个 Mapper 或者 Reduce 任务使用超过 YARN 调度器允许的最大内存,导致任务运行异常。 ```xml mapreduce.map.memory.mb 1024 mapreduce.reduce.memory.mb 1024 ``` 前面我们设置单个容器的最大内存分配量为 `2G`,所以这里设置为默认值 `1G` 更合理,如果有条件,设置为 `2G` 更好。 其实,实际比例应该设置为 `8:1`(【单个容器最大内存分配量】 : 【单个 Map 与 Reduce 任务使用的最大内存分配量】)。但是说回来,没有绝对的比例,设置为 `2G` 也够用了,根据实际情况来吧。 **参数二** 定义了单个 Map 与 Reduce 任务使用的最大虚拟核心数,默认值都为 `1`。 注意,这两项参数都不可以超过单个容器的最大虚拟核心数(`yarn.scheduler.maximum-allocation-vcores`),避免单个 Mapper 或者 Reduce 任务使用超过 YARN 调度器允许的最大虚拟核心数,导致任务运行异常。 ```xml mapreduce.map.cpu.vcores 1 mapreduce.reduce.cpu.vcores 1 ``` 前面我们设置单个容器的最大虚拟核心数为 `2`,所以这里设置为默认值 `1` 更合理,根据实际条件向上调吧。 ## 分组聚合优化 ------ Map-Side 在 Hadoop MapReduce 中,Map-Side 聚合是一种优化技术,用于在 Map 任务阶段进行部分数据聚合,以减少数据传输到 Reducer 任务的量。 Map-Side 聚合是一种有效的性能优化技术,可以减少 MapReduce 作业中的数据传输和磁盘写入/读取,从而提高作业的执行速度。 ### 优化参数解析 以下是在 Hive 中设置 Map-Side 聚合相关的关键参数,以及它们的详细解释: **1. hive.map.aggr** * 默认值:`true` * 用于启用 Map-Side 聚合功能,默认开启。Hive 会尝试在 Map 任务中执行一些简单的聚合操作,例如 SUM、COUNT 等,以减少 Map 输出的数据量。这可以降低作业的整体负载,提高查询性能,特别是对于一些聚合型的查询。 这可能会降低 Reducer 的负载,但同时会增加 Map 任务的计算负担。如果查询需要更复杂的聚合操作或跨多个分组键的聚合,可能无法完全受益于 Map-Side 聚合。 **2. hive.map.aggr.hash.min.reduction** * 默认值:`0.5` * 这个参数的值是一个浮点数,表示 Map-Side 聚合的最小减少量的阈值。阈值的范围是 `0` 到 `1` 之间,`0` 表示不启用 Map-Side 聚合,`1` 表示始终启用 Map-Side 聚合。 如果设置为 `0.5`,表示只有当 Map 任务中的聚合操作可以减少至少 `50%` 的数据量时,才会启用 Map-Side 聚合。 如果设置为 `1`,表示无论聚合操作能否减少数据量,都始终启用 Map-Side 聚合。 如果设置为 `0`,表示禁用 Map-Side 聚合,不管聚合操作是否有助于减少数据传输到 Reducer 的数量。 要注意的是,过大的阈值可能导致 Map-Side 聚合不经常发生,从而减少其性能优势。过小的阈值可能导致频繁的 Map-Side 聚合,增加了 Map 任务的计算开销。因此,合适的阈值应该基于具体查询和数据集的特点进行调整和测试。 **3. hive.groupby.mapaggr.checkinterval** * 默认值:`100000` * 控制 Map-Side 聚合的检查条数,用于验证任务是否满足聚合条件。 通俗来说就是,在开启 Map-Side 聚合操作后,当我们执行了聚合操作,在 Map 阶段系统会自动取前 `100000` 条数据取进行判断,此时,会出现下面两种情况: * 如果其中的聚合键值大部分都一样,那么就会执行 Map-Side 聚合操作。 * 如果大部分聚合键值都不一样,那么就不会进行 Map-Side 聚合操作。 这个判断很容易会受到数据的分布影响,假设前 `100000` 行数据前面都不一样,只是因为数据量大,但其实后面有很多聚合键值都一样的数据,所以这就会造成判断不符合 Map-Side 聚合操作。 这种情况我们就需要根据实际情况进行判断了,如果聚合后数据量确实少了一半,我们可以强制开启 Map-Side 聚合操作。 **4. hive.map.aggr.hash.force.flush.memory.threshold** * 默认值:`0.9` * 用于控制 Map-Side 聚合的内存阈值,指定 Map 任务在进行 Map-Side 聚合时,何时强制将内存中的数据写入磁盘以释放内存。 当 Map 任务的内存中数据占用达到或超过这个阈值时,Map 任务将强制将内存中的数据写入磁盘以释放内存,从而避免 OOM(内存溢出)错误。 ### 优化案例 **未开启 Map-Side 聚合执行** ```shell set hive.map.aggr = false; ``` 执行如下 Hive SQL,数据量大约 `1000w` 行: ```sql select product_id, count(1) from order_detail group by product_id; ``` 执行计划: ```shell STAGE DEPENDENCIES: Stage-1 is a root stage Stage-0 depends on stages: Stage-1 "" STAGE PLANS: Stage: Stage-1 Map Reduce Map Operator Tree: TableScan alias: order_detail Statistics: Num rows: 13066777 Data size: 11760099340 Basic stats: COMPLETE Column stats: NONE Select Operator expressions: product_id (type: string) outputColumnNames: product_id Statistics: Num rows: 13066777 Data size: 11760099340 Basic stats: COMPLETE Column stats: NONE Reduce Output Operator key expressions: product_id (type: string) sort order: + Map-reduce partition columns: product_id (type: string) Statistics: Num rows: 13066777 Data size: 11760099340 Basic stats: COMPLETE Column stats: NONE Execution mode: vectorized Reduce Operator Tree: Group By Operator aggregations: count() keys: KEY._col0 (type: string) mode: complete " outputColumnNames: _col0, _col1" Statistics: Num rows: 6533388 Data size: 5880049219 Basic stats: COMPLETE Column stats: NONE File Output Operator compressed: false Statistics: Num rows: 6533388 Data size: 5880049219 Basic stats: COMPLETE Column stats: NONE table: input format: org.apache.hadoop.mapred.SequenceFileInputFormat output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe "" Stage: Stage-0 Fetch Operator limit: -1 Processor Tree: ListSink ``` 从执行计划中可以看出,这段代码在 Map 阶段并没有进行聚合操作,在进行 Reduce 操作前,数据量并未发生任何变化。 执行结果,运行 `43` 秒: ![在这里插入图片描述](https://file.jishuzhan.net/article/1697815159185084418/1f9812175fee4072a44853cbeb646e5d.png) **开启 Map-Side 聚合执行** ```shell set hive.map.aggr = true; # 其余参数都保持默认 ``` 执行同上 Hive SQL: ```sql select product_id, count(1) from order_detail group by product_id; ``` 执行计划: ```shell STAGE DEPENDENCIES: Stage-1 is a root stage Stage-0 depends on stages: Stage-1 "" STAGE PLANS: Stage: Stage-1 Map Reduce Map Operator Tree: TableScan alias: order_detail Statistics: Num rows: 13066777 Data size: 11760099340 Basic stats: COMPLETE Column stats: NONE Select Operator expressions: product_id (type: string) outputColumnNames: product_id Statistics: Num rows: 13066777 Data size: 11760099340 Basic stats: COMPLETE Column stats: NONE Group By Operator aggregations: count() keys: product_id (type: string) mode: hash " outputColumnNames: _col0, _col1" Statistics: Num rows: 13066777 Data size: 11760099340 Basic stats: COMPLETE Column stats: NONE Reduce Output Operator key expressions: _col0 (type: string) sort order: + Map-reduce partition columns: _col0 (type: string) Statistics: Num rows: 13066777 Data size: 11760099340 Basic stats: COMPLETE Column stats: NONE value expressions: _col1 (type: bigint) Execution mode: vectorized Reduce Operator Tree: Group By Operator aggregations: count(VALUE._col0) keys: KEY._col0 (type: string) mode: mergepartial " outputColumnNames: _col0, _col1" Statistics: Num rows: 6533388 Data size: 5880049219 Basic stats: COMPLETE Column stats: NONE File Output Operator compressed: false Statistics: Num rows: 6533388 Data size: 5880049219 Basic stats: COMPLETE Column stats: NONE table: input format: org.apache.hadoop.mapred.SequenceFileInputFormat output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe "" Stage: Stage-0 Fetch Operator limit: -1 Processor Tree: ListSink ``` 从执行计划中可以看到,我们虽然已经开启了 Map-Side 聚合操作,在 Mape 阶段出现了 Group By 聚合操作,可是我们进入 Reduce 阶段前的数据量并没有变少,和之前一样。 这是因为在上面提到的数据分布影响造成的问题,因为 `hive.groupby.mapaggr.checkinterval` 默认只检查前 `100000` 行来验证是否进行 Map-Side 聚合操作,由于这里数据量比较大,导致前 `100000` 行的聚合键值大部分不相同(它觉得即使对这 `100000` 行数据进行了聚合操作,也达不到数据量减少 `50%` 的程度),所以它才没有进行 Map-Side 聚合操作,在这种情况下,需要强制开启 Map-Side 聚合操作。 ```sql set hive.map.aggr.hash.min.reduction = 1; ``` 执行结果,运行 `29` 秒: ![在这里插入图片描述](https://file.jishuzhan.net/article/1697815159185084418/5098d1a483614d9abfb9fc94fbf53053.png) 可见,速度的确得到大幅提升! 从历史服务器中查看执行该任务的一个 Map 中可以看出,在 Map 阶段的确发生了 Map-Side 聚合操作。 ![在这里插入图片描述](https://file.jishuzhan.net/article/1697815159185084418/98a26e5ddc6f42828caa71813b8142d7.png) Map 阶段数据量前后对比少了很多,这就是 Map-Side 的玩法。 ## Join 优化 ### Map Join Map Join 是 Hive 中的一种特殊类型的 Join,它用于处理大型维度表与较小事实表之间的连接操作,以提高查询性能。Map Join 利用了 Hive 中的 Map-Side Join 机制,将维度表加载到内存中,并在 Map 阶段执行连接操作,从而减少了数据的传输和磁盘读取,提高了查询性能。 **相关参数** 1. `hive.auto.convert.join`,默认值为 `true`,以允许 Hive 自动将适合 Map Join 的连接转换为 Map Join。 2. `hive.mapjoin.smalltable.filesize`,默认值为 `25000000`(以字节为单位,差不多`25MB`),用于指定哪些表被视为小表的文件大小阈值,如果表的大小低于此阈值,它将被认为是小表,可以用于 Map Join。 3. `hive.mapjoin.bucket.cache.size`,默认值为 `100`(表示桶缓存中最多可以存储 100 个桶表),用于配置 Map Join 时的桶缓存大小,桶缓存用于缓存桶表以提高连接性能。 4. `hive.auto.convert.join.noconditionaltask` ,默认值为 `true`,在无条件限制的情况下,允许将 Join 转换为 Map Join。【当连接操作包含条件限制时,Hive 不会自动将其优化为 Map Join 或 Sort Merge Join。它会按照连接操作中指定的条件来执行连接,确保满足条件的行被正确连接,例如 Where 子句等】 5. `hive.auto.convert.join.noconditionaltask.size`,默认值为 `10000000` (以字节为单位,差不多 `10MB`),设置在没有条件限制的情况下自动将连接操作转换为 Map Join 或 Sort Merge Join 的大小阈值。 这个参数的作用是定义了一个阈值,当连接操作中的中间表(即中间结果临时表)大小低于该阈值时,Hive 将自动尝试将连接操作转换为 Map Join 或 Sort Merge Join,以提高性能。 **工作原理** 1. Map Join 需要一个小的维度表(通常是维度表)和一个较大的事实表。 2. 在执行查询之前,Hive 会将维度表加载到 Map 任务的内存中。 3. 当执行 Join 操作时,Map 任务会将事实表的每一行与内存中的维度表进行连接。由于维度表已加载到内存中,连接操作非常快速。 4. Map 任务将连接后的结果发送给 Reducer 进行进一步的处理(如果需要的话)。 **示例** 假设有一个销售数据的事实表 `sales` 和一个产品维度表 `products`,我们想要获取每个销售记录的产品名称。`products` 表是较小的,可以加载到内存中。 ```sql -- 创建产品维度表 CREATE TABLE products ( product_id INT, product_name STRING ); -- 创建销售事实表 CREATE TABLE sales ( sale_id INT, product_id INT, sale_amount DOUBLE ); -- 使用 Map Join 进行查询 SELECT s.sale_id, p.product_name, s.sale_amount FROM sales s JOIN products p ON s.product_id = p.product_id; ``` Map Join 在处理小型维度表与大型事实表的连接时非常有用,但并不适用于所有情况。对于两个大型表之间的连接,Map Join 可能不是最佳选择,因为它可能导致内存不足的问题。因此,在使用 Map Join 时,应根据表的大小和系统资源来评估是否适合。

相关推荐
Tyler先森27 分钟前
Oracle数据库数据编程SQL<5 正则表达式函数*****>
数据库·sql·oracle
haaaaaaarry38 分钟前
【SQL】子查询详解(附例题)
java·数据库·sql
張萠飛1 小时前
flink cdc的source数据流如何配置事件时间,如何设置时间语义,分配时间戳并生成水位线
大数据·flink
一只专注api接口开发的技术猿1 小时前
京东API智能风控引擎:基于行为分析识别恶意爬虫与异常调用
大数据·开发语言·前端·爬虫
三品PLM系统4 小时前
三品PLM研发管理软件如何构筑制造企业全产品生命周期管理?
大数据·运维·人工智能·安全·制造
chat2tomorrow5 小时前
数据仓库的核心架构与关键技术(数据仓库系列二)
数据仓库·低代码·架构·spark·bi·数据中台·sql2api
Always_away5 小时前
数据库系统概论|第三章:关系数据库标准语言SQL—课程笔记1
数据库·笔记·sql·学习
viperrrrrrrrrr76 小时前
大数据学习(100)-kafka详解
大数据·学习·kafka
小吕学编程6 小时前
企业级开发SpringBoost玩转Elasticsearch
大数据·spring boot·elasticsearch·搜索引擎
编程在手天下我有7 小时前
一文读懂数据仓库:从概念到技术落地
数据仓库