跳到主要内容

内存设置

Map join 和输出缓冲区

以下配置键可以说是性能调优最重要的,尤其是在运行带有重 join 的复杂查询时。

  • hive-site.xml 中的 hive.auto.convert.join.noconditionaltask.size 指定触发 map join 的阈值。与 Apache Hive 在内部应用额外公式来调整 hive-site.xml 中指定的值不同,HivePlus 使用值时不进行任何调整。因此,建议用户选择一个比 Apache Hive 推荐值大得多的值。
  • tez-site.xml 中的 tez.runtime.io.sort.mbtez.runtime.unordered.output.buffer.size-mb 指定 Tez 中输出缓冲区的大小。

以下示例显示这些配置键的示例值。

vi conf/hive-site.xml
<property>
<name>hive.auto.convert.join.noconditionaltask.size</name>
<value>4000000000</value>
</property>
vi conf/tez-site.xml
<property>
<name>tez.runtime.io.sort.mb</name>
<value>1040</value>
</property>
<property>
<name>tez.runtime.unordered.output.buffer.size-mb</name>
<value>307</value>
</property>

可以在 Beeline 连接中为每个单独查询覆盖默认值,如下所示。

0: jdbc:hive2://192.168.10.1:9852/> set hive.auto.convert.join.noconditionaltask.size=2000000000;
0: jdbc:hive2://192.168.10.1:9852/> set tez.runtime.io.sort.mb=2000;
0: jdbc:hive2://192.168.10.1:9852/> set tez.runtime.unordered.output.buffer.size-mb=600;
0: jdbc:hive2://192.168.10.1:9852/> !run /home/hive/sample.sql

tez.runtime.io.sort.mb 设置为较大的值可能会导致 OutOfMemoryError,因为 Java VM 无法为输出缓冲区分配连续的内存段。

Caused by: java.lang.OutOfMemoryError: Java heap space...org.apache.tez.runtime.library.common.sort.impl.PipelinedSorter.allocateSpace(PipelinedSorter.java:269)

在这种情况下,用户可以尝试为 tez.runtime.io.sort.mb 设置一个较小的值。

使用空闲内存存储 shuffle 输入

HivePlus 可以利用内存来存储从上游 mapper 传输的 shuffle 输入数据。如果 ContainerWorker 并发运行多个 Task,Task 可能会在耗尽分配给它的全部内存后在 Java 堆中找到空闲内存。tez-site.xml 中的配置键 tez.runtime.use.free.memory.fetched.input(在 MR3 发布版本中设置为 true)控制是否使用此类空闲内存来存储 shuffle 输入数据。如果没有足够的空闲内存可用,shuffle 输入数据会被写入本地磁盘。

在以下情况下,将 tez.runtime.use.free.memory.fetched.input 设置为 true 可以显著减少查询的执行时间:

  1. 多个 Task 可以在 ContainerWorker 中并发运行(例如,单个 ContainerWorker 中的 18 个 Task)。
  2. 查询产生的 shuffle 数据量超过内存容量。
  3. 本地磁盘使用慢速存储(如 HDD)。

以下 HiveServer2 日志显示,在查询执行期间,689,693,191 字节的 shuffle 数据存储在内存中,没有数据被写入本地磁盘。(63,312,923 字节的数据直接从本地磁盘读取,因为 mapper 和 reducer 是并置的。)

2023-12-15T06:59:16,028 INFO [HiveServer2-Background-Pool: Thread-84] mr3.MR3Task: SHUFFLE_BYTES_TO_MEM: 689693191
2023-12-15T06:59:16,028 INFO [HiveServer2-Background-Pool: Thread-84] mr3.MR3Task: SHUFFLE_BYTES_TO_DISK: 0
2023-12-15T06:59:16,028 INFO [HiveServer2-Background-Pool: Thread-84] mr3.MR3Task: SHUFFLE_BYTES_DISK_DIRECT: 63312923

根据 shuffle 数据量和分配给各个 Task 的内存大小,HivePlus 可能会将 shuffle 数据写入本地磁盘。在以下示例中,10,475,065,599 字节的 shuffle 数据被写入本地磁盘。

2023-12-15T07:09:57,472 INFO [HiveServer2-Background-Pool: Thread-99] mr3.MR3Task: SHUFFLE_BYTES_TO_MEM: 142177894794
2023-12-15T07:09:57,472 INFO [HiveServer2-Background-Pool: Thread-99] mr3.MR3Task: SHUFFLE_BYTES_TO_DISK: 10475065599
2023-12-15T07:09:57,472 INFO [HiveServer2-Background-Pool: Thread-99] mr3.MR3Task: SHUFFLE_BYTES_DISK_DIRECT: 13894557846

tez.runtime.free.memory.factor.for.fetched.input

tez-site.xml 中的配置键 tez.runtime.free.memory.factor.for.fetched.input 指定一个乘数,用于计算存储 shuffle 输入数据的每个 LogicalInput 的空闲内存总量:

  • tez.runtime.free.memory.factor.for.fetched.input * 分配给单个 Task 的内存(hive.mr3.map.task.memory.mbhive.mr3.reduce.task.memory.mb

tez.runtime.free.memory.factor.for.fetched.input 的默认值是 2。将其设置为更大的值(例如 6)以更积极地使用空闲内存。

消除无序数据的本地磁盘写入

对于通过无序边获取的 shuffle 输入数据,下游 reducer 可以通过在 tez-site.xml 中将配置键 tez.runtime.shuffle.unordered.memory.streaming 设置为 true 来完全消除本地磁盘写入。如果没有足够的内存来存储传入数据,fetcher 会暂时停滞,直到之前获取的数据被消费并从内存中释放。

当本地磁盘相对较慢时(例如 HDD),此优化特别有益。然而,使用快速本地磁盘(例如 SSD)时,执行时间可能会略有增加。

使用空闲内存存储 shuffle 输出

HivePlus 还可以利用空闲内存来存储要传输到下游 reducer 的 shuffle 输出数据。tez-site.xml 中的配置键 tez.runtime.use.free.memory.writer.output(在 MR3 发布版本中设置为 true)控制是否使用空闲内存来存储 shuffle 输出数据。如果没有足够的空闲内存可用,shuffle 输出数据会被溢出到本地磁盘。

信息

目前 tez.runtime.use.free.memory.writer.output 仅在使用流水线 shuffle 时有效(tez.runtime.pipelined-shuffle.enabled 设置为 true)。

与将 shuffle 输入数据存储在内存中相比,将 shuffle 输出数据存储在内存中可以更有效地减少执行时间,因为相同的输出数据通常被下游 reducer 读取多次。

当使用空闲内存来存储 shuffle 输出数据时,强烈建议hive-site.xml 中的配置键 hive.mr3.delete.vertex.local.directory 设置为 true。默认情况下,ContainerWorker 仅在每个查询完成后删除中间数据。如果 shuffle 输出数据写入本地磁盘,此默认行为是可以接受的。但是,如果 shuffle 输出数据存储在内存中,一旦所有下游 reducer 完成,该内存就会被浪费。通过将 hive.mr3.delete.vertex.local.directory 设置为 true,MR3 会在所有下游 Vertex 完成后立即删除 Vertex 生成的中间数据。例如,在下图中,Map 1 和 Map2 可以在 Reduce 1 完成後删除其中间数据。

hive.k8s.delete.vertex.local

在内部,HiveServer2 将 MR3 配置键 mr3.am.notify.destination.vertex.complete 设置为 true,ContainerWorker 会收到每个单独 Vertex 的所有下游 Vertex 完成的通知。用户可以在启动 HiveServer2 之前在 hive-site.xml 中将 hive.mr3.delete.vertex.local.directory 设置为 true,也可以在 Beeline 中提交查询之前覆盖其值。

需要注意的是,如果配置键 hive.mr3.delete.vertex.local.directory 设置为 true,fetch 失败可能导致 Vertex 重新运行一直级联到叶子 Vertex。在上图中,假设 Reduce 1 完成其所有 Task 后,Reduce 2 的 TaskAttempt 报告 fetch 失败。然后发生 Vertex 重新运行,Reduce 1 创建一个新的 TaskAttempt。然而,新的 TaskAttempt 发现没有输入数据可用,因为 Map 1 和 Map 2 已经删除了它们的中间数据。因此,Map 1 和 Map 2 重新执行其所有 Task,这又导致其祖先的 Vertex 重新运行。

提示

当执行可能偶尔触发容错的长时间运行的批处理查询时,用户不应使用空闲内存来存储 shuffle 输出(将 hive.mr3.delete.vertex.local.directory 设置为 true)。

Tez 中的软引用

提示

仅推荐高级用户在生产环境中完成基本性能调优后启用软引用。

在内存相对于核心数量充足的集群中(例如,每个核心 12GB 内存),对 Tez 中 PipelinedSorter 分配的 ByteBuffer 使用软引用可以产生明显的差异。具体来说,在 tez-site.xml 中将配置键 tez.runtime.pipelined.sorter.use.soft.reference 设置为 true 会为 PipelinedSorter 中分配的 ByteBuffer 创建软引用,并允许这些引用在同一 ContainerWorker 中运行的所有 TaskAttempt 之间重用,从而减轻垃圾回收器的压力。然而,当分配给每个 ContainerWorker 的内存较小时,使用软引用不太可能提高性能。

vi conf/tez-site.xml
<property>
<name>tez.runtime.pipelined.sorter.use.soft.reference</name>
<value>true</value>
</property>

在使用软引用的情况下,用户应为 mr3-site.xml 中配置键 mr3.container.launch.cmd-opts 追加 Java VM 选项 SoftRefLRUPolicyMSPerMB(以毫秒为单位)。否则,ContainerWorker 使用 SoftRefLRUPolicyMSPerMB 的默认值 1000。在以下示例中,我们将 SoftRefLRUPolicyMSPerMB 设置为 25 毫秒:

vi conf/mr3-site.xml
<property>
<name>mr3.container.launch.cmd-opts</name>
<value>... -XX:SoftRefLRUPolicyMSPerMB=25</value>
</property>