Skip to main content

Command Palette

Search for a command to run...

Deletion Vectors en Delta Lake: Funcionamiento interno, impacto en el rendimiento y recomendaciones prácticas

Updated
7 min read

Los Deletion Vectors (DV) son una de las funcionalidades más relevantes en Delta Lake para acelerar las operaciones de modificación de datos.

¿Cómo funcionan los Deletion Vectors?

Tradicionalmente, Delta Lake utiliza un enfoque Copy-on-Write:
cuando se elimina, actualiza o mergea una fila dentro de un archivo Parquet, el archivo completo debe reescribirse, excluyendo las filas afectadas. Esto es costoso en E/S, especialmente para archivos grandes o modificaciones puntuales.

Con los Deletion Vectors, Delta introduce un modelo Merge-on-Read:

  • Los archivos Parquet originales no se reescriben.

  • Las filas eliminadas se registran en un archivo auxiliar comprimido (un .bin).

  • Durante la lectura, el motor aplica el vector de eliminación y descarta esas posiciones lógicamente.

  • El coste de reescritura total se pospone a operaciones posteriores como OPTIMIZE o REORG TABLE ... APPLY (PURGE).

Este enfoque reduce de manera drástica la E/S asociada a las operaciones de DELETE/UPDATE/MERGE.


🛠️ Ejemplo Práctico en Microsoft Fabric

A continuación definimos dos tablas Delta:
una sin Deletion Vectors (Copy-on-Write) y otra con DV habilitado (Merge-on-Read).

Preparación y creación de tablas

Primero, creamos los datos base y luego las dos tablas, una sin la propiedad Deletion Vectors y otra con esta propiedad habilitada.

data = [
    (1, "Ana", "Ventas"), 
    (2, "Luis", "IT"), 
    (3, "Marta", "Marketing"), 
    (4, "Carlos", "Ventas"), 
    (5, "Elena", "IT")
]
columns = ["id", "nombre", "departamento"]
df = spark.createDataFrame(data, columns)

# --- Tabla SIN Deletion Vectors (Comportamiento Predeterminado) ---
print("Creando tabla SIN Deletion Vectors...")
df.coalesce(1).write.format("delta").mode("overwrite").saveAsTable("tabla_sin_dv")

# --- Tabla CON Deletion Vectors ---
print("Creando tabla CON Deletion Vectors...")
df.coalesce(1).write.format("delta").mode("overwrite") \
  .option("overwriteSchema", "true") \
  .option("delta.enableDeletionVectors", "true") \
  .saveAsTable("tabla_con_dv")

Puedes verificar la configuración de cada tabla con:

SHOW TBLPROPERTIES tabla_sin_dv;
SHOW TBLPROPERTIES tabla_con_dv;

📂 Comportamiento SIN Deletion Vectors (Copy-on-Write)

Antes del DELETE, la tabla contiene un único Parquet.

Se puede ver que existe un único fichero parquet y un commit en la carpeta _delta_log. Tras ejecutar:

spark.sql(f"DELETE FROM tabla_sin_dv WHERE id = 3")

Delta:

  1. marca el archivo original como remove en _delta_log,

  2. genera un archivo Parquet nuevo con 4 filas,

  3. registra el add correspondiente.

Ejemplo mínimo del commit JSON:

{
    ...
        "operationMetrics": {
            "numRemovedFiles": "1",
            "numCopiedRows": "4",
            "numDeletionVectorsAdded": "0",
            "numDeletionVectorsRemoved": "0",
            "numAddedChangeFiles": "0",
            "numDeletionVectorsUpdated": "0",
            "numDeletedRows": "1",
            "numAddedFiles": "1",
            ...
        ...
}
{
    "remove": {
        "path": "part-00000-ff53dcfe-e9ee-44c5-ae25-c9e90ba6ff46-c000.snappy.parquet",
        "deletionTimestamp": 1764081601227,
        ...
    }
}
{
    "add": {
        "path": "part-00000-3c93366e-b599-49fe-99e0-f2087490b294-c000.snappy.parquet",
        "modificationTime": 1764081601092,
        ...
    }
}

📂 Comportamiento CON Deletion Vectors (Merge-on-Read)

Antes del DELETE, la tabla también tiene un único Parquet.

Se puede ver que también existe un único fichero parquet y un commit en la carpeta _delta_log. Tras ejecutar:

spark.sql(f"DELETE FROM tabla_con_dv WHERE id = 3")

Haciendo el mismo borrado pero para la tabla con Deletion Vectors habilitado, el contenido de la carpeta es el siguiente:

Delta:

  1. mantiene el archivo Parquet tal cual,

  2. añade un archivo deletion_vector_....bin con la posición invalidada,

  3. actualiza el commit indicando el DeletionVector aplicado.

Nuestro nuevo commit contendría lo siguiente, donde se elimina la referencia al archivo Parquet existente y se añade un puntero al mismo archivo Parquet con un vector de eliminación:

{
    ...
        "operationMetrics": {
            ...
            "numRemovedFiles": "0",
            "numCopiedRows": "0",
            "numDeletionVectorsAdded": "1",
            "numDeletionVectorsRemoved": "0",
            "numAddedChangeFiles": "0",
            "numDeletionVectorsUpdated": "0",
            "numDeletedRows": "1",
            "numAddedFiles": "0",
            ...
        },
        ...
}
{
    "add": {
        "path": "part-00000-67be0c96-23bd-48e4-97a1-c0b43f615ad3-c000.snappy.parquet",
        ...
        "deletionVector": {
            "storageType": "u",
            "pathOrInlineDv": "5+[vtVR%EkPn{Xs}UU8<",
            "offset": 1,
            "sizeInBytes": 34,
            "cardinality": 1
        }
    }
}
{
    "remove": {
        "path": "part-00000-67be0c96-23bd-48e4-97a1-c0b43f615ad3-c000.snappy.parquet",
        ...
    }
}

Resumen de los dos comportamientos

TablaCambios en archivosComportamiento
tabla_sin_dvHay 2 archivos Parquet: el original se marca como eliminado en el Log Delta, y se escribe un archivo Parquet nuevo y más pequeño (el Copy-on-Write).Se reescribe el archivo afectado.
tabla_con_dvSe mantienen los archivos Parquet originales y un nuevo archivo con la extensión .bin (el Deletion Vector).Se escribe solo el Deletion Vector. El archivo Parquet original NO se reescribe, solo se marca la posición de la fila eliminada.

📊 Análisis del impacto en rendimiento

Ahora que comprendemos cómo funcionan conceptualmente los vectores de eliminación, veamos el impacto real en el rendimiento.

Para ello, he utilizado un conjunto de datos idéntico de 100 millones de filas en dos tablas Delta diferentes, una con vectores de eliminación habilitados y otra sin. Las pruebas que he realizado para medir el impacto en el rendimiento son:

  • Borrado de un registro

  • Borrado del 25% de la tabla

  • Actualización del 5% de la tabla

  • MERGE de un nuevo dataset que contiene 2 millones de filas (2%) en la tabla existente

  • SELECT COUNT(1) WHERE

  • SELECT SUM()

  • OPTIMIZE

  • VACUUM

Las operaciones se han hecho en el mismo orden que aparecen en el listado y los resultados han sido los siguientes:

⏱️ Resultados principales

  • DELETE (1 fila): La tabla con Deletion Vectors realiza el borrado 6.2x más rápido

  • DELETE (25M): La tabla con Deletion Vectors realiza el borrado 3.2x más rápido

  • UPDATE (5M): La tabla con Deletion Vectors realiza el update 3.5x más lento

  • MERGE (2M): rendimiento similar, ligera penalización con DV

  • OPTIMIZE: Deletion Vectors es 208x más rápido

  • VACUUM: Deletion Vectors es 1.6x más rápido

  • SELECT COUNT(1): DV es 5.9x más lento

  • SELECT SUM(price): DV es 1.8x más lento

Esto nos lleva a un punto clave:
el beneficio de DV es enorme en escritura, pero las lecturas pueden penalizarse seriamente.


🔍 ¿Por qué las lecturas son más lentas con Deletion Vectors?

La causa no es el DV en sí, sino el modelo Merge-on-Read, donde el lector debe:

  1. Leer los archivos Parquet originales

  2. Leer los vectores de eliminación asociados

  3. Combinar ambas fuentes

  4. Filtrar las filas inválidas

  5. Reconstruir el dataset resultante.

Cuantos más deletes/updates acumula una tabla, más trabajo deben hacer los lectores.

📈 El efecto se amplifica si no se ejecuta OPTIMIZE

En las pruebas:

  • SELECT COUNT(1) → 5.9x más lento

  • SELECT SUM() → 1.8x más lento

Esto se debe a que el motor debe leer más datos de los necesarios, incluyendo registros ya invalidados.

🧹 El remedio: compactación

Después de un:

OPTIMIZE <tabla>

o

REORG TABLE <tabla> APPLY (PURGE)

la tabla queda físicamente limpia y las lecturas vuelven a ser rápidas.


🧭 ¿Cuándo habilitar Deletion Vectors?

✔️ Casos recomendados

1. Capas Bronze y Silver

Ideal cuando:

  • hay ingestas frecuentes

  • existen deletes/updates parciales

  • se realizan merges incrementales

  • la prioridad es la velocidad de ingestión.

2. Workloads MoR donde la E/S es el cuello de botella

DV evita reescrituras costosas en Parquet.

3. Cuando hay una estrategia de optimización establecida

Es imprescindible:

  • Programar OPTIMIZE o REORG ... APPLY (PURGE)

  • Ejecutar VACUUM periódicamente

❌ Casos NO recomendados

1. Tablas con pocas escrituras y muchas lecturas

Ejemplos:

  • tablas Gold

  • modelos de agregación

  • capas analíticas puras

  • dashboards con baja latencia

  • Power BI Direct Lake

Aquí CoW suele ser más eficiente.

2. Problemas de compatibilidad

DV requiere:

  • Delta Lake 2.3+

  • Reader version ≥ 3

  • Writer version ≥ 7

3. Fabric Copy Data Activity (limitación temporal)

Copy Data ignora Deletion Vectors → pueden reaparecer filas “borradas”.
Esto se resolverá (si no se ha resuelto ya) cuando Fabric actualice el soporte a esta funcionalidad.

💡 Conclusión

Los Deletion Vectors representan una innovación clave en Delta Lake que mejora la eficiencia de las operaciones de escritura y reduce la E/S del sistema de forma drástica. Sin embargo, su adopción implica entender claramente las implicaciones del modelo Merge-on-Read, especialmente en términos de rendimiento de lectura.

En escenarios con cargas frecuentes y actualizaciones parciales —especialmente en capas Bronze y Silver— los DV proporcionan mejoras significativas. En contrapartida, en modelos orientados a lectura intensiva como las capas Gold, puede ser preferible mantener el enfoque tradicional Copy-on-Write o aplicar un mantenimiento regular que elimine los vectores acumulados.

En resumen:

  • DV aceleran la escritura

  • Penalizan las lecturas si no hay mantenimiento

  • Ofrecen el mejor rendimiento total cuando se combinan con OPTIMIZE.

More from this blog

D

DataGym | Microsoft Fabric

36 posts