Aprenda Spark en 20 días (0) Comenzando con la versión más simple de Spark

Spark es un motor de análisis de big data rápido, versátil y escalable escrito en Scala. El llamado análisis de big data analiza y procesa principalmente grandes cantidades de datos y es una habilidad esencial para las carreras actuales de desarrollo de big data.

1. Breve introducción

La siguiente es una breve introducción a Spark, bueno, habla principalmente de lo bueno que es Spark, si no quieres verlo, puedes ir directamente al segundo paso.

Características

1) Rápido: en comparación con MapReduce de Hadoop, las operaciones basadas en memoria de Spark son más de 100 veces más rápidas y las operaciones basadas en disco duro también son más de 10 veces más rápidas. Spark implementa un motor de ejecución DAG eficiente que puede procesar de manera eficiente flujos de datos basados ​​en la memoria. Los resultados intermedios de los cálculos se almacenan en la memoria.

2) Facilidad de uso: Spark admite las API de Java, Python y Scala, y también admite más de 80 algoritmos avanzados, lo que permite a los usuarios crear rápidamente diferentes aplicaciones. Además, Spark admite shells interactivos de Python y Scala, y es muy conveniente utilizar clústeres de Spark en estos shells para verificar soluciones a problemas.

3) Universal: Spark proporciona una solución unificada. Spark se puede utilizar para procesamiento por lotes, consultas interactivas (Spark SQL), procesamiento de flujo en tiempo real (Spark Streaming), aprendizaje automático (Spark MLlib) y computación de gráficos (GraphX). Todos estos diferentes tipos de procesamiento se pueden utilizar sin problemas dentro de la misma aplicación. La solución unificada de Spark es muy atractiva: después de todo, cualquier empresa quiere utilizar una plataforma unificada para abordar los problemas que encuentra, reduciendo los costos laborales de desarrollo y mantenimiento y los costos materiales de implementación de la plataforma.

4) Compatibilidad: Spark se puede integrar fácilmente con otros productos de código abierto. Por ejemplo, Spark puede usar YARN y Apache Mesos de Hadoop como programador y administrador de recursos, y puede procesar todos los datos compatibles con Hadoop, incluidos HDFS, HBase y Cassandra. Esto es particularmente importante para los usuarios que ya han implementado clústeres de Hadoop, porque pueden utilizar las poderosas capacidades de procesamiento de Spark sin realizar ninguna migración de datos. Spark tampoco depende de programadores y administración de recursos de terceros. Implementa Standalone como su marco integrado de programación y administración de recursos, lo que reduce aún más el umbral para usar Spark y hace que sea muy fácil para todos implementar y usar Spark. Además, Spark también proporciona herramientas para implementar el clúster Spark de Standalone en EC2.

2. Comenzando con Spark en 20 líneas de código.

Nota: El entorno actual es idea + jdk8. Todos los códigos de este artículo se basan en la base de programación Java anterior.

1. paquete de frasco

Primero, cree un proyecto maven normal en idea, vaya al archivo pom del proyecto e introduzca las dependencias de maven. Si se ha introducido, no es necesario introducirlo.

<dependencies>
        <!-- scala依赖 开始 -->
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-reflect</artifactId>
            <version>2.12.8</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>2.12.8</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-compiler</artifactId>
            <version>2.12.8</version>
            <scope>compile</scope>
        </dependency>

        <!-- scala依赖 结束 -->

        <!-- spark依赖 开始 -->
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-sql_2.12</artifactId>
            <version>2.4.3</version>
        </dependency>

        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-mllib_2.12</artifactId>
            <version>2.4.3</version>
        </dependency>

        <!-- spark依赖 结束 -->
    </dependencies>

    <build>
        <plugins>
            <!-- 该插件用于将 Scala 代码编译成 class 文件 -->
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <version>3.4.6</version>
                <executions>
                    <execution>
                        <!-- 声明绑定到 maven 的 compile 阶段 -->
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

2. Crea un conjunto de datos

Cree una carpeta de datos en el directorio del proyecto para almacenar datos, cree un archivo de Word e ingrese el siguiente contenido,

La siguiente tarea es utilizar estadísticas de chispa para analizar el número de apariciones de cada palabra.

java~hadoop~java
html~js
java
js~jquery

3. Cree el archivo WordCount.scala para el cálculo. Tenga en cuenta que el tipo de archivo es objeto. El orden de uso de Spark es aproximadamente el siguiente

1. Crear contexto Spark

2. Leer el archivo de datos

3. Procesar datos en formatos apropiados

4. Cálculos estadísticos

5. Obtenga los resultados y guárdelos

El código de procesamiento específico es el siguiente

object WordCount {
    
    

  def main(args: Array[String]): Unit = {
    
    
    // 第一个参数启动的方式,第二个参数启动任务的名称
    val spark = new SparkContext("local",
      "WordCount")

    // 读取数据,word就是刚创建的文件
    val data = spark.textFile("data/word")

    // 方便计算的格式
    val result = data
      .flatMap(_.split("~")) // 使用~切分每条记录
      .map((_,1)) // java,1 html,1
     .countByKey() // 统计相同key值的数量

    println(result)
  }

}

Finalmente, se imprime el número de apariciones de cada palabra.

Este es el caso de uso de Spark más simple

3. Introducción al proceso de ejecución de código.

El código anterior es el código Spark Case más básico. El siguiente contenido es una introducción al proceso de ejecución del código anterior.

3.1 Palabras clave comunes

Antes de aprender Spark, primero clasificamos una serie de palabras clave de uso común para evitar que los estudiantes no comprendan el artículo.

RDD

RDD (Resilient Distributed Dataset) se denomina conjunto de datos distribuidos. Es la abstracción de datos más básica en Spark. Representa un conjunto inmutable y particionable cuyos elementos se pueden calcular en paralelo. Es un objeto básico en Spark.

defecto:

​No admite operaciones de escritura y actualización de grano fino (como rastreadores web).
Spark escribe datos de manera de grano grueso. El llamado grano grueso significa escribir datos en lotes (escritura por lotes).
Sin embargo, leer datos es De grano fino, lo que significa que se puede escribir en una oración. La lectura de tiras (lectura de tiras una por una)
no admite cálculos iterativos incrementales, pero Flink sí lo admite.

Gráfico acíclico dirigido por DAG

En un gráfico dirigido sin bucle, ningún vértice puede volver al punto a través de varios bordes, por lo que el gráfico no tiene bucle
Sentimiento personal: en Spark, se refiere a la conversión mutua de varios RDD para operación y procesamiento, y finalmente el resultado es obtenido.

Operador de transformación

La transformación se refiere a una serie de métodos para la transformación RDD en Spark. Es una carga diferida y no se ejecutará de inmediato, solo se ejecutará cuando se encuentre un operador de acción.

operador de acción

Un método para procesar o realizar cálculos.

trabajo

Tarea, cuando se encuentra un operador de acción en el programa, se enviará un trabajo para realizar la serie de operaciones anterior. Una tarea tiene varios trabajos y los trabajos se ejecutan en serie.

Dependencia de la amplitud y la estrechez

Cuando el operador se está ejecutando, el RDD se convertirá. El RDD anterior es el RDD principal y el RDD secundario posterior. Cuando un RDD principal ingresa a un RDD secundario, como el operador de mapa, se convierte en una dependencia estrecha. Si el RDD Puede pasar uno o más RDD para convertirlos y generarlos, lo que se denomina dependencia amplia, como el operador groupByKey.

escenario

Un trabajo contiene una o más etapas. Cada etapa se ejecuta en orden. La división de etapas se basa en dependencias aleatorias.

División de etapas: las tareas de Spark formarán un gráfico acíclico dirigido por DAG basado en las dependencias entre RDD. El DAG se enviará a DAGScheduler. DAGScheduler dividirá el DAG en múltiples etapas que dependen entre sí. La base para dividir las etapas es la La relación entre RDD depende del ancho. Cuando se encuentran dependencias amplias, las etapas se dividen y cada etapa contiene una o más tareas. Luego, estas tareas se envían a TaskScheduler en forma de conjunto de tareas para su ejecución.

Tarea

La etapa continúa descomponiéndose en tareas. El número de tareas es en realidad el paralelismo de la etapa, la unidad mínima de ejecución de la tarea. En última instancia, una tarea se ejecuta en forma de tarea en el ejecutor.

Resumen de relaciones de trabajo, etapa y tarea

El trabajo está limitado por métodos de acción. Cuando se encuentra un método de acción, se activa un trabajo;

La etapa es un subconjunto de Job, delimitado por dependencias amplias de RDD (es decir, Shuffle) y se divide una vez cuando se encuentra Shuffle;

La tarea es un subconjunto de la Etapa, medida por el grado de paralelismo (número de particiones), y el número de particiones es el mismo que el número de tareas.

obrero

Administre la memoria del nodo actual y el uso de la CPU, reciba instrucciones de recursos asignadas por el maestro y asigne tareas a través del programa de inicio ExecutorRunner. El trabajador es similar a un contratista. Un proceso de trabajo iniciará uno o más subprocesos ejecutores para ejecutar un componente de topología ( Pico o Perno)

Ejecutor

La unidad de ejecución de la tarea Spark (tarea) se ejecuta en el trabajador. De hecho, es un proceso JVM, una colección de recursos informáticos (núcleo de CPU, memoria). La memoria y la CPU de un trabajador son compartidas por varios ejecutores. Cuando se inicia la aplicación Spark, los ejecutores comienzan al mismo tiempo y siguen todo el ciclo de vida. Hay dos funciones principales principales:
1. Responsable de ejecutar las tareas que componen la aplicación Spark y devolver los resultados al proceso del controlador;
2. Proporcionan almacenamiento en memoria para RDD que requieren almacenamiento en caché en programas de usuario a través de sus propios administradores de bloques. (Administrador de bloques). RDD se almacena en caché directamente en el proceso Ejecutor, por lo que las tareas pueden aprovechar al máximo los datos almacenados en caché para acelerar las operaciones durante el tiempo de ejecución.

dividir

Partición de datos de Spark: se asigna una gran cantidad de datos a las particiones de cada nodo para su procesamiento.

barajar

Un trabajo se divide en varias etapas. Cuando se activan dependencias amplias, entra en la etapa de reproducción aleatoria. La reproducción aleatoria se divide en la etapa de mapa (rojo) y la etapa de reducción (escritura).

El número de tareas en el lado del mapa es consistente con el número de particiones. El lado reducido usa spark.default.parallelism como configuración predeterminada. De lo contrario, el número de las últimas particiones RDD se usa como el número de tareas.

shuffle se divide en HashShuffle y SortShuffle

HashShuffle realiza un algoritmo hash en la misma clave, escribiendo así la misma clave
en el mismo archivo de disco, y cada archivo de disco solo pertenece a una tarea de la etapa posterior. ¿Cuántas tareas hay en la siguiente etapa? ¿Cuántas tareas hay? ¿Hay en la etapa actual? ¿Cuántos archivos de disco se crean para cada tarea?

Establecer spark.shuffle.consolidateFiles en verdadero puede habilitar la optimización y aparecerá un shuffleFileGroup. Durante la ejecución, cada lote de tareas reutilizará el shuffleFileGroup, sin crear nuevos archivos de disco ni reducir la cantidad de archivos de disco.

SortShuffe agrega procesamiento de clasificación en comparación con HashShuffle. SortShuffe se usa de forma predeterminada después de Spark 1.2. La diferencia es que cuando cada tarea realiza la operación aleatoria, aunque se generarán más archivos de disco temporales, todos los archivos temporales se fusionarán al final. ) en un archivo de disco y hay un archivo de índice correspondiente

Hay dos modos de funcionamiento, el primero es el modo normal y el segundo es el modo de derivación (ingresado cuando el número de tareas de lectura es menor que el valor del parámetro bypassMergeThreshold, o cuando no es un operador aleatorio agregado)

Las diferencias en el modo bypass: 1. El mecanismo de escritura en el disco es diferente, 2. No ordena. Como no se realiza ninguna clasificación, esta parte de la sobrecarga de rendimiento se puede ahorrar.

maestro

Gestiona clústeres y nodos, no participa en los cálculos.

obrero

Nodo de computación, el proceso en sí no participa en el cálculo y reporta al maestro

Administre la memoria actual del nodo y el uso de la CPU, reciba instrucciones de recursos asignadas por el maestro y asigne tareas a través del programa de inicio ExecutorRunner. Es similar a un contratista, administra y asigna nuevos procesos y realiza servicios de cálculo.

Conductor

El método principal para ejecutar el programa pertenece a la entrada del programa, sus funciones principales son 1. Convertir el programa de usuario en una tarea (trabajo), 2. Solicitar recursos del clúster, 3. Responsable de la programación y análisis del trabajo. 4. Generar Etapa y programar la Tarea al Ejecutor.

3.2 Flujo de procesamiento de 20 líneas de código

El flujo de procesamiento se puede dividir aproximadamente en tres partes: la primera es enviar la tarea, la segunda es encontrar los recursos correspondientes y la segunda es programar la tarea.

Hay muchas formas de iniciar el programa y el método de ejecución depende del valor de master pasado al crear el objeto sparkContext. Puede usar el método de ejecución local (generalmente puede usar esto al probar el código),

local: ejecutándose localmente, un proceso, sin paralelismo; local[k]: k procesos ejecutándose; local[*]: el número de procesos es igual al número de núcleos de CPU

Además del inicio local, también puede utilizar el modo de clúster para iniciar. Hay tres métodos generales y el más utilizado es YARN.

Modo de inicio Características método
Modo autónomo independiente Clúster nativo simple con servicios completos Chispa - chispear://
Apache Mesos Marco de gestión de recursos distribuidos mesos://
HILO Hadoop Al ejecutarse en Yarn [Nota 1], el mecanismo unificado de gestión de recursos se divide en cliente de hilo y grupo de hilo según la ubicación del controlador. cliente de hilo: la unidad se ejecuta localmente, el trabajo se ejecuta en el clúster YRAN, use el cliente en modo de implementación al implementar; clúster de hilo: el controlador y el trabajo están ambos en el clúster, use el clúster en modo de implementación al implementar

Nota 1: YARN, el administrador de recursos de Hadoop, es un sistema de administración de recursos general que puede proporcionar administración y programación de recursos unificadas para aplicaciones de capa superior. Su introducción ha brindado nuevos beneficios al clúster en términos de utilización, administración de recursos unificada e intercambio de datos. .Grandes beneficios.

En nuestro código anterior, usamos local, que es local

val spark = new SparkContext("local","WordCount")

1. Al ejecutar aquí, escanee los archivos dependientes y los paquetes jar, comience a generar SparkContext, inicialice el lado del controlador y prepare el Ejecutor. Primero, el controlador inicia y registra la aplicación con el Maestro, y el Maestro la encuentra según el recurso. requisitos del script enviado. Los recursos internos pueden iniciar al menos un Ejecutor de todos los Trabajadores, y luego asignar Ejecutores entre estos Trabajadores. Después de que se inicia el Ejecutor en el Trabajador, se registrará de manera inversa con el Controlador. Después de que todos los Ejecutores estén registrados, el El entorno se inicializa y comienza la ejecución de la tarea.

2. El controlador comienza a ejecutar la función principal y construye el gráfico DAG. Una vez completada la construcción, el gráfico DAG se envía a DAGScheduler. DAGScheduler (programador DAG) comienza a dividir las etapas y comienza a dividir las etapas de acuerdo con el operador de acción. .En el código anterior

.countByKey()

El método pertenece al operador Acción. Cuando se ejecuta esta línea, la tarea anterior se dividirá en una etapa. Las etapas tienen dependencias secuenciales, y luego el TaskSet en la etapa se enviará al TaskScheduler.

3. TaskScheduler (programador de tareas) obtiene todas las tareas de TaskSet (conjunto de tareas) y procesa la división de tareas de acuerdo con dependencias amplias y estrechas, que son las siguientes dos líneas de código.

.flatMap(_.split("~")) // 使用~切分每条记录
.map((_,1)) // java,1 html,1

Todos los operadores anteriores tienen una relación 1-1 con el RDD principal, por lo que todos son dependencias estrechas.

Las dependencias amplias pertenecen a múltiples RDD para la integración y división, y la activación de la reproducción aleatoria implica la transmisión de red, lo que fácilmente consume recursos.

4. A continuación, envíe todas las tareas, datos y códigos de ejecución al ejecutor. El ejecutor coloca las tareas en el grupo de subprocesos para ejecutarlas y envía los resultados de la ejecución al TaskScheduler. El TaskScheduler luego envía los resultados al DAGScheduler. Hasta que se completen todas las tareas, libere todos los recursos.

Supongo que te gusta

Origin blog.csdn.net/lihao1107156171/article/details/126403591
Recomendado
Clasificación