Compartir a través de


Descripción del rendimiento de las consultas de Direct Lake

Además del diseño semántico del modelo y la complejidad de las consultas, el rendimiento de Direct Lake depende específicamente de las tablas Delta bien optimizadas para la carga eficaz y rápida de columnas (transcodificación) y la ejecución óptima de las consultas. Asegúrese de aplicar la optimización de orden V. Además, mantenga pequeño el número de archivos Parquet, use grupos de filas grandes y esfuércese por minimizar el efecto de las actualizaciones de datos en el registro Delta. Estos son procedimientos recomendados comunes que pueden ayudar a garantizar una ejecución rápida de consultas en modo Direct Lake en los estados frío, semicaliente, cálido y caliente.

En este artículo se explica cómo el rendimiento de Direct Lake depende del estado de la tabla Delta y de las actualizaciones de datos eficaces. Comprender estas dependencias es fundamental. Aprenderás que la disposición de datos en los archivos Parquet es tan importante para el rendimiento de las consultas como un buen diseño de modelo semántico y medidas de expresión de análisis de datos bien optimizadas (DAX).

Lo que necesita saber

En este artículo se da por supuesto que ya está familiarizado con los conceptos siguientes:

  • Tablas delta en OneLake: información detallada sobre las tablas delta de OneLake está disponible en la documentación de aspectos básicos de Microsoft Fabric.
  • Archivos parquet, grupos de filas y registro delta: el formato de archivo Parquet, incluido cómo se organizan los datos en grupos de filas y fragmentos de columna, y cómo se controlan los metadatos, se explica en la documentación del formato de archivo Parquet. Consulte también la documentación del protocolo de registro de transacciones delta.
  • Optimización de tablas delta y orden V: consulte la documentación de Fabric Lakehouse sobre el mantenimiento de tablas delta, como la optimización de tablas de Delta Lake y el orden V.
  • Marco y transcodificación: la actualización de un modelo semántico de Direct Lake (marco) y la carga de datos de columna a petición (transcodificación) son conceptos importantes, que se tratan en un nivel introductorio en el artículo de introducción a Direct Lake.
  • Motor de fórmulas y almacenamiento: cuando se ejecuta una consulta DAX, el motor de fórmulas genera el plan de consulta y recupera los datos necesarios y agregaciones iniciales del motor de almacenamiento. Las optimizaciones descritas en este artículo se centran en el motor de almacenamiento. Para más información sobre el motor de fórmulas y el motor de almacenamiento, explore la documentación para desarrolladores de Analysis Services.
  • VertiPaq y VertiScan: en el modo de importación y en el modo Direct Lake, el motor de almacenamiento utiliza su motor VertiPaq para mantener un almacén columnar en memoria. VertiScan permite que el motor de fórmulas interactúe con VertiPaq. Para obtener más información, consulte la documentación para desarrolladores de Analysis Services.
  • Codificación de diccionario: tanto los archivos Parquet como VertiPaq usan la codificación de diccionario, que es una técnica de compresión de datos aplicada a columnas individuales de varios tipos de datos, como int, long, date y char. Funciona almacenando cada valor único de columna en la memoria como un entero mediante la codificación Run Length Encoding (RLE)/Bit-Packing codificación híbrida. VertiPaq siempre usa codificación de diccionario, pero Parquet podría cambiar a codificación sin formato o codificación diferencial en algunas circunstancias, como se explica en Codificaciones en la documentación de Parquet File-Format, lo que requeriría que Direct Lake vuelva a codificar los datos con el efecto correspondiente en el rendimiento de la transcodificación.
  • Fragmentos de columna y segmentos de columna: consulte la forma en que Parquet y VertiPaq almacenan los datos de columna para una recuperación de datos eficaz. Cada columna de una tabla se divide en fragmentos más pequeños que se pueden procesar y comprimir de forma independiente. VertiPaq llama a estos fragmentos segmentos. Puede usar el conjunto de filas de esquema DISCOVER_STORAGE_TABLE_COLUMN_SEGMENTS para recuperar información sobre los segmentos de columna en un modelo semántico de Direct Lake.
  • Python y Jupyter Notebooks: los cuadernos de Jupyter Notebook proporcionan un entorno interactivo para escribir y ejecutar código de Python. El conocimiento básico de Python es útil si desea seguir los fragmentos de código más adelante en este capítulo. Para obtener más información, consulte la referencia del lenguaje Python. Para obtener información sobre cómo usar cuadernos en Microsoft Fabric, consulte Uso de cuadernos: Microsoft Fabric.

Lo que afecta al rendimiento de las consultas de Direct Lake

En esta sección se resumen los principales factores que afectan al rendimiento de Direct Lake. Las secciones posteriores ofrecen explicaciones más detalladas:

  • Compresión de orden V: la eficacia de la compresión puede afectar al rendimiento de las consultas, ya que una mejor compresión conduce a una carga de datos más rápida y un procesamiento de consultas más eficaz. La carga de datos es rápida porque el streaming de datos comprimidos aumenta la eficacia de la transcodificación. El rendimiento de las consultas también es óptimo porque la compresión V-Order permite a VertiScan calcular los resultados directamente sobre los datos comprimidos, omitiendo el paso de descompresión.
  • Tipos de datos: el uso de tipos de datos adecuados para columnas puede mejorar la compresión y el rendimiento. Por ejemplo, use tipos de datos enteros en lugar de cadenas siempre que sea posible y evite almacenar enteros como cadenas.
  • Tamaño y recuento de segmentos: VertiPaq almacena los datos de columna en segmentos. Un gran número de segmentos más pequeños puede afectar negativamente al rendimiento de las consultas. En el caso de las tablas grandes, Direct Lake prefiere tamaños de segmento grandes, como entre 1 millón y 16 millones de filas.
  • Cardinalidad de columna: las columnas de cardinalidad alta (columnas con muchos valores únicos) pueden ralentizar el rendimiento. Reducir la cardinalidad siempre que sea posible puede ayudar.
  • Índices y agregaciones: las columnas con una cardinalidad menor se benefician de la codificación del diccionario, lo que puede acelerar las consultas reduciendo la cantidad de datos que se deben examinar.
  • Reversión de DirectQuery: las operaciones de reversión pueden dar lugar a un rendimiento de consulta más lento, ya que los datos ahora deben obtenerse desde el extremo de SQL Analytics del origen de datos de Fabric. Además, el recurso de respaldo se basa en planes de consulta híbridos para admitir tanto DirectQuery como VertiScan, con algunos compromisos en el rendimiento, incluso cuando Direct Lake no necesita recurrir al método alternativo. Si es posible, deshabilite la alternativa de DirectQuery para evitar planes de consultas híbridas.
  • Grado de residencia de memoria: los modelos semánticos de Direct Lake pueden estar en estado frío, templado, cálido o caliente, con un rendimiento mejorando progresivamente de frío a caliente. La transición rápida de frío a cálido es una clave para un buen rendimiento de Direct Lake.
    • Frío: el almacén de VertiPaq está vacío. Todos los datos necesarios para responder a una consulta DAX deben cargarse desde tablas Delta.
    • Semiwarm: Direct Lake solo elimina esos segmentos de columna durante el proceso de enmarcado que pertenecen a grupos de filas eliminados. Esto significa que solo se deben cargar los datos actualizados o recién agregados. Un modelo semántico de Direct Lake también puede entrar en estado semicálido bajo presión de memoria cuando debe descargar segmentos e índices de combinación.
    • Activado: los datos de las columnas necesarios para responder a una consulta DAX ya se encuentran completamente cargados en memoria.
    • Caliente: los datos de columna ya están totalmente cargados en la memoria, se rellenan las memorias caché de VertiScan y las consultas DAX alcanzan las memorias caché.
  • Presión de memoria: Direct Lake debe cargar todos los datos de columna necesarios para responder a una consulta DAX en la memoria, lo que puede agotar los recursos de memoria disponibles. Con memoria insuficiente, Direct Lake debe liberar los datos de columna cargados anteriormente, debido a que puede tener que volver a cargarlos para las consultas DAX posteriores. Dimensionar adecuadamente los modelos semánticos de Direct Lake puede ayudar a evitar la recarga frecuente.

Residencia de memoria y rendimiento de consultas

Direct Lake funciona mejor en estado cálido o caliente, mientras que los estados fríos producen un rendimiento más lento. Direct Lake evita el retroceso a un estado frío tanto como sea posible mediante el uso de encuadres incrementales.

Arranque

Después de cargar el modelo semántico inicial, todavía no hay datos de columna residentes en la memoria. Direct Lake está frío. Cuando un cliente envía una consulta DAX a un modelo semántico de Direct Lake en estado frío, Direct Lake debe realizar las siguientes tareas principales para que la consulta DAX se pueda procesar y responder:

  • Transcodificación del diccionario VertiPaq. Direct Lake debe fusionar los diccionarios locales de Parquet para cada fragmento de columna y crear así un diccionario global de VertiPaq para la columna. Esta operación de combinación afecta al tiempo de respuesta de la consulta.

  • Carga de bloques de columna Parquet en segmentos de columna. En la mayoría de los casos, se trata de una reasignación directa de identificadores de datos de Parquet a identificadores de VertiPaq cuando ambas partes pueden utilizar la codificación híbrida RLE/Bit-Packing. Si los diccionarios Parquet usan codificación sin formato, VertiPaq debe convertir los valores en codificación híbrida RLE/Bit-Packing, lo que lleva más tiempo.

    • El rendimiento de Direct Lake es óptimo en archivos Parquet con orden V, ya que el orden V aumenta la calidad de la compresión RLE. Direct Lake puede cargar datos V-Ordered estrechamente empaquetados más rápido que los datos menos comprimidos.
  • Generación de índices de combinación. Si la consulta DAX accede a columnas de varias tablas, Direct Lake debe compilar índices de combinación según las relaciones de tabla para que VertiScan pueda combinar correctamente las tablas. Para compilar los índices de combinación, Direct Lake debe cargar los diccionarios de las columnas de clave que participan en la relación y los segmentos de columna de la columna de clave principal (la columna en el lado uno de la relación de tabla).

  • Aplicación de vectores de eliminación delta. Si una tabla delta de origen usa vectores de eliminación, Direct Lake debe cargar estos vectores de eliminación para asegurarse de que los datos eliminados se excluyen del procesamiento de consultas.

    Nota

    El estado de frío también se puede inducir enviando un processClear seguido de un processFull comando XMLA al modelo. El ProcessClear comando quita todos los datos y la asociación con la versión de la tabla Delta enmarcada. El comando ProcessFull de XMLA establece el encuadre para enlazar el modelo a la versión más reciente disponible del compromiso Delta.

Estructuración incremental

Durante el encuadre, Direct Lake analiza el log de Delta de cada tabla Delta y quita los segmentos de columna cargados e índices de unión solo cuando los datos subyacentes han cambiado. Los diccionarios se conservan para evitar la transcodificación innecesaria y los nuevos valores se agregan simplemente a los diccionarios existentes. Este enfoque de estructuración incremental reduce la carga de recarga y beneficia el rendimiento de las consultas en frío.

Puede analizar la efectividad del encuadre incremental mediante la función DAX INFO.STORAGETABLECOLUMNSEGMENTS(), que encapsula el conjunto de filas del esquema DISCOVER_STORAGE_TABLE_COLUMN_SEGMENTS. Siga estos pasos para garantizar resultados significativos:

  1. Consulte el modelo semántico de Direct Lake para asegurarse de que está en estado cálido o caliente.
  2. Actualice la tabla Delta que desea investigar y actualice el modelo semántico de Direct Lake para efectuar el encuadramiento.
  3. Ejecute una consulta DAX para recuperar la información del segmento de columna mediante la INFO.STORAGETABLECOLUMNSEGMENTS() función , como en la captura de pantalla siguiente. En la captura de pantalla se usa una tabla de ejemplo pequeña con fines ilustrativos. Cada columna solo tiene un único segmento. Los segmentos no residen en la memoria. Esto indica un estado de frío verdadero. Si el modelo estaba cálido antes de enmarcar, esto significa que la tabla Delta se actualizó mediante un patrón de carga de datos destructivo, lo que hace imposible usar el marco incremental. Los patrones de actualización de tablas delta se tratan más adelante en este artículo.

Captura de pantalla que muestra el resultado de una consulta DAX mediante INFO.STORAGETABLECOLUMNSEGMENTS en un modelo semántico de Direct Lake, resaltando la ubicación de segmentos de columna.

Nota

Cuando una tabla Delta no recibe actualizaciones, no es necesario volver a cargar las columnas que ya residen en la memoria. Al usar patrones de actualización no destructivos, las consultas muestran un impacto mucho menor en el rendimiento después de la estructuración, ya que la estructuración incremental básicamente permite a Direct Lake actualizar directamente partes sustanciales de los datos existentes en memoria.

Residencia de memoria completa

Con diccionarios, segmentos de columna e índices de combinación cargados, Direct Lake alcanza el estado templado con un rendimiento de las consultas comparable al del modo de importación. En ambos modos, el número y el tamaño de los segmentos de columna desempeñan un papel fundamental en la optimización del rendimiento de las consultas.

Diferencias de tabla delta

Los archivos Parquet organizan los datos por columnas en lugar de filas. Direct Lake también organiza los datos por columnas. La alineación facilita la integración sin problemas, pero hay diferencias importantes, específicamente en relación con los grupos de filas y los diccionarios.

Grupos de filas frente a segmentos de columna

Un grupo de filas de un archivo Parquet consta de fragmentos de columna y cada fragmento contiene datos para una columna específica. Un segmento de columna en un modelo semántico, por otro lado, también contiene un fragmento de datos de columna.

Hay una relación directa entre el recuento total de grupos de filas de una tabla Delta y el recuento de segmentos de cada columna de la tabla de modelo semántica correspondiente. Por ejemplo, si una tabla Delta en todos sus archivos Parquet actuales tiene tres grupos de filas en total, la tabla del modelo semántico correspondiente tiene tres segmentos por columna, como se muestra en el diagrama siguiente. En otras palabras, si una tabla Delta tiene un gran número de grupos de filas diminutos, una tabla de modelo semántico correspondiente también tendría un gran número de segmentos de columna diminutos. Esto afectaría negativamente al rendimiento de las consultas.

Diagrama que muestra la relación entre los grupos de filas de tabla Delta y los segmentos de columna de modelo semántico en Direct Lake.

Nota

Dado que Direct Lake prefiere segmentos de columna grandes, los grupos de filas de las tablas delta de origen deberían ser idealmente grandes.

Diccionarios locales frente a diccionarios globales

El recuento total de grupos de filas de una tabla Delta también tiene efecto directo en el rendimiento de transcodificación de diccionarios porque los archivos Parquet usan diccionarios locales, mientras que los modelos semánticos de Direct Lake usan un diccionario global para cada columna, como se muestra en el diagrama siguiente. Cuanto mayor sea el número de grupos de filas, mayor será el número de diccionarios locales que Direct Lake debe combinar para crear un diccionario global y cuanto más tiempo tarde en completarse la transcodificación.

Diagrama que ilustra el proceso de combinar diccionarios locales de Parquet en un diccionario global para los modelos semánticos de Direct Lake.

Patrones de actualización de tablas delta

El método utilizado para ingerir datos en una tabla Delta puede influir considerablemente en la eficiencia de enmarcado incremental. Por ejemplo, usar la opción de Sobrescribir al cargar datos en una tabla existente elimina el registro Delta con cada carga. Esto significa que Direct Lake no puede usar el marco incremental y debe volver a cargar todos los datos, diccionarios e índices de combinación. Estos patrones de actualización destructivos afectan negativamente al rendimiento de las consultas.

Diagrama que muestra patrones de ingesta y actualización de datos para tablas Delta en Direct Lake.

En esta sección se tratan los patrones de actualización de Tablas Delta que permiten a Direct Lake usar marcos incrementales, conservando elementos de almacén de columnas VertiPaq, como diccionarios, segmentos de columna e índices de combinación, para maximizar la eficiencia de transcodificación y aumentar el rendimiento de las consultas en frío.

Procesamiento por lotes sin creación de particiones

Este patrón de actualización recopila y procesa datos en lotes grandes a intervalos programados, como semanal o mensualmente. A medida que llegan nuevos datos, los datos antiguos a menudo se eliminan mediante una ventana deslizante para mantener el tamaño de la tabla bajo control. Sin embargo, eliminar datos antiguos puede ser un desafío si los datos se distribuyen a través de la mayoría de los archivos Parquet. Por ejemplo, eliminar un día de 30 días podría tener un impacto en 95% de los archivos Parquet en lugar de 5%. En este caso, Direct Lake tendría que volver a cargar 95% de los datos incluso para una operación de eliminación relativamente pequeña. El mismo problema también se aplica a las actualizaciones de las filas existentes porque estas actualizaciones combinan eliminaciones y adiciones. Puede analizar el efecto de las operaciones de eliminación y actualización mediante Delta Analyzer, como se explica más adelante en este artículo.

Procesamiento por lotes con creación de particiones

La creación de particiones de tabla delta puede ayudar a reducir el efecto de las operaciones de eliminación , ya que la tabla se divide en archivos Parquet más pequeños almacenados en carpetas en función de los valores distintos de la columna de partición. Las columnas de partición usadas habitualmente incluyen las categorías de fecha, región u otras dimensiones. En el ejemplo anterior de quitar un día de 30 días, una tabla Delta particionada por fecha restringiría las eliminaciones solo a los archivos Parquet de la partición para ese día. Sin embargo, es importante tener en cuenta que la creación de particiones extensas podría dar lugar a un número considerablemente mayor de archivos y grupos de filas de Parquet, lo que provoca un aumento excesivo en los segmentos de columna dentro del modelo semántico de Direct Lake, lo que afecta negativamente al rendimiento de las consultas. Elegir una columna de partición de cardinalidad baja es fundamental para el rendimiento de las consultas. Como procedimiento recomendado, la columna debe tener menos de 100-200 valores distintos.

Carga incremental

Con la carga incremental, el proceso de actualización solo inserta datos nuevos en una tabla Delta sin afectar los archivos Parquet y los grupos de filas existentes. No hay eliminaciones. Direct Lake puede cargar los nuevos datos de forma incremental sin tener que descartar y volver a cargar los elementos de almacén de columnas VertiPaq existentes. Este método funciona bien con el marco incremental de Direct Lake. La creación de particiones de tabla delta no es necesaria.

Procesamiento de flujos

El procesamiento de datos casi en tiempo real, a medida que llegan, puede provocar una proliferación de pequeños archivos Parquet y grupos de filas, lo que puede afectar negativamente al rendimiento de Direct Lake. Al igual que con el patrón de carga incremental, no es necesario particionar la tabla Delta. Sin embargo, el mantenimiento frecuente de tablas es esencial para garantizar que el número de archivos y grupos de filas de Parquet permanezca dentro de los límites de protección especificados en el artículo de información general de Direct Lake. En otras palabras, no olvide ejecutar Spark Optimize con regularidad, como diariamente o incluso más a menudo. Spark Optimize se trata de nuevo en la sección siguiente.

Nota

El análisis real en tiempo real se implementa mejor mediante Eventstreams, bases de datos KQL y Eventhouse. Consulte la documentación deReal-Time Intelligence de Microsoft Fabric para obtener instrucciones.

Mantenimiento de tablas delta

Entre las tareas de mantenimiento clave se incluyen el vaciado y la optimización de tablas Delta. Para automatizar las operaciones de mantenimiento, puede usar las API de Lakehouse, como se explica en la documentación de administración de Lakehouse con la API rest de Microsoft Fabric .

Aspirar

El proceso de limpieza elimina los archivos Parquet que ya no están incluidos en la versión de commit Delta actual y que son anteriores a un umbral de retención establecido. Quitar estos archivos Parquet no afecta al rendimiento de Direct Lake porque Direct Lake solo carga los archivos Parquet que se encuentran en la versión de confirmación actual. Si ejecuta VACUUM diariamente con los valores predeterminados, se retienen las versiones de confirmación Delta de los últimos siete días para el desplazamiento temporal.

Importante

Dado que un modelo semántico de Direct Lake enmarcado hace referencia a una versión de confirmación Delta determinada, debe asegurarse de que la tabla Delta mantiene esta versión hasta que refresque (enmarque) el modelo de nuevo para moverlo a la versión actual. De lo contrario, los usuarios detectan errores de consulta cuando el modelo semántico de Direct Lake intenta acceder a los archivos Parquet que ya no están disponibles.

Optimización de Spark

La optimización de las Tablas Delta combina varios archivos Parquet pequeños en menos archivos grandes. Dado que esto puede afectar al rendimiento del modo frío de Direct Lake, es buena práctica optimizar con poca frecuencia, como los fines de semana o al final del mes. Optimice con más frecuencia si los archivos Parquet pequeños se acumulan rápidamente (actualizaciones pequeñas de alta frecuencia) para asegurarse de que la tabla Delta permanezca dentro de los límites de seguridad.

La creación de particiones puede ayudar a minimizar el efecto de optimización en el marco incremental porque la creación de particiones coloca eficazmente los datos. Por ejemplo, particionar una tabla Delta grande en función de una columna date_key de baja cardinalidad restringiría el mantenimiento semanal a un máximo de siete particiones. La tabla Delta conservaría la mayoría de los archivos Parquet existentes. Direct Lake solo tendría que volver a cargar siete días de datos.

Análisis de actualizaciones de tablas delta

Use Delta Analyzer o herramientas similares para estudiar cómo las modificaciones de las tablas Delta afectan a los archivos Parquet y a los grupos de filas. Delta Analyzer permite realizar un seguimiento de la evolución de los archivos Parquet, los grupos de filas, los fragmentos de columna y las columnas en respuesta a las operaciones de anexión, actualización y eliminación . Delta Analyzer está disponible como jupyter Notebook independiente. También está disponible en la biblioteca semantic-link-labs. En las secciones siguientes se usa semantic-link-labs. Esta biblioteca es fácil de instalar en un cuaderno mediante el %pip install semantic-link-labs comando .

Tamaño de grupo de filas

El tamaño ideal de grupo de filas para los modelos semánticos de Direct Lake es de entre 1 millón y 16 millones de filas, pero Fabric podría usar tamaños de grupo de filas más grandes para tablas grandes si los datos son comprimibles. Por lo general, no se recomienda cambiar el tamaño predeterminado del grupo de filas. Es mejor permitir que Fabric administre el diseño de la tabla Delta. Pero también es una buena idea comprobarlo.

El siguiente código de Python puede servir como punto de partida para analizar los tamaños del grupo de filas y otros detalles de una tabla Delta en una instancia de Lakehouse conectada a cuadernos de Fabric. En la tabla siguiente se muestra la salida de una tabla de ejemplo con 1 mil millones de filas.

import sempy_labs as labs
from IPython.display import HTML
from IPython.display import clear_output

table_name = "<Provide your table name>"

# Load the Delta table and run Delta Analyzer
df = spark.read.format("delta").load(f"Tables/{table_name}")
da_results = labs.delta_analyzer(table_name)

# Display the table summary in an HTML table.
clear_output()

df1 = da_results['Summary'].iloc[0]

html_table = "<table border='1'><tr><th>Column Name</th><th>{table_name}</th></tr>"
for column in da_results['Summary'].columns:
    html_table += f"<tr><td>{column}</td><td>{df1[column]}</td></tr>"
html_table += "</table>"

display(HTML(html_table))

Salida:

Parámetro Importancia
Nombre de la tabla sales_1
Recuento de filas 1000000000
Grupos de filas veinticuatro
Archivos de Parquet 8
Número máximo de filas por grupo de filas 51210000
Filas mínimas por grupo de filas 22580000
Promedio de filas por grupo de filas 41666666.666666664
VOrder habilitado Cierto
Tamaño total 7700808430
Timestamp 2025-03-24 03:01:02.794979

El resumen de Delta Analyzer muestra un tamaño medio de grupo de filas de aproximadamente 40 millones de filas. Esto es mayor que el tamaño máximo recomendado del grupo de filas de 16 millones de filas. Afortunadamente, el tamaño de grupo de filas mayor no causa problemas significativos para Direct Lake. Los grupos de filas más grandes facilitan los trabajos de segmento continuo con una sobrecarga mínima en el motor de almacenamiento. Por el contrario, los grupos de filas pequeños, los que están significativamente por debajo de 1 millón de filas, pueden causar problemas de rendimiento.

Lo más importante en el ejemplo anterior es que Fabric distribuyó los grupos de filas entre ocho archivos Parquet. Esto se alinea con el número de núcleos de la capacidad de Fabric para admitir operaciones de lectura paralelas eficaces. También es importante que los tamaños de grupo de filas individuales no se desvíen demasiado del promedio. Las grandes variaciones pueden provocar una carga VertiScan no uniforme, lo que da lugar a un rendimiento de consulta subóptimo.

Actualizaciones graduales de la ventana

Con fines ilustrativos, el siguiente ejemplo de código de Python simula una actualización gradual de la ventana. El código quita las filas con el DateID más antiguo de una tabla Delta de ejemplo. A continuación, actualiza el DateID de estas filas e los vuelve a insertar en la tabla de ejemplo como las filas más recientes.

from pyspark.sql.functions import lit

table_name = "<Provide your table name>"
table_df = spark.read.format("delta").load(f"Tables/{table_name}")

# Get the rows of the oldest DateID.
rows_df = table_df[table_df["DateID"] == 20200101]
rows_df = spark.createDataFrame(rows_df.rdd, schema=rows_df.schema)

# Delete these rows from the table
table_df.createOrReplaceTempView(f"{table_name}_view")
spark.sql(f"DELETE From {table_name}_view WHERE DateID = '20200101'")

# Update the DateID and append the rows as new data
rows_df = rows_df.withColumn("DateID", lit(20250101))
rows_df.write.format("delta").mode("append").save(f"Tables/{table_name}")

La get_delta_table_history función de la biblioteca semantic-link-labs puede ayudar a analizar el efecto de esta actualización gradual de la ventana. Consulte el siguiente ejemplo de código de Python. Consulte también la tabla con la salida después del fragmento de código.

import sempy_labs as labs
from IPython.display import HTML
from IPython.display import clear_output

table_name = "<Provide your table name>"
da_results = labs.get_delta_table_history(table_name)

# Create a single HTML table for specified columns
html_table = "<table border='1'>"
# Add data rows for specified columns
for index, row in da_results.iterrows():
    for column in ['Version', 'Operation', 'Operation Parameters', 'Operation Metrics']:
        if column == 'Version':
            html_table += f"<tr><td><b>Version</b></td><td><b>{row[column]}</b></td></tr>"
        else:
            html_table += f"<tr><td>{column}</td><td>{row[column]}</td></tr>"
html_table += "</table>"

# Display the HTML table
display(HTML(html_table))

Salida:

Versión Descripción Importancia
2 Operación ESCRITURA
Parámetros de operación {'mode': 'Append', 'partitionBy': '[]'}
Métricas de operación {'numFiles': '1', 'numOutputRows': '548665', 'numOutputBytes': '4103076'}
1 Operación ELIMINAR
Parámetros de operación {'predicate': '["(DateID#3910 = 20200101)"]'}
Métricas de operación {'numRemovedFiles': '8', 'numRemovedBytes': '7700855198', 'numCopiedRows': '999451335', 'numDeletionVectorsAdded': '0', 'numDeletionVectorsRemoved': '0', 'numAddedChangeFiles': '0', 'executionTimeMs': '123446', 'numDeletionVectorsUpdated': '0', 'numDeletedRows': '548665', 'scanTimeMs': '4820', 'numAddedFiles': '18', 'numAddedBytes': '7696900084', 'rewriteTimeMs': '198625'}
0 Operación ESCRITURA
Parámetros de operación {'mode': 'Overwrite', 'partitionBy': '[]'}
Métricas de operación {'numFiles': '8', 'numOutputRows': '10000000000', 'numOutputBytes': '7700892169'}

El historial de Delta Analyzer anterior muestra que esta tabla Delta ahora tiene las tres versiones siguientes:

  • Versión 0: esta es la versión original con ocho archivos Parquet y 24 grupos de filas como se describe en la sección anterior.
  • Versión 1: esta versión refleja la operación de eliminación . Aunque solo se quitó el conjunto de datos de un solo día (DateID = "20200101") de la tabla de ejemplo que contiene cinco años de transacciones de ventas, todos los ocho archivos Parquet se han visto afectados. En las métricas de operación, numRemovedFiles son ocho [archivos Parquet] y numAddedFiles son 18 [archivos Parquet]. Significa que la operación de eliminación reemplazó los ocho archivos Parquet originales por 18 nuevos archivos Parquet.
  • Versión 3: Las métricas de operación revelan que se agregó un archivo Parquet más con 548 665 filas a la tabla Delta.

Después de la actualización gradual de la ventana, la versión de confirmación delta más reciente incluye 19 archivos Parquet y 21 grupos de filas, con tamaños que van de 500 mil a 50 millones de filas. La actualización de ventana deslizante de 548.665 filas afectó a toda la tabla Delta de 1 mil millones de filas. Reemplazó todos los archivos Parquet y los grupos de filas. No se puede esperar que el marco incremental mejore el rendimiento en frío en este caso y es poco probable que la variación mayor en los tamaños de grupo de filas beneficie al rendimiento cálido.

Actualizaciones de tablas Delta

El siguiente código de Python actualiza una tabla Delta de la misma manera que se describe en la sección anterior. A simple vista, la función de actualización solo cambia el valor de DateID de las filas existentes que coinciden con un valor DateID determinado. Sin embargo, los archivos Parquet son inmutables y no se pueden modificar. Debajo de la superficie, la operación de actualización quita los archivos Parquet existentes y agrega nuevos archivos Parquet. El resultado y el efecto son los mismos que para la actualización gradual de la ventana.

from pyspark.sql.functions import col, when
from delta.tables import DeltaTable

# Load the Delta table
table_name = "<Provide your table name>"
delta_table = DeltaTable.forPath(spark, f"Tables/{table_name}")

# Define the condition and the column to update
condition = col("DateID") == 20200101
column_name = "DateID"
new_value = 20250101

# Update the DateID column based on the condition
delta_table.update(
    condition,
    {column_name: when(condition, new_value).otherwise(col(column_name))}
)

Actualizaciones de ventana deslizante con particiones

La creación de particiones puede ayudar a reducir el efecto de las actualizaciones de tabla. Puede resultar tentador usar las claves de fecha, pero una comprobación de cardinalidad rápida puede revelar que esta no es la mejor opción. Por ejemplo, la tabla de ejemplo que se describe hasta ahora contiene transacciones de ventas durante los últimos cinco años, equivalentes a unos 1800 valores de fecha distintos. Esta cardinalidad es demasiado alta. La columna de partición debe tener menos de 200 valores distintos.

column_name = 'DateID'
table_name = "<Provide your table name>"
table_df = spark.read.format("delta").load(f"Tables/{table_name}")

distinct_count = table_df.select(column_name).distinct().count()
print(f"The '{column_name}' column has {distinct_count} distinct values.")

if distinct_count <= 200:
    print(f"The '{column_name}' column is a good partitioning candidate.")
    table_df.write.format("delta").partitionBy(column_name).save(f"Tables/{table_name}_by_date_id")
    print(f"Table '{table_name}_by_date_id' partitioned and saved successfully.")
else:   
    print(f"The cardinality of the '{column_name}' column is possibly too high.")

Salida:

The 'DateID' column has 1825 distinct values.
The cardinality of the 'DateID' column is possibly too high.

Si no hay ninguna columna de partición adecuada, se puede crear artificialmente reduciendo la cardinalidad de una columna existente. El siguiente código de Python agrega una columna Month quitando los dos últimos dígitos de DateID. Esto genera 60 valores distintos. A continuación, el código de ejemplo guarda la tabla Delta particionada por la columna Mes.

from pyspark.sql.functions import col, expr

column_name = 'DateID'
table_name = "sales_1"
table_df = spark.read.format("delta").load(f"Tables/{table_name}")

partition_column = 'Month'
partitioned_table = f"{table_name}_by_month"
table_df = table_df.withColumn(partition_column, expr(f"int({column_name} / 100)"))

distinct_count = table_df.select(partition_column).distinct().count()
print(f"The '{partition_column}' column has {distinct_count} distinct values.")

if distinct_count <= 200:
    print(f"The '{partition_column}' column is a good partitioning candidate.")
    table_df.write.format("delta").partitionBy(partition_column).save(f"Tables/{partitioned_table}")
    print(f"Table '{partitioned_table}' partitioned and saved successfully.")
else:   
    print(f"The cardinality of the '{partition_column}' column is possibly too high.")

Salida:

The 'Month' column has 60 distinct values.
The 'Month' column is a good partitioning candidate.
Table 'sales_1_by_month' partitioned and saved successfully.

El resumen de Delta Analyzer muestra ahora que el diseño de la tabla Delta está bien alineado con Direct Lake. El tamaño medio del grupo de filas es de aproximadamente 16 millones de filas y la desviación absoluta media de los tamaños del grupo de filas y, por tanto, los tamaños de segmento son inferiores a 1 millón de filas.

Parámetro Importancia
Nombre de la tabla ventas_1_por_mes
Recuento de filas 1000000000
Grupos de filas 60
Archivos de Parquet 60
Número máximo de filas por grupo de filas 16997436
Filas mínimas por grupo de filas 15339311
Promedio de filas por grupo de filas 16666666.666666666
VOrder habilitado Cierto
Tamaño total 7447946016
Timestamp 2025-03-24 03:01:02.794979

Después de una actualización de ventana móvil en una tabla de ejemplo particionada, el historial de Delta Analyzer muestra que solo se ha afectado un archivo Parquet. Consulte la tabla de salida siguiente. La versión 2 tiene exactamente 16.445.655 filas copiadas del archivo parquet antiguo en un archivo Parquet de reemplazo y la versión 3 agrega un nuevo archivo Parquet con 548.665 filas. En resumen, Direct Lake solo necesita volver a cargar aproximadamente 17 millones de filas, una mejora considerable respecto a una recarga de mil millones de filas sin particiones.

Versión Descripción Importancia
2 Operación ESCRITURA
Parámetros de operación {'mode': 'Añadir', 'partitionBy': '["Mes"]'}
Métricas de operación {'numFiles': '1', 'numOutputRows': '548665', 'numOutputBytes': '4103076'}
1 Operación ELIMINAR
Parámetros de operación {'predicate': '["(DateID#3910 = 20200101)"]'}
Métricas de operación {'númeroArchivosEliminados': '1', 'númeroBytesEliminados': '126464179', 'númeroFilasCopiadas': '16445655', 'númeroVectoresEliminaciónAgregados': '0', 'númeroVectoresEliminaciónRemovidos': '0', 'númeroArchivosCambioAgregados': '0', 'tiempoEjecuciónMs': '19065', 'númeroVectoresEliminaciónActualizados': '0', 'númeroFilasEliminadas': '548665', 'tiempoEscaneoMs': '1926', 'númeroArchivosAgregados': '1', 'númeroBytesAgregados': '121275513', 'tiempoReescrituraMs': '17138'}
0 Operación ESCRITURA
Parámetros de operación {'mode': 'Overwrite', 'partitionBy': '["Month"]'}
Métricas de operación {'numFiles': '60', 'numOutputRows': '10000000000', 'numOutputBytes': '7447681467'}

Patrón de solo anexión seguido de Spark Optimize

Los patrones de solo adjunción no afectan a los archivos Parquet existentes. Funcionan bien con el marco incremental de Direct Lake. Sin embargo, no olvide optimizar las Tablas Delta para consolidar los archivos Parquet y los grupos de filas, como se explicó anteriormente en este artículo. Los anexos pequeños y frecuentes pueden acumular archivos rápidamente y pueden distorsionar la uniformidad de los tamaños del grupo de filas.

En la salida siguiente se muestra el historial de Delta Analyzer para una tabla sin particiones en comparación con una tabla con particiones. El historial incluye siete anexos y una operación de optimización posterior.

Versión Descripción Valor en el diseño predeterminado Valor en el diseño con particiones
8 Operación OPTIMIZAR OPTIMIZAR
Parámetros de operación {'predicate': '[]', 'auto': 'false', 'clusterBy': '[]', 'vorder': 'true', 'zOrderBy': '[]'} {'predicate': '["('Month >= 202501)"]', 'auto': 'false', 'clusterBy': '[]', 'vorder': 'true', 'zOrderBy': '[]'}
Métricas de operación {'numRemovedFiles': '8', 'numRemovedBytes': '991234561', 'p25FileSize': '990694179', 'numDeletionVectorsRemoved': '0', 'minFileSize': '990694179', 'numAddedFiles': '1', 'maxFileSize': '990694179', 'p75FileSize': '990694179', 'p50FileSize': '990694179', 'numAddedBytes': '990694179'} {'numRemovedFiles': '7', 'numRemovedBytes': '28658548', 'p25FileSize': '28308495', 'numDeletionVectorsRemoved': '0', 'minFileSize': '28308495', 'numAddedFiles': '1', 'maxFileSize': '28308495', 'p75FileSize': '28308495', 'p50FileSize': '28308495', 'numAddedBytes': '28308495'}
7 Operación ESCRITURA ESCRITURA
Parámetros de operación {'mode': 'Append', 'partitionBy': '[]'} {'mode': 'Append', 'partitionBy': '["Month"]'}
Métricas de operación {'numFiles': '1', 'numOutputRows': '547453', 'numOutputBytes': '4091802'} {'numFiles': '1', 'numOutputRows': '547453', 'numOutputBytes': '4091802'}
6 Operación ESCRITURA ESCRITURA
Parámetros de operación {'mode': 'Append', 'partitionBy': '[]'} {'modo': 'Añadir', 'particiónPor': '["Mes"]'}
Métricas de operación {'numFiles': '1', 'numOutputRows': '548176', 'numOutputBytes': '4095497'} {'numFiles': '1', 'numOutputRows': '548176', 'numOutputBytes': '4095497'}
5 Operación ESCRITURA ESCRITURA
Parámetros de operación {'mode': 'Append', 'partitionBy': '[]'} {'mode': 'Append', 'partitionBy': '["Month"]'}
Métricas de operación {'numFiles': '1', 'numOutputRows': '547952', 'numOutputBytes': '4090107'} {'numFiles': '1', 'numOutputRows': '547952', 'numOutputBytes': '4093015'}
4 Operación ESCRITURA ESCRITURA
Parámetros de operación {'mode': 'Append', 'partitionBy': '[]'} {'mode': 'Append', 'partitionBy': '["Month"]'}
Métricas de operación {'numFiles': '1', 'numOutputRows': '548631', 'numOutputBytes': '4093134'} {'numFiles': '1', 'numOutputRows': '548631', 'numOutputBytes': '4094376'}
3 Operación ESCRITURA ESCRITURA
Parámetros de operación {'mode': 'Append', 'partitionBy': '[]'} {'mode': 'Append', 'partitionBy': '["Month"]'}
Métricas de operación {'numFiles': '1', 'numOutputRows': '548671', 'numOutputBytes': '4101221'} {'numFiles': '1', 'numOutputRows': '548671', 'numOutputBytes': '4101221'}
2 Operación ESCRITURA ESCRITURA
Parámetros de operación {'mode': 'Append', 'partitionBy': '[]'} {'mode': 'Append', 'partitionBy': '["Month"]'}
Métricas de operación {'numFiles': '1', 'numOutputRows': '546530', 'numOutputBytes': '4081589'} {'numFiles': '1', 'numOutputRows': '546530', 'numOutputBytes': '4081589'}
1 Operación ESCRITURA ESCRITURA
Parámetros de operación {'mode': 'Append', 'partitionBy': '[]'} {'mode': 'Añadir', 'partitionBy': '["Mes"]'}
Métricas de operación {'numFiles': '1', 'numOutputRows': '548665', 'numOutputBytes': '4101048'} {'numFiles': '1', 'numOutputRows': '548665', 'numOutputBytes': '4101048'}
0 Operación ESCRITURA ESCRITURA
Parámetros de operación {'mode': 'Overwrite', 'partitionBy': '[]'} {'mode': 'Sobrescribir', 'partitionBy': '["Mes"]'}
Métricas de operación {'numFiles': '8', 'numOutputRows': '10000000000', 'numOutputBytes': '7700855198'} {'numFiles': '60', 'numOutputRows': '10000000000', 'numOutputBytes': '7447681467'}

Al examinar las métricas de operación de la versión 8, vale la pena señalar que la operación de optimización para la tabla no particionada combinada ocho archivos Parquet que afectan a aproximadamente 1 GB de datos mientras que la operación de optimización de la tabla con particiones combina siete archivos Parquet que afectan solo a unos 25 MB de datos. A continuación, Direct Lake funcionaría mejor con la tabla con particiones.

Consideraciones y limitaciones

Las consideraciones y limitaciones para optimizar el rendimiento de Direct Lake son las siguientes:

  • Evite patrones de actualización destructivos en tablas delta grandes para conservar el marco incremental en Direct Lake.
  • No es necesario optimizar las tablas delta pequeñas para el marco incremental.
  • Objetivo de un tamaño de grupo de filas de entre 1 millón y 16 millones de filas para crear segmentos de columna en Direct Lake con 1 millón a 16 millones de filas. Direct Lake prefiere segmentos de columna grandes.
  • Evite las columnas de partición de cardinalidad alta porque la transcodificación de Direct Lake es menos eficaz con muchos pequeños archivos Parquet y grupos de filas que con menos archivos Parquet grandes y grupos de filas.
  • Debido a la demanda imprevista de recursos de proceso y memoria, es posible que un modelo semántico se vuelva a cargar en otro nodo de clúster de Fabric en estado inactivo.
  • Direct Lake no utiliza estadísticas delta\Parquet para omitir grupos de filas y archivos con el fin de optimizar la carga de datos.