Aprendizaje de chispa de 0 a 1 (10) -Ajuste de chispa (1) -Ajuste de sistema y código

1. Ajuste de recursos

1.1 Configurar la CPU y la memoria al construir un clúster Spark

Configure spark-env.sh en la configuración del paquete de instalación de Spark

Configuración Descripción Defaults
SPARK_WORKER_CORES Número de núcleos de CPU disponibles para el trabajo Todos los núcleos de CPU disponibles
SPARK_WORKER_INSTANCES Número de trabajadores corriendo en cada máquina 1
SPARK_WORKER_CORES × SPARK_WORKER_INSTANCES Núcleos totales por máquina
SPARK_WORKER_MEMORY Capacidad de memoria disponible para operación 1G
SPARK_WORKER_INSTANCES × SPARK_WORKER_MEMORY Cantidad máxima de memoria utilizada por cada nodo
SPARK_DAEMON_MEMORY Espacio de memoria asignado a los demonios maestro y trabajador de Spark 512M

1.2 Asignar más recursos a la solicitud al enviar la solicitud

Enviar opciones de comando:

每个executor使用的core数,spark on yarn 默认为1,standalone默认为worker上所有可用的core。
--executor-cores  
每个executor内存大小(例如:2G),默认1G
--executor-memory
executor使用的总核数,仅限于SparkStandalone、Spark on Mesos模式
--total-executor-cores

2. Sintonización paralela

Principio: un núcleo generalmente asigna de 2 a 3 tareas, y cada tarea generalmente procesa datos 1G.

Formas de mejorar el paralelismo:

  1. Si los datos leídos están en HDFS, reduzca el tamaño del bloque.

  2. sc.textFile(path,numPartitions)

  3. sc.parallelize(list, numPartitions)Generalmente utilizado para pruebas

  4. coalesce, repartitionPuede aumentar el número de particiones RDD.

  5. Modificar la información de configuración:

    spark.default.parallelism:4: No establezca el número total de núcleos ejecutores predeterminados

    spark.sql.shuffle.partitions 200

  6. Particionador personalizado

3. Ajuste de código

3.1 Evite la creación de RDD duplicados

val rdd1 = sc.textFile("xxx")
val rdd2 = sc.textFile("xxx")

No hay diferencia en la eficiencia de ejecución, pero el código es desordenado.

  1. En otros trabajos, el operador de persistencia debe usarse para el RDD usado repetidamente

    • cache:MEMORY_ONLY

    • persistir:

      MEMORY_ONLY

      MEMORY_ONLY_SER

      MEMORY_AND_DISK_SER

      Generalmente no elija un nivel de persistencia con _2

    • control

      Si el tiempo de cálculo de un RDD es relativamente largo o el cálculo es más complicado, generalmente guarde el resultado del cálculo de este RDD en HDFS, para que los datos estén más seguros.

      Si un RDD tiene una dependencia muy larga, también se utilizará un punto de control, que cortará la dependencia y mejorará la eficiencia de la tolerancia a fallos.

3.2 Intente utilizar variables de difusión

Durante el proceso de desarrollo, encontrará escenarios en los que necesita usar variables externas en funciones de operador (especialmente variables grandes, como colecciones grandes por encima de 100M). Entonces deberías usar las variables de transmisión de Spark (Broad

cast) para mejorar el rendimiento.Cuando se utilizan variables externas en la función, de forma predeterminada, Spark hará múltiples copias de la variable y las transferirá a la tarea a través de la red. En este momento, cada tarea tiene una copia de variable. Si la variable en sí es relativamente grande (como 100M, o incluso 1G), la sobrecarga de rendimiento de una gran cantidad de copias de variables en la transmisión de red y el uso excesivo de memoria en cada etapa del Executor provocan GC frecuente, lo que afectará en gran medida el rendimiento. Si la variable externa utilizada es relativamente grande, se recomienda utilizar la función de difusión de Spark para difundir la variable. Después de la difusión, la variable explotará en la memoria de cada Ejecutor, y solo una copia de la variable reside, mientras se ejecuta la tarea en el Ejecutor. Comparta la copia de las variables en el Ejecutor. De esta manera, el número de copias variables se puede reducir en gran medida, reduciendo así la sobrecarga de rendimiento de la transmisión de red, reduciendo el uso de memoria de Eexcutor y reduciendo la frecuencia de GC.

Difusión del método de transmisión de variables grandes: Executor no transmitió variables al principio, pero la operación de la tarea necesita usar las variables de transmisión y encontrará el BlockManager de Executor. BlockManager encuentra BlockManagerMaster en Driver.

El uso de variables de difusión puede reducir en gran medida la cantidad de copias de variables en el clúster.

No utilice variables de difusión: el número de copias de variables es el mismo que el número de tareas.

Usar variable de difusión: el número de copias de la variable es el mismo que el número de Ejecutor.

La memoria máxima ocupada por las variables de difusión: ExecutorMemory * 60% * 90% * 80%

3.3 Trate de evitar el uso de operadores aleatorios

Utilice variables de difusión para simular el uso de escenarios de uso de unión: un RDD es relativamente grande y un RDD es relativamente pequeño.

operador de unión = variable de difusión + filtro, variable de difusión + mapa, variable de difusión + mapa plano

3.4 operación aleatoria usando la agregación previa del lado del mapa

Intente utilizar el operador de reproducción aleatoria del combinador.

Concepto de combinación: en el lado del mapa, la agregación local se realiza después de que se calcula cada tarea de mapa.

Beneficios del combinador:

  • Reduzca la cantidad de datos escritos en el disco mediante la escritura aleatoria.
  • Reduzca la cantidad de datos extraídos por lectura aleatoria.
  • Reducir el número de agregaciones en el lado reducido.

Hay operadores aleatorios de commbiner:

  • reduceByKey: Este operador tiene un combinador en el lado del mapa. En algunos escenarios, reduceByKey se puede usar en lugar de groupByKey.
  • aggregateByKey
  • combineByKey

3.5 Intente utilizar operadores de alto rendimiento

  • Usar reduceByKeyalternativagroupBkKey。
  • Usar en mapPartitionlugar demap。
  • Usar foreachPartitionalternativaforeach。
  • filterDespués de su uso coalescepara reducir el número de particiones.
  • Utilizando repartitionAndSortWithinPartitionsalternativa repartitiony sortoperación.

3.6 Utilice Kryo para optimizar el rendimiento de la serialización

3.6.1 En Spark, la serialización está involucrada principalmente en tres lugares:

  1. Cuando se utiliza una variable externa en una función de operador, la variable se serializará para la transmisión de la red.
  2. Cuando se utiliza un tipo personalizado como tipo genérico de RDD (por ejemplo JavaRDD<User>, Usuario es un tipo personalizado), todos los objetos de tipo personalizado se serializarán. Así, en este caso, también se requiere una clase personalizada que debe implementar Serializablela interfaz.
  3. Cuando se usa una estrategia de persistencia serializable (como MEMORY_ONLY_SER), Spark serializará todas las particiones en el RDD en una gran matriz de bytes.

3.6.2 Introducción al serializador de Kryo

Spark admite el uso del mecanismo de serialización de Kryo. El mecanismo de serialización de Kryo es más rápido que el mecanismo de serialización de Java predeterminado, y los datos serializados son más pequeños, que es aproximadamente 1/10 del mecanismo de serialización de Java. Por lo tanto, después de la serialización de Kryo, los datos transmitidos a través de la red pueden reducirse. Los recursos de memoria consumidos en el clúster se reducen considerablemente.

Para estos tres lugares donde se produce la serialización, podemos optimizar el rendimiento de la serialización y deserialización utilizando la biblioteca de serialización de Kryo. Spark es el mecanismo de serialización de Java predeterminado, que se ObjectOutputStream/ObjectInputStream APIdebe serializar y deserializar. Sin embargo, Spark también admite el uso de la biblioteca de serialización de Kryo. El rendimiento de la biblioteca de serialización de Kryo es mucho mayor que el de la biblioteca de serialización de Java. Según la introducción oficial, el mecanismo de serialización de Kryo es aproximadamente 10 veces mayor que el mecanismo de serialización de Java. La razón por la que Spark no usa la biblioteca de serialización de Kryo de forma predeterminada es porque Kryo requiere que sea mejor registrar los tipos personalizados que deben serializarse, por lo que este método es más problemático para los desarrolladores.

3.6.3 Usando Kryo en Spark

SparkConf conf = new SparkConf();
conf.setMaster("local");
conf .set("spark.serializer","org.apache.spark.serializer.KryoSerializer");
conf.registerKryoClasses(new Class[]{
    
    CameraAggInfo.class});

3.7 Optimizar la estructura de datos

Hay tres tipos de consumo de memoria en Java:

  1. Objeto, cada objeto Java tiene información adicional como el encabezado y la referencia del objeto, por lo que ocupa más espacio en la memoria.
  2. Cadena, cada cadena contiene información adicional como la longitud de una matriz de caracteres.
  3. Tipos de colección, como HashMap, List, etc., porque algunas clases internas se suelen utilizar para encapsular elementos de colección, como Map.Entry.

Por lo tanto, Spark recomienda oficialmente que en la codificación de Spark, especialmente para el código en la función del operador, Jinlbuy use las tres estructuras de datos anteriores. Intente usar cadenas en lugar de objetos, use tipos de datos primitivos (como Int, Long) en lugar de cadenas y use matrices en lugar de tipos de colección, para reducir el uso de memoria tanto como sea posible, reduciendo así la frecuencia de GC y el rendimiento de las solicitudes.

3.8 Utilice la biblioteca de alto rendimiento fastutil

fastutil es una biblioteca de clases que amplía el marco de la colección estándar de Java (mapa, lista, conjunto) y proporciona tipos especiales de mapa, conjunto, lista y cola; fastutil puede proporcionar una huella de memoria más pequeña y una velocidad de acceso más rápida. Utilice la clase de colección proporcionada por fastutil en lugar de la colección nativa de JDK que usa habitualmente. La ventaja es que la clase de colección fastutil puede reducir el uso de memoria, y atraviesa la colección, obtiene el valor del elemento según el índice (o clave) y establece el valor del elemento Cuando, proporcione una velocidad de acceso más rápida. Cada tipo de colección de fastutil implementa la interfaz estándar correspondiente en Java (por ejemplo, el mapa de fastutil, que implementa la interfaz Map de Java), por lo que se puede colocar directamente en cualquier código del sistema existente.

La última versión de fastutil requiere Java 7 y superior.

Supongo que te gusta

Origin blog.csdn.net/dwjf321/article/details/109056145
Recomendado
Clasificación