Corrigir um erro de falta de memória do Apache Hive no Azure HDInsight

Saiba como corrigir um erro de falta de memória (OOM) do Apache Hive ao processar tabelas grandes definindo as configurações de memória do Hive.

Executar a consulta do Apache Hive em tabelas grandes

Um cliente executou uma consulta do Hive:

SELECT
    COUNT (T1.COLUMN1) as DisplayColumn1,
    …
    …
    ….
FROM
    TABLE1 T1,
    TABLE2 T2,
    TABLE3 T3,
    TABLE5 T4,
    TABLE6 T5,
    TABLE7 T6
where (T1.KEY1 = T2.KEY1….
    …
    …

Algumas nuances desta consulta:

  • T1 é um alias para uma tabela grande, TABLE1, que tem muitos tipos de coluna STRING.
  • Outras tabelas não são tão grandes, mas têm muitas colunas.
  • Todas as tabelas estão unidas umas às outras, em alguns casos com várias colunas na TABELA1 e outras.

A consulta do Hive levou 26 minutos para ser concluída em um cluster A3 HDInsight de 24 nós. O cliente notou as seguintes mensagens de aviso:

    Warning: Map Join MAPJOIN[428][bigTable=?] in task 'Stage-21:MAPRED' is a cross product
    Warning: Shuffle Join JOIN[8][tables = [t1933775, t1932766]] in Stage 'Stage-4:MAPRED' is a cross product

Usando o mecanismo de execução Apache Tez. A mesma consulta foi executada por 15 minutos e, em seguida, lançou o seguinte erro:

    Status: Failed
    Vertex failed, vertexName=Map 5, vertexId=vertex_1443634917922_0008_1_05, diagnostics=[Task failed, taskId=task_1443634917922_0008_1_05_000006, diagnostics=[TaskAttempt 0 failed, info=[Error: Failure while running task:java.lang.RuntimeException: java.lang.OutOfMemoryError: Java heap space
        at
    org.apache.hadoop.hive.ql.exec.tez.TezProcessor.initializeAndRunProcessor(TezProcessor.java:172)
        at org.apache.hadoop.hive.ql.exec.tez.TezProcessor.run(TezProcessor.java:138)
        at
    org.apache.tez.runtime.LogicalIOProcessorRuntimeTask.run(LogicalIOProcessorRuntimeTask.java:324)
        at
    org.apache.tez.runtime.task.TezTaskRunner$TaskRunnerCallable$1.run(TezTaskRunner.java:176)
        at
    org.apache.tez.runtime.task.TezTaskRunner$TaskRunnerCallable$1.run(TezTaskRunner.java:168)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAs(Subject.java:415)
        at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1628)
        at
    org.apache.tez.runtime.task.TezTaskRunner$TaskRunnerCallable.call(TezTaskRunner.java:168)
        at
    org.apache.tez.runtime.task.TezTaskRunner$TaskRunnerCallable.call(TezTaskRunner.java:163)
        at java.util.concurrent.FutureTask.run(FutureTask.java:262)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:745)
    Caused by: java.lang.OutOfMemoryError: Java heap space

O erro permanece ao usar uma máquina virtual maior (por exemplo, D12).

Depurar o erro de falta de memória

Nossas equipes de suporte e engenharia juntas descobriram que um dos problemas que causavam o erro de falta de memória era um problema conhecido descrito no Apache JIRA:

"Quando hive.auto.convert.join.noconditionaltask = true verificamos noconditionaltask.size e se a soma dos tamanhos das tabelas na junção do mapa for menor que noconditionaltask.size o plano geraria uma junção do mapa, o problema com isso é que o cálculo não leva em conta a sobrecarga introduzida pela implementação HashTable diferente, pois os resultados se a soma dos tamanhos de entrada for menor do que o tamanho da tarefa nocondicional por uma pequena margem as consultas atingirão o OOM."

A tarefa hive.auto.convert.join.noconditionaltask no arquivo hive-site.xml foi definida como true:

<property>
    <name>hive.auto.convert.join.noconditionaltask</name>
    <value>true</value>
    <description>
            Whether Hive enables the optimization about converting common join into mapjoin based on the input file size.
            If this parameter is on, and the sum of size for n-1 of the tables/partitions for a n-way join is smaller than the
            specified size, the join is directly converted to a mapjoin (there is no conditional task).
    </description>
</property>

É provável que a junção de mapas tenha sido a causa do erro de memória do Java Heap Space. Como explicado na postagem do blog Configurações de memória do Hadoop Yarn no HDInsight, quando o mecanismo de execução Tez é usado, o espaço de pilha usado na verdade pertence ao contêiner Tez. Veja a imagem a seguir descrevendo a memória do contêiner Tez.

Tez container memory diagram: Hive out of memory error.

Como a postagem do blog sugere, as duas configurações de memória a seguir definem a memória do contêiner para a pilha: hive.tez.container.size e hive.tez.java.opts. Pela nossa experiência, a exceção de falta de memória não significa que o tamanho do contêiner seja muito pequeno. Isso significa que o tamanho da pilha Java (hive.tez.java.opts) é muito pequeno. Então, sempre que você vê fora da memória, você pode tentar aumentar hive.tez.java.opts. Se necessário, poderá ter de aumentar hive.tez.container.size. A configuração java.opts deve ser em torno de 80% do container.size.

Nota

A configuração hive.tez.java.opts deve ser sempre menor que hive.tez.container.size.

Como uma máquina D12 tem 28 GB de memória, decidimos usar um tamanho de contêiner de 10 GB (10240 MB) e atribuir 80% a java.opts:

SET hive.tez.container.size=10240
SET hive.tez.java.opts=-Xmx8192m

Com as novas configurações, a consulta foi executada com êxito em menos de 10 minutos.

Próximos passos

Obter um erro OOM não significa necessariamente que o tamanho do contêiner é muito pequeno. Em vez disso, você deve definir as configurações de memória para que o tamanho da pilha seja aumentado e seja pelo menos 80% do tamanho da memória do contêiner. Para otimizar consultas do Hive, consulte Otimizar consultas do Apache Hive para Apache Hadoop no HDInsight.