Apache Spark 4 en 2026: Nuevas Funcionalidades, Structured Streaming y Preguntas de Entrevista

Guía completa sobre Apache Spark 4: modo ANSI SQL, tipo de dato VARIANT, Real-Time Mode streaming, Spark Connect y preguntas de entrevista para data engineering.

Apache Spark 4 nuevas funcionalidades y streaming estructurado

Apache Spark 4 representa la renovación más significativa del motor Spark desde la versión 3.0. Lanzado a mediados de 2025 con iteraciones rápidas hasta la versión 4.1 y la próxima 4.2, la línea 4.x redefine cómo los ingenieros de datos construyen pipelines, ejecutan cargas de trabajo de streaming y manejan SQL a gran escala. Esta guía desglosa los cambios críticos, los demuestra con código PySpark y Scala, y cubre las preguntas de entrevista que los equipos de contratación formulan con mayor frecuencia.

Cronología de Lanzamientos de Spark 4

Spark 4.0.0 se lanzó en mayo de 2025 con más de 5,100 tickets resueltos por 390 colaboradores. Spark 4.1.0 siguió en diciembre de 2025, agregando Real-Time Mode para streaming y Declarative Pipelines. Las versiones de vista previa de Spark 4.2 comenzaron en febrero de 2026.

Modo ANSI SQL Ahora Activado por Defecto en Spark 4

Spark 4.0 cambia spark.sql.ansi.enabled a true. Cada desbordamiento aritmético, conversión inválida y división por cero que anteriormente devolvía NULL o truncaba silenciosamente ahora genera una excepción. El objetivo: cumplimiento estricto del estándar SQL, igualando el comportamiento de PostgreSQL y Oracle.

El impacto práctico afecta principalmente a los pipelines ETL. Las consultas que dependían de coerción implícita o propagación silenciosa de NULL fallarán en tiempo de ejecución hasta que se manejen explícitamente.

python
# ansi_mode_migration.py
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, try_cast

spark = SparkSession.builder.appName("ANSIMigration").getOrCreate()

# Esto ahora genera ArithmeticException en modo ANSI de Spark 4
# SELECT CAST('abc' AS INT)  -- error en tiempo de ejecución

# Migración segura: usar TRY_CAST para preservar comportamiento NULL
df = spark.sql("""
    SELECT
        TRY_CAST(raw_value AS INT) AS parsed_value,
        TRY_CAST(amount AS DECIMAL(10,2)) AS safe_amount
    FROM raw_events
    WHERE TRY_CAST(raw_value AS INT) IS NOT NULL
""")

# Alternativamente, envolver aritmética en try_divide / try_add
result = df.withColumn(
    "ratio",
    col("safe_amount") / col("parsed_value")  # genera excepción con cero
)

Lista de verificación para migración: auditar cada llamada a CAST, reemplazar con TRY_CAST donde se espere comportamiento NULL-safe, y agregar verificaciones explícitas de desbordamiento en columnas numéricas. La guía de migración SQL de Spark documenta cada cambio de comportamiento.

Tipo de Dato VARIANT para Datos Semi-Estructurados

Spark 4.0 agrega un tipo de columna nativo VARIANT diseñado para datos semi-estructurados tipo JSON. A diferencia de almacenar cadenas JSON sin procesar y analizarlas en tiempo de consulta, VARIANT usa un formato binario optimizado con fragmentación de columnas que ofrece lecturas hasta 8 veces más rápidas en acceso a campos anidados.

VariantExample.scalascala
import org.apache.spark.sql.SparkSession

val spark = SparkSession.builder.appName("VariantDemo").getOrCreate()
import spark.implicits._

// Crear tabla con columna VARIANT
spark.sql("""
    CREATE TABLE events (
        event_id BIGINT,
        timestamp TIMESTAMP,
        payload VARIANT
    ) USING PARQUET
""")

// Insertar datos JSON — convertidos automáticamente a VARIANT
spark.sql("""
    INSERT INTO events VALUES
    (1, current_timestamp(),
     PARSE_JSON('{"user_id": 42, "action": "click", "metadata": {"page": "/home", "duration_ms": 320}}')),
    (2, current_timestamp(),
     PARSE_JSON('{"user_id": 87, "action": "purchase", "metadata": {"item_id": "SKU-9001", "amount": 49.99}}')),
""")

// Consultar campos anidados con notación de punto — usa fragmentación para acceso rápido
val clicks = spark.sql("""
    SELECT
        event_id,
        payload:user_id::INT AS user_id,
        payload:action::STRING AS action,
        payload:metadata.page::STRING AS page
    FROM events
    WHERE payload:action::STRING = 'click'
""")
clicks.show()

VARIANT elimina la necesidad de soluciones alternativas con schema-on-read. En arquitecturas de data lake donde los esquemas de eventos evolucionan frecuentemente, una sola columna VARIANT absorbe cambios de esquema sin migraciones DDL. Spark 4.1 extendió VARIANT con fragmentación a nivel de Parquet, reduciendo aún más el IO para acceso selectivo a campos.

Real-Time Mode Streaming en Spark 4.1

Structured Streaming en Spark 4.1 introduce Real-Time Mode (RTM), el primer soporte oficial para latencia de sub-segundo en Spark. Para cargas de trabajo sin estado, la latencia cae a milisegundos de un solo dígito, compitiendo directamente con Apache Flink en su propio territorio.

Tres cambios arquitectónicos hacen esto posible: flujo continuo de datos (sin límites de micro-batch), programación de pipeline (etapas read/compute/write superpuestas) y shuffle en streaming (estado almacenado en memoria en lugar de checkpoints entre lotes).

python
# real_time_streaming.py
from pyspark.sql import SparkSession
from pyspark.sql.functions import from_json, col, window
from pyspark.sql.types import StructType, StructField, StringType, DoubleType, TimestampType

spark = SparkSession.builder \
    .appName("RTMDemo") \
    .config("spark.sql.streaming.mode", "realtime") \
    .getOrCreate()

schema = StructType([
    StructField("sensor_id", StringType()),
    StructField("temperature", DoubleType()),
    StructField("event_time", TimestampType())
])

# Leer desde Kafka con Real-Time Mode habilitado
raw_stream = spark.readStream \
    .format("kafka") \
    .option("kafka.bootstrap.servers", "broker:9092") \
    .option("subscribe", "sensor-readings") \
    .option("startingOffsets", "latest") \
    .load()

parsed = raw_stream \
    .select(from_json(col("value").cast("string"), schema).alias("data")) \
    .select("data.*")

# Escribir a sink Kafka — end-to-end de sub-segundo
query = parsed \
    .filter(col("temperature") > 80.0) \
    .writeStream \
    .format("kafka") \
    .option("kafka.bootstrap.servers", "broker:9092") \
    .option("topic", "alerts") \
    .option("checkpointLocation", "/tmp/checkpoints/alerts") \
    .start()

query.awaitTermination()

Activar RTM requiere un solo cambio de configuración: spark.sql.streaming.mode = realtime. No se necesita reescribir código.

¿Listo para aprobar tus entrevistas de Data Engineering?

Practica con nuestros simuladores interactivos, flashcards y tests técnicos.

Spark Connect y el Cliente PySpark Ligero

Spark 4.0 incluye un paquete independiente pyspark-client que pesa solo 1.5 MB, en comparación con la distribución completa de PySpark de más de 300 MB.

python
# spark_connect_client.py
from pyspark.sql import SparkSession

# Conectar a clúster Spark remoto — no se necesita JVM local
spark = SparkSession.builder \
    .remote("sc://spark-cluster.internal:15002") \
    .getOrCreate()

# API completa de DataFrame disponible a través del cliente ligero
df = spark.read.parquet("s3a://data-lake/events/2026/")

aggregated = df.groupBy("country", "event_type") \
    .count() \
    .orderBy("count", ascending=False)

aggregated.show(20)

Esta arquitectura separa completamente el código de aplicación del runtime de Spark. Los notebooks de Jupyter, scripts de análisis y herramientas de BI pueden ejecutar lógica de Spark sin instalar Java o gestionar dependencias JVM locales. Spark Connect usa gRPC para comunicación cliente-servidor con soporte automático de reintentos, balanceo de carga y tolerancia a fallos.

API transformWithState para Lógica de Streaming Compleja

Spark 4.0 reemplaza el limitado mapGroupsWithState con transformWithState. La API anterior requería gestión manual de timeouts, no soportaba múltiples estados por clave y obligaba a los desarrolladores a implementar lógica de limpieza personalizada. transformWithState introduce gestión declarativa de estado con soporte para múltiples variables de estado, TTL configurables y procesamiento basado en temporizadores.

SessionTracker.scalascala
import org.apache.spark.sql.streaming._
import org.apache.spark.sql.streaming.StatefulProcessor
import java.time.Duration

case class UserEvent(userId: String, action: String, timestamp: Long)
case class SessionSummary(userId: String, actionCount: Int, durationMs: Long)

class SessionProcessor extends StatefulProcessor[String, UserEvent, SessionSummary] {

  @transient private var sessionStart: ValueState[Long] = _
  @transient private var actionCount: ValueState[Int] = _

  override def init(outputMode: OutputMode, timeMode: TimeMode): Unit = {
    sessionStart = getHandle.getValueState[Long]("start", TTLConfig(Duration.ofMinutes(30)))
    actionCount = getHandle.getValueState[Int]("count", TTLConfig(Duration.ofMinutes(30)))
  }

  override def handleInputRows(
      key: String,
      rows: Iterator[UserEvent],
      timerValues: TimerValues
  ): Iterator[SessionSummary] = {
    rows.flatMap { event =>
      if (!sessionStart.exists()) {
        sessionStart.update(event.timestamp)
        actionCount.update(1)
        Iterator.empty
      } else {
        val count = actionCount.get() + 1
        actionCount.update(count)
        if (count % 10 == 0) {
          val duration = event.timestamp - sessionStart.get()
          Iterator(SessionSummary(key, count, duration))
        } else Iterator.empty
      }
    }
  }
}

Los temporizadores permiten ejecutar lógica en momentos específicos independientemente de la llegada de datos. Las aplicaciones pueden implementar timeouts de sesión, agregaciones basadas en ventanas de tiempo y limpieza de estado sin depender de marcas de agua (watermarks).

Spark Declarative Pipelines (SDP) en Spark 4.1

Spark Declarative Pipelines introduce programación funcional similar a dbt para pipelines de Spark. Los desarrolladores definen tablas como funciones Python decoradas con @spark.declarative.table. El runtime de Spark construye automáticamente el DAG de dependencias, ejecuta transformaciones en orden y gestiona la materialización de tablas.

python
# declarative_pipeline.py
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, sum as _sum, current_date

spark = SparkSession.builder.appName("SDP-Demo").getOrCreate()

@spark.declarative.table("clean_orders")
def clean_orders():
    return spark.read.table("raw_orders") \
        .filter(col("status") != "cancelled") \
        .filter(col("amount") > 0)

@spark.declarative.table("daily_revenue")
def daily_revenue():
    return spark.read.table("clean_orders") \
        .groupBy("order_date", "region") \
        .agg(_sum("amount").alias("total_revenue"))

@spark.declarative.table("revenue_summary")
def revenue_summary():
    return spark.read.table("daily_revenue") \
        .groupBy("region") \
        .agg(_sum("total_revenue").alias("grand_total")) \
        .orderBy("grand_total", ascending=False)

spark.declarative.run()

SDP detecta automáticamente dependencias analizando las llamadas spark.read.table(). Cuando se ejecuta spark.declarative.run(), el motor materializa tablas en orden topológico, paraleliza transformaciones independientes y gestiona el linaje de datos. El resultado es código más limpio, mejor observabilidad y menos código repetitivo de orquestación.

Preguntas de Entrevista sobre Spark 4 para Data Engineering

1. ¿Qué cambia cuando el modo ANSI está habilitado por defecto en Spark 4?

Cuando el modo ANSI está habilitado (spark.sql.ansi.enabled = true), Spark cumple estrictamente el estándar SQL ANSI en lugar de usar comportamiento permisivo. Los cambios clave incluyen: (1) Conversiones de tipo inválidas generan excepciones en lugar de devolver NULL, (2) Desbordamientos aritméticos fallan en lugar de truncar silenciosamente, (3) División por cero genera error en lugar de devolver NULL, (4) Acceso a elementos de array fuera de límites genera excepción. Los pipelines ETL existentes que dependían del comportamiento permisivo deben migrarse usando funciones TRY_* como TRY_CAST, TRY_DIVIDE y TRY_ADD para preservar semántica NULL-safe donde sea necesario.

2. ¿Qué es el tipo de dato VARIANT y cuándo usarlo?

VARIANT es un tipo de columna nativo introducido en Spark 4.0 optimizado para almacenar datos semi-estructurados tipo JSON. A diferencia de almacenar JSON como strings y analizar en tiempo de consulta, VARIANT usa representación binaria con fragmentación de columnas (shredding) que acelera lecturas hasta 8x para acceso a campos anidados. Se debe usar VARIANT cuando: (1) Los esquemas de eventos evolucionan frecuentemente y se necesita flexibilidad de esquema, (2) Solo se consultan subconjuntos de campos anidados regularmente, (3) Los datos contienen estructuras anidadas profundas con cardinalidad variable, (4) Se trabaja con arquitecturas de data lake donde el esquema en escritura es impracticable. Spark 4.1 extendió VARIANT con soporte de fragmentación a nivel Parquet, permitiendo que el motor salte datos de columna irrelevantes durante escaneos.

Tip de Entrevista

Cuando se discute VARIANT, mencionar la diferencia con alternativas previas como get_json_object() o esquemas de struct anidados demuestra comprensión de evolución arquitectónica. Los entrevistadores valoran candidatos que pueden articular trade-offs entre flexibilidad de esquema y rendimiento de consulta.

3. ¿Cómo difiere Real-Time Mode del streaming por micro-batch?

Real-Time Mode (RTM), introducido en Spark 4.1, elimina los límites de micro-batch completamente. Structured Streaming tradicional procesa datos en micro-batches discretos (típicamente 100ms-1s), donde cada batch pasa por ciclos read-compute-write secuenciales. RTM implementa tres cambios: (1) Flujo continuo de datos sin barreras de batch, (2) Programación de pipeline en etapas (stages) que superpone lectura, cómputo y escritura, (3) Shuffle en streaming que mantiene estado en memoria sin checkpointing entre batches. Para cargas sin estado, la latencia cae a milisegundos de un solo dígito. Las aplicaciones con estado usan agregaciones incrementales en ventanas deslizantes. RTM se activa con spark.sql.streaming.mode = realtime sin cambios de código. La principal limitación: cargas de trabajo con estado pesado aún enfrentan sobrecarga de checkpoint; RTM brilla en casos de uso de transformación y filtrado de baja latencia.

4. ¿Qué es transformWithState y por qué reemplaza mapGroupsWithState?

transformWithState es la nueva API de procesamiento con estado en Spark 4.0 que reemplaza mapGroupsWithState y flatMapGroupsWithState. Las APIs anteriores tenían limitaciones: (1) Solo una variable de estado por clave, (2) Gestión manual de timeouts con lógica propensa a errores, (3) Sin soporte nativo para múltiples tipos de estado, (4) TTL de estado difícil de implementar. transformWithState introduce: (1) Múltiples ValueState y ListState por clave con TTL declarativos, (2) Procesamiento basado en temporizadores que ejecuta lógica independiente de llegada de datos, (3) Mejor rendimiento mediante evitación de serialización, (4) Tipos seguros con manejo claro de estado. Los casos de uso incluyen: seguimiento de sesiones de usuario con timeouts, agregaciones en ventanas personalizadas, máquinas de estado para procesamiento de eventos complejos, y joins entre streams con búferes de estado.

Confusión Común: Spark Connect vs Apache Livy

Los candidatos frecuentemente confunden Spark Connect con Apache Livy. Livy es un servidor REST para enviar trabajos batch remotos; Spark Connect es un protocolo cliente-servidor que expone la API completa de DataFrame a través de gRPC con clientes ligeros. Livy opera a nivel de job; Connect opera a nivel de DataFrame con ejecución lazy. Clarificar esta distinción en entrevistas demuestra conocimiento arquitectónico profundo.

5. ¿Qué problema resuelve Spark Connect?

Spark Connect resuelve la dependencia JVM pesada en aplicaciones cliente. Antes de Spark 4.0, ejecutar código PySpark requería una JVM local con todas las dependencias de Spark (300+ MB), haciendo notebooks, scripts de análisis y herramientas de BI difíciles de desplegar. Spark Connect introduce una arquitectura cliente-servidor donde: (1) Los clientes ejecutan un thin client ligero (1.5 MB pyspark-client), (2) Toda ejecución ocurre en el lado del servidor a través de protocolo gRPC, (3) Los clientes envían planes lógicos; el servidor ejecuta planes físicos, (4) Comunicación stateless con reintentos automáticos y tolerancia a fallos. Los casos de uso incluyen: notebooks Jupyter sin gestión JVM, entornos serverless donde las dependencias JVM son prohibitivas, herramientas de BI que consultan clústeres remotos, y ambientes multi-tenant donde la ejecución del cliente debe estar aislada del runtime de Spark.

Construyendo un Pipeline ETL Completo con Spark 4

Un pipeline ETL moderno con Spark 4 combina streaming en tiempo real, validación de datos con modo ANSI, almacenamiento semi-estructurado con VARIANT y arquitectura lakehouse con Delta Lake. El siguiente ejemplo demuestra un pipeline de tres capas (bronze-silver-gold) procesando eventos de usuarios desde Kafka.

python
# spark4_etl_pipeline.py
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, from_json, current_timestamp, parse_json

spark = SparkSession.builder \
    .appName("Spark4ETL") \
    .config("spark.sql.ansi.enabled", "true") \
    .getOrCreate()

bronze = spark.readStream \
    .format("kafka") \
    .option("kafka.bootstrap.servers", "broker:9092") \
    .option("subscribe", "user-events") \
    .load() \
    .select(
        col("key").cast("string").alias("event_key"),
        parse_json(col("value").cast("string")).alias("payload"),
        col("timestamp").alias("kafka_ts")
    )

silver = bronze.select(
    col("event_key"),
    col("payload:user_id").cast("int").alias("user_id"),
    col("payload:action").cast("string").alias("action"),
    col("payload:metadata.session_id").cast("string").alias("session_id"),
    col("kafka_ts"),
    current_timestamp().alias("processed_at")
).filter(
    col("user_id").isNotNull()
)

query = silver.writeStream \
    .format("delta") \
    .outputMode("append") \
    .option("checkpointLocation", "s3a://checkpoints/user-events/") \
    .option("mergeSchema", "true") \
    .toTable("gold.user_events")

query.awaitTermination()

La capa bronze ingiere datos crudos desde Kafka usando parse_json() para convertir payloads JSON a columnas VARIANT. Esto permite consultar campos anidados sin definir esquemas estrictos por adelantado. La capa silver aplica transformaciones de limpieza: extracción de campos tipados con casting seguro, filtrado de registros inválidos usando predicados NULL-check, y adición de metadatos de procesamiento. El modo ANSI asegura que conversiones de tipo inválidas fallen rápido en lugar de corromper datos silenciosamente.

La escritura a Delta Lake con mergeSchema = true permite evolución de esquema automática. Cuando nuevos campos aparecen en los eventos de origen, Delta agrega columnas sin intervención manual. Los checkpoints en S3 proporcionan recuperación exactamente-una-vez (exactly-once) en caso de fallos.

Conclusión: Spark 4 Como Plataforma Unificada para Data Engineering Moderna

Apache Spark 4 cierra las brechas que históricamente empujaban a los equipos hacia motores especializados. Las mejoras clave incluyen:

  • Cumplimiento SQL estricto con modo ANSI por defecto, eliminando comportamiento indefinido que causaba bugs silenciosos en producción
  • Flexibilidad de esquema nativa a través de columnas VARIANT, soportando arquitecturas de data lake sin sacrificar rendimiento de consulta
  • Streaming de latencia ultra-baja con Real-Time Mode, compitiendo directamente con Apache Flink para casos de uso que requieren respuesta de milisegundos
  • Clientes ligeros sin JVM vía Spark Connect, simplificando despliegues de notebooks, herramientas de BI y ambientes serverless
  • Gestión de estado de primera clase con transformWithState, habilitando lógica de procesamiento de eventos complejos sin código repetitivo
  • Programación declarativa de pipelines con SDP, trayendo patrones estilo dbt al ecosistema Spark

Para ingenieros de datos que preparan entrevistas técnicas, dominar estos conceptos demuestra comprensión de arquitectura distribuida moderna. Los empleadores buscan candidatos que pueden articular trade-offs entre consistencia y rendimiento, explicar cuándo usar streaming versus batch, y diseñar pipelines que manejan evolución de esquema gracefully. Spark 4 proporciona las primitivas para construir sistemas de datos robustos y escalables; los profesionales que dominan estas herramientas permanecen altamente competitivos en el mercado de data engineering de 2026.

¡Empieza a practicar!

Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.

Etiquetas

#Apache Spark
#Data Engineering
#Structured Streaming
#PySpark
#Spark Connect

Compartir

Artículos relacionados