Spark (1) -- entiende Spark en un artículo

Introducción a Chispa:        

Spark es un marco de procesamiento de big data rápido y general, que proporciona capacidades eficientes de procesamiento y análisis de datos distribuidos.

La relación entre Spark y Hadoop: 

        Aunque Spark no forma parte de Hadoop, está estrechamente integrado con el ecosistema de Hadoop. Spark proporciona capacidades de análisis y procesamiento de datos más rápidos, y tiene funciones como procesamiento por lotes, procesamiento de flujo, aprendizaje automático y computación gráfica. Spark puede entenderse como un modelo informático actualizado de MapReduce en Hadoop.

Hadoop VS Spark
Hadoop Chispa - chispear
tipo Una plataforma básica distribuida completa que admite computación, almacenamiento, programación, etc. Herramienta de computación distribuida, que se puede utilizar para computación iterativa, computación interactiva, computación de flujo
ecosistema Tiene un completo ecosistema de procesamiento y análisis de big data como Hive, Pig, HBase y Sqoop Admite la integración con Hive, Pig, HBase, Kafka, etc.; es compatible con varias plataformas y se puede integrar con varios sistemas de procesamiento y almacenamiento de datos
Requisitos para la máquina Bajo altos requisitos de memoria
modelo de procesamiento de datos Use el modelo MapReduce para el procesamiento de datos, los datos se distribuyen en varios nodos a través de HDFS y las operaciones Map y Reduce se realizan en cada nodo. Utilice la abstracción RDD para representar y procesar conjuntos de datos distribuidos, adopte el modo de computación basado en memoria y mejore la velocidad del análisis y procesamiento de datos a través de la computación de memoria.
velocidad de calculo El modelo MapReduce es adecuado para procesar tareas por lotes, pero el cálculo iterativo y el procesamiento de datos en tiempo real son relativamente lentos debido a la E/S lenta del disco. Spark proporciona una velocidad informática más rápida a través de la informática de la memoria y la abstracción de datos de nivel superior (como RDD, DataFrames y Datasets), y tiene ventajas en la informática iterativa, el procesamiento de flujo y las consultas interactivas.
Flexibilidad en el procesamiento de datos El HDFS de Hadoop es adecuado para el almacenamiento a largo plazo y el procesamiento por lotes, pero para las necesidades complejas de procesamiento de datos, se necesitan otras herramientas y tecnologías, como Hive y Pig. Spark admite el procesamiento por lotes, el procesamiento de secuencias, el aprendizaje automático y la computación gráfica, etc., y proporciona API avanzadas (como Spark SQL) y bibliotecas especializadas (como MLlib y GraphX) para análisis y procesamiento de datos más complejos.
estructura de almacenamiento de datos Los resultados del cálculo intermedio de MapReduce se almacenan en el disco HDFS y el retraso es grande Los resultados de la operación intermedia RDD se almacenan en la memoria y el retraso es pequeño
paradigma de programación Map+Reduce, la API es de nivel relativamente bajo y la adaptabilidad del algoritmo es pobre RDD forma un gráfico acíclico dirigido por DAG, y la API es relativamente de alto nivel, lo cual es fácil de usar
Modo de operación La tarea se mantiene como un proceso y el inicio de la tarea es lento La tarea se mantiene en modo subproceso, el inicio de la tarea es rápido

Arquitectura de chispa: 

  • Spark Core: el componente central de Spark, que proporciona funciones básicas como RDD, programación de tareas distribuidas, computación de memoria, transmisión de datos, recuperación de errores e interacción con sistemas de almacenamiento, y es la base de otros componentes de Spark.
    • RDD (Conjuntos de datos distribuidos resistentes): los conjuntos de datos distribuidos resistentes son la abstracción de datos central de Spark, que representa un conjunto de datos inmutable que se puede particionar y calcular en paralelo. RDD proporciona tolerancia elástica a fallas, partición de datos y operaciones de conversión de datos, y es la unidad básica de computación distribuida.
  • Spark SQL: es el módulo de procesamiento y consultas SQL de Spark, que brinda la capacidad de procesar datos estructurados, admite el uso de declaraciones SQL para consultar y analizar datos, y también puede integrarse perfectamente con RDD
  • Spark Streaming: el módulo de procesamiento de flujo de Spark para procesar flujos de datos en tiempo real. Proporciona una API de alto nivel para el cálculo y procesamiento de datos en tiempo real de varias fuentes de datos
  • MLib (Machine Leaning Library): la biblioteca de aprendizaje automático de Spark, que proporciona una serie de herramientas y algoritmos de aprendizaje automático de uso común para tareas de aprendizaje automático en conjuntos de datos a gran escala.
  • GraphX: la biblioteca de procesamiento de gráficos de Spark para procesar datos y cálculos de gráficos. Proporciona una API para crear y manipular gráficos, tareas de apoyo como algoritmos de gráficos y análisis de gráficos.
  • Spark Job: una tarea enviada al clúster para su ejecución a través de Spark, que contiene el código, los datos y la configuración que deben ejecutarse.

Núcleo de chispa: 

 RDD:

RDD (Conjuntos de datos distribuidos resistentes): los conjuntos de datos distribuidos resistentes son la abstracción de datos central de Spark, que representa un conjunto de datos inmutable que se puede particionar y calcular en paralelo. RDD proporciona tolerancia elástica a fallas, particionamiento de datos y operaciones de conversión de datos, y es la unidad básica de computación distribuida.

  • Resiliente  : Es elástico, y los datos en RDD se pueden almacenar en memoria o en disco;
  • Distribuido  :  los elementos que contiene se almacenan de forma distribuida y se pueden utilizar para computación distribuida;
  • DatasetEs una colección que puede almacenar muchos elementos.

Hay tal descripción en el código fuente de RDD: 

A list of partitions
A function for computing each split
A list of dependencies on other RDDs
Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)
Optionally, a list of preferred locations to compute each split on(e.g. block locations for an HDFS file)
  • Una lista de particiones: Una lista de particiones. Una partición es una unidad informática de RDD. El número de particiones determina el grado de paralelismo.
  • Una función para calcular cada división: la función se aplica a cada partición y el RDD se calcula en unidades de particiones.
  • Una lista de dependencias en otros RDD: RDD se basará en muchos otros RDD, y cada conversión de RDD generará un nuevo RDD. Una vez que se pierden los datos de la partición, los datos de la partición perdida se pueden volver a calcular a través de esta dependencia. (Mecanismo de tolerancia a fallas de Spark)
  • Opcionalmente, un particionador para RDD de clave-valor (por ejemplo, para decir que el RDD tiene particiones hash): opcional , habrá un particionador (función de partición) para KV tipo RDD, el valor predeterminado es HashPartitioner
  • Opcionalmente, una lista de ubicaciones preferidas para calcular cada división (por ejemplo, ubicaciones de bloques para un archivo HDFS): Opcional , la ubicación preferida de la partición de almacenamiento. Para archivos HDFS, la lista guarda la posición del bloque donde se encuentra cada partición.

La descripción anterior corresponde una vez a la lista de particiones, la función de cálculo, la dependencia, la función de partición y la ubicación óptima de Spark.

  • Lista de particiones, función de partición y ubicación óptima Estos tres atributos en realidad se refieren a dónde está el conjunto de datos, dónde es más apropiado calcularlo y cómo particionarlo;
  • Funciones computacionales y dependencias, estas dos propiedades en realidad se refieren a cómo surgió el conjunto de datos.

Algunas propiedades clave y el uso de RDD:

  1. Particionamiento: RDD divide el conjunto de datos en varias particiones lógicas (particiones lógicas) y distribuye estas particiones en varios nodos del clúster. El número de particiones determina el grado de computación paralela.

  2. Resiliencia: RDD tiene la característica de resiliencia, es decir, cuando fallan los datos o el nodo de una determinada partición, Spark puede recalcular la partición perdida a través del linaje, logrando así la tolerancia a fallas .

  3. Inmutabilidad: los RDD son inmutables y no se pueden modificar una vez creados. Para realizar transformaciones u operaciones en RDD, se debe generar un nuevo RDD.

  4. Transformaciones: RDD proporciona una serie de operaciones de transformación, como mapear, filtrar, reducir, unir, etc. A través de estas operaciones de conversión, se puede crear otro RDD con diferente estructura de datos o contenido a partir de un RDD para realizar la conversión y el procesamiento de datos.

  5. Acciones: RDD proporciona algunas operaciones de acción, como contar, recopilar, reducir, etc. Las operaciones de acción desencadenan el cálculo real y devuelven el resultado al controlador o guardan el resultado en un sistema de almacenamiento externo.

  6. Persistencia de datos: RDD admite la aceleración del acceso a datos mediante el almacenamiento en caché de datos en la memoria. Puede optar por persistir RDD en la memoria, el disco o almacenar de forma serializada.

API de RDD

1. Crear DDR 

Hay tres métodos para la creación de RDD: 

1.1 Para crear un RDD a partir de una colección, utilice el método de paralelización de SparkContext: 

val sparkConf = new SparkConf().setAppName("RDD creation")
val sc = new SparkContext(sparkConf)

val data = List(1, 2, 3, 4, 5)
val rdd = sc.parallelize(data)

1.2 Cree RDD desde almacenamiento externo, aplique el método textFile de SparkContext

val sparkConf = new SparkConf().setAppName("RDD creation")
val sc = new SparkContext(sparkConf)

// 指定读取文件路径或包含文件的目录路径
val rdd = sc.textFile("hdfs://path/to/textfile.txt")
// 指定分区数
val rdd2 = sc.textFile("hdfs://path/to/textfile.txt", 7)

1.3 Transformación desde otros RDD Crear RDD: Puede usar operaciones de transformación de RDD (operadores de conversión) (como map, filter, flatMapetc.)

val sparkConf = new SparkConf().setAppName("RDD creation")
val sc = new SparkContext(sparkConf)

val input = sc.parallelize(List(1, 2, 3, 4, 5))
val rdd = input.map(x => x * 2)

2. operador RDD

RDD en realidad no almacena los datos que se van a calcular, pero registra la ubicación de los datos y la relación de conversión de los datos (qué método se llama, qué función se pasa).

Los operadores RDD se dividen principalmente en dos categorías: 

  • Transformación: devuelve un nuevo RDD. Todas las transformaciones en RDD se evalúan/aplazan de forma perezosa, es decir, no se calculan directamente. Estas transformaciones solo se ejecutarán realmente cuando ocurra una acción que requiera que se devuelva un resultado  Driver . ActionDebido a que esto puede realizar la división por etapas y la optimización paralela en la operación RDD para formar un gráfico acíclico dirigido por DAG durante la acción, este diseño hace que Spark funcione de manera más eficiente
  • Operador de acción (acción): el valor de retorno no es RDD (sin valor de retorno o retorno de otro).

Operador de transformación (transformación)

  1. Conversión de un solo elemento:
    1. map(func): aplica una función a cada elemento en el RDD funcy devuelve un nuevo RDD
    2. flatMap(func): aplica una función a cada elemento en el RDD funcy devuelve un nuevo RDD con un conjunto de resultados aplanado
    3. filter(func): Según funcel valor de retorno de la función es verdadero o falso, filtrar los elementos en el RDD y devolver un nuevo RDD
    4. mapPartitions(func): similar a map, pero opera independientemente en cada segmento de RDD, por lo que cuando se opera en RDD de tipo T, el tipo de función de func debe ser Iterator[T] => Iterator[U]
    5. mapPartitionsWithIndex(func): similar a mapPartitions, pero func tiene un parámetro entero que representa el valor de índice de la partición, por lo que cuando se ejecuta en un RDD de tipo T, el tipo de función de func debe ser (Int, Interator[T]) => Iterador[U]
  2. Transformaciones de elementos múltiples:
    1. union(other): devuelve un other nuevo RDD que contiene todos los elementos de dos RDD (RDD actual y RDD)
    2. intersection(other)other : Devuelve un nuevo RDD que contiene elementos comunes de dos RDD (RDD actual y RDD)
    3. subtract(other)other : Devuelve un nuevo RDD que solo contiene elementos que existen en el RDD actual pero que no existen en el RDD
    4. distinct([numTasks]): Devolver un nuevo RDD después de la deduplicación
    5. cartesian(other): Devuelve un nuevo RDD que contiene todas las combinaciones de elementos de dos RDD (RDD actual y other RDD)
    6. join(otherDataset, [numTasks]): invocado en RDD de tipo (K, V) y (K, W), devuelve un par (K, (V, W)) de todos los elementos correspondientes a la misma clave RDD
    7. cogroup(otherDataset, [numTasks]): invocado en RDD de tipo (K,V) y (K,W), devuelve un RDD de tipo (K,(Iterable,Iterable))
    8. cartesian(otherDataset): producto cartesiano
  3. Conversiones relacionadas con la partición:
    1. repartition(numPartitions): repartición, devuelve un nuevo RDD con el número especificado de particiones
    2. coalesce(numPartitions): Repartición, reduce el número de particiones de RDD al valor especificado. Después de filtrar una gran cantidad de datos, puede hacer esto
    3. partitionBy(partitioner): Particiona el RDD según el particionador especificado y devuelve un nuevo RDD.
    4. groupByKey([numTasks]): invocado en un (K, V) RDD, devuelve un (K, Iterator[V]) RDD
    5. reduceByKey(func, [numTasks]): invocado en un (K, V) RDD, devuelve un (K, V) RDD, utiliza la función de reducción especificada para agregar los valores de la misma clave, similar a groupByKey, The el número de tareas de reducción se puede establecer mediante el segundo parámetro opcional
    6. sortByKey([ascending], [numTasks]): invocado en un (K, V) RDD, K debe implementar la interfaz ordenada y devuelve un (K, V) RDD ordenado por clave
    7. sortBy(func, ascending=True, numPartitions=None): funcOrdena el RDD según el valor de retorno de la función y devuelve un nuevo RDD ordenado.
  4. otras conversiones
    1. sample(withReplacement, fraction, seed): De acuerdo con la relación de muestreo dada fraction, se muestrea el RDD y se devuelve un nuevo RDD
    2. glom(): Convierta los elementos de cada partición en una matriz y devuelva un nuevo RDD
    3. tubería (comando, [envVars]): operación de canalización en rdd

Operador de acción (acción):

  1. Acciones básicas 
    1. reduce(func): use funcuna función para reducir los elementos en el RDD y devuelva el resultado reducido
    2. collect(): Devuelve todos los elementos en el RDD como una matriz
    3. count(): devuelve el número de elementos en el RDD
    4. first(): devuelve el primer elemento en el RDD
    5. take(n): Devuelve los primeros n elementos del RDD
  2. Acciones relacionadas con las estadísticas
    1. max(): devuelve el valor máximo en el RDD
    2. min(): devuelve el valor mínimo en el RDD
    3. sum(): Devuelve la suma de todos los elementos en el RDD
    4. mean(): devuelve el valor promedio en el RDD
    5. variance(): devuelve la varianza de los elementos en el RDD
    6. stdev(): Devuelve la desviación estándar de los elementos en el RDD
  3. otras acciones
    1. foreach(func)func: aplica una función a cada elemento en el RDD
    2. foreachPartition(func)func: aplica una función a cada partición en el RDD
    3. countByValue(): Para un RDD de tipo (K, V), devuelve un mapa de (V, Int), indicando el número de elementos correspondientes a cada valor
    4. countByKey(): Para un RDD de tipo (K, V), devuelve un mapa de (K, Int), indicando el número de elementos correspondientes a cada clave

3. Persistencia/caché RDD 

        Algunos  RDD cálculos o conversiones pueden llevar mucho tiempo. Si se  RDD utilizan con frecuencia más adelante, se pueden  RDD conservar o almacenar en caché para mejorar la reutilización y la eficiencia de cálculo de RDD. Las operaciones de persistencia y almacenamiento en caché permiten que los datos de RDD se almacenen en la memoria o en el disco, lo que evita cálculos repetidos y mejora la velocidad de acceso a los datos.

val rdd1 = sc.textFile("hdfs://node01:8020/words.txt")
val rdd2 = rdd1.flatMap(x=>x.split(" ")).map((_,1)).reduceByKey(_+_)
rdd2.cache //缓存, 等同于rdd2.persist(StorageLevel.MEMORY_ONLY)
rdd2.sortBy(_._2,false).collect//触发action,会去读取HDFS的文件,rdd2会真正执行持久化
rdd2.sortBy(_._2,false).collect//触发action,会去读缓存中的数据,执行速度会比之前快,因为rdd2已经持久化到内存中了

3.1 persist(storageLevel: StorageLevel)Método: marque el RDD como persistente y especifique el nivel de almacenamiento de datos Nivel de almacenamiento:

  1. MEMORY_ONLY: almacenar datos RDD en la memoria
  2. MEMORY_AND_DISK: si la memoria es insuficiente, almacene los datos RDD en la memoria y el resto de los datos se desbordará en el disco (generalmente se usa en desarrollo)
  3. MEMORY_ONLY_SER: almacenar datos RDD en la memoria de forma serializada
  4. MEMORY_AND_DISK_SER: Es similar a MEMORY_AND_DISK,la escritura desbordada en el disco si no cabe en la memoria, pero los datos se almacenan de forma serializada.
  5. DISK_ONLY: almacenar particiones RDD en disco
  6. MEMORY_ONLY_2, MEMORY_AND_DISK_2等:将持久化数据存储为2份,备份每个分区存储在两个集群节点上

3.2 c ache()método: Es persist()una forma simplificada del método, utilizando el nivel de almacenamiento predeterminado MEMORY_ONLYpara marcar RDD como persistente

Aviso: 

  • La persistencia y el almacenamiento en caché solo son válidos para operaciones posteriores y deben realizarse antes de las operaciones de conversión de RDD.
  • Solo cuando se ejecuta la operación de acción, los datos de RDD se conservarán o almacenarán en caché.
  • Si la cantidad de datos en el RDD es grande y la memoria no es suficiente para contener todos los datos, puede optar por almacenar el RDD en el disco ( o ) MEMORY_AND_DISK, MEMORY_AND_DISK_SERpara que los datos se puedan leer desde el disco cuando sea necesario.
  • Use unpersist()el método para eliminar manualmente el RDD persistente de la memoria

3.3 persistencia del punto de control (mecanismo de tolerancia a fallas)

El punto de control de RDD es un mecanismo tolerante a fallas para la persistencia en Spark, que puede guardar los resultados intermedios en un sistema de almacenamiento distribuido confiable (como HDFS), para que pueda restaurarse rápidamente cuando falla un nodo.

Establecer punto de control:

val conf = new SparkConf()
        .setAppName("CheckpointExample")
        .set("spark.checkpoint.dir", "hdfs://path/to/checkpoints")
val sc = new SparkContext(conf)
val rdd = sc.parallelize(Seq(1, 2, 3, 4, 5))
rdd.checkpoint()

Restauración de datos mediante puntos de control: 

val conf = new SparkConf().setAppName("CheckpointRecovery").set("spark.checkpoint.dir", "hdfs://path/to/checkpoints")
val sc = new SparkContext(conf)
sc.setCheckpointDir("hdfs://path/to/checkpoints")

Al utilizar el mecanismo de punto de control, debe prestar atención a los siguientes puntos:

  • La creación de puntos de control es una operación costosa porque requiere que los datos se escriban en el disco. Por lo tanto, utilícelo solo cuando necesite tomar mucho tiempo o reutilizar resultados intermedios.
  • Los puntos de control interrumpen el gráfico de dependencia del RDD, por lo que se recomienda realizar un punto de control inmediatamente después de que el resultado deba persistir.
  • Los datos de los puntos de control ocupan espacio de almacenamiento en el disco y deben administrarse y limpiarse adecuadamente según los recursos disponibles y las necesidades de la aplicación.

Comparado con persistir: 

  • Persist es adecuado para escenarios en los que los datos de RDD deben usarse repetidamente o los resultados intermedios deben almacenarse en caché para cálculos posteriores.
  • Checkpoint es adecuado para escenarios que requieren retención de datos a largo plazo, protección de confiabilidad o gráficos de dependencia de RDD largos.

Chispa SQL 

Introducción a Spark SQL:

Spark SQL combina la API de RDD tradicional y el lenguaje de consultas SQL, lo que permite a los desarrolladores utilizar declaraciones SQL o la API DataFrame única de Spark para la manipulación y el análisis de datos, procesando así datos estructurados de manera más conveniente y eficiente.

Principales características:

  1. DataFrame y Dataset: Spark SQL presenta dos conceptos abstractos, DataFrame y Dataset, que proporcionan una interfaz de operación de datos más avanzada.

    1. DataFrame es una colección de datos distribuidos basada en RDD, tiene una estructura similar a una tabla de base de datos relacional y puede realizar operaciones como filtrado, agregación y consulta SQL. Puede entenderse como una estructura JSON, solo restringida por nombres de columna y tipos de valor de campo.

    2. Dataset proporciona una API con mayor seguridad de tipos, que puede evitar errores de tiempo de ejecución a través de la verificación en tiempo de compilación. En comparación con DataFrame, DataSet puede entenderse como Entidad Además de las restricciones de nombre de campo y tipo de valor de campo, también existen restricciones de tipo de entidad.

  2. Consulta SQL y funciones integradas: Spark SQL admite operaciones de consulta y agregación de datos mediante declaraciones de consulta SQL estándar, lo que permite a los desarrolladores utilizar la sintaxis SQL familiar para el análisis de datos. Además, Spark SQL también tiene incorporadas muchas funciones SQL de uso común (como funciones agregadas, funciones de fecha, etc.) para procesar datos de manera más conveniente.

  3. Extensión de fuente de datos: Spark SQL admite la conexión y el funcionamiento de varias fuentes de datos, incluidas bases de datos relacionales (como MySQL, PostgreSQL), Hive, Parquet, Avro, JSON, CSV, etc. Con la ayuda de la interfaz de fuente de datos de Spark SQL, puede leer, escribir y procesar datos fácilmente en diferentes formatos.

  4. Optimizador de Catalyst: Spark SQL utiliza el optimizador de Catalyst para optimizar las consultas de SQL, incluido el análisis de consultas, la optimización lógica, la generación de planes físicos y la optimización de la ejecución. Catalyst utiliza estrategias de optimización basadas en reglas y costos para convertir y optimizar automáticamente los planes de consulta para mejorar el rendimiento de la consulta.

Ejemplo de operación de shell simple de Spark SQL:

Ejemplo de estilo DSL para Spark SQL:

# 打开Spark shell 控制台
sh bin/spark-shell

# 读取文件,创建RDD 
val lineRDD = sc.textFile("file:///Users/zhoushimiao/Desktop/people.txt").map(_.split(" ")) 

# 定义Person 字段和字段类型
case class Person(id:Int, name:String, age:Int)

#将 RDD 和 Person 关联
val personRDD = lineRDD.map(x => Person(x(0).toInt, x(1), x(2).toInt)) //RDD[Person] , DataSet
# 将 RDD 转换成 DataFrame
val personDF = personRDD.toDF //DataFrame

# 查看数据
personDF.show
personDF.select(personDF.col("name")).show
personDF.select(personDF("name")).show
personDF.select(col("name")).show
personDF.select("name").show 

También es posible construir el DataFrame directamente:

val dataFrame=spark.read.text("file:///Users/zhoushimiao/Desktop/people.txt")
dataFrame.show //注意:直接读取的文本文件没有完整schema信息
dataFrame.printSchema

# 或者, 直接读取JSON文件, json中已经包含了schema信息
val jsonDF= spark.read.json("file:///Users/zhoushimiao/Desktop/people.json")
jsonDF.show

estilo SQL: 

# 将DataFrame 转换成注册表
personDF.createOrReplaceTempView("t_person")

# 执行查询SQL
spark.sql("select id,name from t_person where id > 3").show

        Spark SQL proporciona dos API diferentes, sintaxis SQL y API DataFrame/Dataset, también conocida como Spark SQL DSL (lenguaje específico del dominio):

  • Sintaxis SQL: Spark SQL admite la sintaxis de consulta SQL estándar y las declaraciones SQL se pueden usar para consultar, filtrar y agregar datos. La ventaja de usar la sintaxis SQL es que puede usar directamente declaraciones SQL familiares para el procesamiento de datos sin aprender nuevas API. La sintaxis de SQL es intuitiva y conveniente cuando se trabaja con datos estructurados y es adecuada para desarrolladores familiarizados con SQL.
  • DataFrame/Dataset API (Spark SQL DSL): DataFrame/Dataset API es un conjunto de interfaces de programación proporcionado por Spark SQL. Está basado en RDD, pero proporciona la capacidad de organizar datos de forma estructurada. DataFrame/Dataset API define métodos de operación enriquecidos, lo que permite a los desarrolladores construir mediante programación operaciones de consulta y transformación. Proporciona operaciones con seguridad de tipos, puede detectar errores en tiempo de compilación y proporciona una mejor optimización del rendimiento y capacidades de ajuste de código.

Procesamiento de flujo de Spark

        Tanto Spark Streaming como Structured Streaming son módulos de procesamiento de flujo de Apache Spark para procesar flujos de datos en tiempo real.

Transmisión de chispa: 

        Spark Streaming es un marco de computación en tiempo real basado en Spark Core. Puede consumir datos de muchas fuentes de datos y procesar los datos en tiempo real. Tiene las características de alto rendimiento y fuerte tolerancia a fallas. Spark Streaming utiliza DStreams (Discrete Streams), que pueden utilizar funciones avanzadas y operaciones RDD para el procesamiento de datos.

Características de Spark Streaming: 

  1. Baja latencia: Spark Streaming logra un procesamiento de flujo de baja latencia al dividir los datos del flujo de entrada en pequeños lotes y procesar cada lote. De forma predeterminada, cada tamaño de lote es de segundos.
  2. Tolerancia a fallas: Spark Streaming proporciona un mecanismo de tolerancia a fallas que puede manejar la falla del nodo o la pérdida de datos. Utiliza conjuntos de datos distribuidos resistentes (RDD) de Spark para recuperar datos perdidos y nodos fallidos.

  3. Escalabilidad: Spark Streaming se puede integrar con otros módulos de Spark (como Spark SQL, MLlib) para lograr tareas de análisis y procesamiento de transmisiones más complejas. Puede ejecutarse en clústeres masivos y escalar automáticamente en función del volumen y la carga de datos.

  4. Compatibilidad con fuentes de datos enriquecidas: Spark Streaming admite una variedad de fuentes de datos, incluidas Kafka, Flume, Hadoop HDFS, Apache NiFi, etc. Esto simplifica la obtención de datos de transmisión de múltiples fuentes y el envío de resultados de procesamiento de datos a múltiples destinos.

Código de muestra: 

import org.apache.spark.streaming._

// 创建StreamingContext,批次时间间隔为5秒
val ssc = new StreamingContext(sparkConf, Seconds(5)) 
// 从TCP套接字获取流数据
val lines = ssc.socketTextStream("localhost", 9999)

// 对流数据进行处理
val words = lines.flatMap(_.split(" "))
val wordCounts = words.map(word => (word, 1)).reduceByKey(_ + _)

// 打印处理结果
wordCounts.print()

// 启动StreamingContext 和 等待流数据的到达和处理
ssc.start() 
ssc.awaitTermination() 

ssc.stop()

Transmisión estructurada

        La transmisión estructurada se basa en DataFrame and Dataset API, lo que facilita la conversión entre el código de procesamiento de transmisión y el código de procesamiento por lotes. Los desarrolladores pueden usar consultas SQL, operaciones DataFrame y funciones integradas para procesar datos de transmisión en tiempo real, lo que les permite realizar el procesamiento de transmisión de manera similar al procesamiento de datos estáticos.

        Se puede entender que la transmisión estructurada asigna fuentes de datos a tablas similares a las bases de datos relacionales, y luego asigna los resultados calculados a otra tabla para operar la transmisión de datos de manera estructurada.Este modelo de programación es muy propicio para el procesamiento y análisis de datos estructurados. datos en tiempo real ; Spark Structured Streaming proporciona un modelo de programación de procesamiento de transmisión más simplificado y unificado, lo que hace que el procesamiento de datos de transmisión en tiempo real sea más fácil e intuitivo. Admite consultas y operaciones más complejas y proporciona abstracciones de alto nivel y garantías semánticas para satisfacer diversas necesidades de procesamiento de datos en tiempo real.        

Características y beneficios clave:

  1. Basado en la hora del evento (event time): la transmisión estructurada admite el procesamiento de datos en función de la hora del evento, lo que significa que los datos se pueden procesar de acuerdo con la hora real en que ocurre el evento, en lugar de según la hora en que llegan los datos. Esto es útil para manejar datos de eventos retrasados ​​y desordenados.

  2. Garantía semántica exactamente una vez: el streaming estructurado proporciona una garantía semántica "exactamente una vez" (exactamente una vez) para garantizar la consistencia y confiabilidad de los resultados de salida, es decir, cada dato de entrada se procesará exactamente una vez sin ninguna pérdida o pérdida. datos.

  3. Abstracción avanzada: la transmisión estructurada proporciona abstracciones avanzadas, como la operación de ventanas, la conexión de datos estáticos de flujo, la conexión de flujo a flujo, etc., lo que hace que sea más fácil e intuitivo manejar escenarios de procesamiento de flujo más complejos.

  4. Admite fuentes de datos enriquecidas y sumideros de datos: la transmisión estructurada admite múltiples fuentes y sumideros de datos, como Kafka, Socket, File, HDFS, etc., y puede enviar los resultados del procesamiento a sistemas de almacenamiento externos (como Hadoop HDFS, Apache Cassandra) o integre otros módulos como Spark SQL para un análisis más detallado.

Código de muestra: 

// 导入必要的库
import org.apache.spark.sql.SparkSession

// 创建SparkSession, 也是一个Structured Streaming作业
val spark = SparkSession
  .builder()
  .appName("StructuredStreamingExample")
  .getOrCreate()

// 从数据源创建输入DataFrame
val inputData = spark
  .readStream
  .format("socket")
  .option("host", "localhost")
  .option("port", 9999)
  .load()

// 对输入数据进行转换和处理
val transformedData = inputData
  .groupBy("word")
  .count()

// 将结果DataFrame写入输出数据接收器
val query = transformedData
  .writeStream
  .format("console")
  .outputMode("complete")
  .start()

query.awaitTermination()

Spark Streaming VS Streaming Estructurado

terreno común: 

  1. Basado en Spark: ambos se basan en el marco IQ de Spark, que puede aprovechar las capacidades informáticas distribuidas y los motores de optimización de Spark.
  2. Alta confiabilidad y tolerancia a fallas: Ambos tienen capacidades de recuperación de fallas y tolerancia a fallas para manejar fallas de nodos y pérdida de datos.
  3. Fuentes de datos y receptores de datos integrados: ambos admiten una amplia gama de fuentes de datos, incluidos Kafka, Flume, Hadoop HDFS, etc., y pueden enviar los resultados del procesamiento a sistemas de almacenamiento externos o integrar otros módulos para el procesamiento.

la diferencia: 

  1. Modelo de procesamiento de datos:
    1. Spark Streaming se basa en el modelo de procesamiento de microlotes, que divide el flujo de datos en tiempo real en pequeños lotes y luego procesa cada lote;
    2. El Steaming estructurado se basa en un modelo de procesamiento continuo, procesando datos en una forma similar a las tablas estáticas, que pueden proporcionar resultados más cercanos a los de tiempo real.
  2. Tolerancia a fallas y consistencia: 
    1. La tolerancia a fallas de Spark Streaming depende más del intervalo del lote y no proporciona garantías estrictas de consistencia.
    2. La transmisión estructurada proporciona garantías semánticas exactamente una vez para garantizar resultados consistentes y confiables
  3. API y semántica: 
    1. Spark Streaming usa DStream (flujo discreto), que puede usar funciones avanzadas y operaciones RDD para el procesamiento de datos
    2. La transmisión estructurada proporciona garantías semánticas exactamente una vez para garantizar resultados consistentes y confiables

        Además, Structured Streaming proporciona un modelo de programación más simple e intuitivo, los desarrolladores pueden desarrollar a través de API similares a las tablas estáticas, lo que reduce el costo de aprendizaje entre el paradigma de programación del procesamiento por lotes.

Chispa aleatoria 

        Shuffle se refiere a la operación de barajado de datos que se produce durante el reparticionamiento y la reorganización de los datos.

        En el marco MapReduce, la etapa Shuffle es un puente que conecta Map y Reduce, y la etapa Map transmite datos a Reduce a través del proceso Shuffle. Dado que Shuffle implica lectura y escritura de disco y E/S de red, el rendimiento de Shuffle afecta directamente el rendimiento de todo el programa. Spark también tiene etapas Map y Reduce, por lo que también se producirá la reproducción aleatoria.

Shuffle generalmente ocurre en las siguientes situaciones

  1. Repartición de datos: cuando es necesario redistribuir los datos a diferentes nodos en el clúster de acuerdo con las claves u otras condiciones, se producirá la operación aleatoria de repartición de datos. Como GroupByKey, ReduceByKey y otras operaciones
  2. Fusión de datos: cuando es necesario fusionar datos de diferentes nodos, se producirá la operación aleatoria de fusión de datos. Como la operación de unión

El proceso Shuffle se divide en tres etapas:

  1. Etapa del mapa: cada ejecutor generará un conjunto de resultados intermedio de acuerdo con la clave de datos u otras condiciones, y lo escribirá en el disco local
  2. Etapa aleatoria: Spark ordena los resultados intermedios en cada Ejecutor de acuerdo con la partición de destino y combina los datos de la misma partición en un bloque de archivos y los escribe en el disco. En esta etapa, se diseña una gran cantidad de transmisión de datos y lectura y escritura de IO.
  3. Fase de reducción: los datos de cada partición se enviarán a la tarea de reducción correspondiente para su agregación, fusión y cálculo.

Hay tres implementaciones principales de Shuffle en Spark:

  1. SortShuffle (ordenar aleatoriamente):
    SortShuffle es un tipo de implementación de Shuffle basada en la clasificación. Primero escribe la salida de datos de la tarea Map en el disco local, y luego la tarea Reducer extrae los datos del nodo donde se encuentra la tarea Map. y ordena por clave y se fusiona. La ventaja de SortShuffle es que puede garantizar el orden global de los datos de resultados , lo cual es adecuado para escenarios que requieren clasificación global, pero puede generar una gran cantidad de sobrecarga de transmisión de red y escritura en disco.

  2. HashShuffle (hash shuffle):
    HashShuffle es una implementación de Shuffle basada en hash. Agrupa la salida de datos de la tarea Map de acuerdo con el valor hash de la clave y envía los datos de la misma clave a la misma tarea Reducer para su procesamiento. HashShuffle tiene una buena escalabilidad porque los datos se dispersan durante la transmisión de la red, lo que reduce la escritura en disco y la sobrecarga de transmisión de la red. Pero no garantiza el ordenamiento global del resultado.

  3. TungstenSortShuffle (ordenación aleatoria de Tungsten):
    TungstenSortShuffle es una implementación optimizada de Shuffle que mejora el rendimiento de SortShuffle mediante el uso del motor de clasificación Tungsten. TungstenSortShuffle utiliza una combinación de memoria y disco para clasificar y combinar, lo que puede reducir significativamente la sobrecarga de lectura y escritura del disco durante el proceso de clasificación y combinación y mejorar el rendimiento de Shuffle.

        De forma predeterminada, Spark seleccionará automáticamente una implementación de Shuffle adecuada en función de los recursos disponibles y los parámetros de configuración. Normalmente, SortShuffle se utilizará cuando la memoria disponible sea pequeña y TungstenSortShuffle se utilizará para mejorar el rendimiento cuando el sistema tenga suficientes recursos de memoria.

Método de optimización del rendimiento aleatorio:

  • Ajustar el paralelismo: al ajustar el número de particiones y los parámetros de paralelismo, se puede reducir el movimiento de datos y la transmisión de la red, mejorando así el rendimiento de Shuffle.

  • Fusionar por adelantado: para múltiples operaciones aleatorias, se pueden fusionar para reducir el número de aleatorias.

  • Utilice la optimización de localidad: mediante el uso de la programación de localidad de datos y la ejecución predictiva, las tareas se asignan a los nodos lo más cerca posible de los datos, lo que reduce la sobrecarga de transmisión y lectura de datos.

  • Use la combinación de memoria y disco: configure razonablemente la proporción de uso de memoria y disco, almacene los datos apropiados en la memoria y reduzca las operaciones de lectura y escritura en el disco.

  • Use la compresión: para la operación aleatoria de una gran cantidad de datos, puede considerar usar la compresión de datos para reducir la sobrecarga de transmisión de red y almacenamiento en disco.

El proceso de barajar es una parte muy costosa de la computación porque implica mover, clasificar y leer y escribir datos hacia y desde el disco. Por lo tanto, optimizar la operación Shuffle es muy importante para mejorar el rendimiento y la eficiencia de las aplicaciones Spark.

Supongo que te gusta

Origin blog.csdn.net/zhoushimiao1990/article/details/131794076
Recomendado
Clasificación