Cada registro de datos de streaming se puede combinar con un origen de datos externo de tabla de dimensiones. Realtime Compute for Apache Flink utiliza esta capacidad para enriquecer los flujos con datos de dimensión.
Descripción general del JOIN de tabla de dimensiones
Un JOIN de tabla de dimensiones (también conocido como Lookup Join) consulta una tabla de dimensiones externa durante el procesamiento de cada dato del flujo y enriquece el flujo principal con columnas de dimensión. Se usa comúnmente para agregar información de diccionario o de dimensión a los flujos de eventos.
Aspectos clave de un JOIN de tabla de dimensiones:
Sintaxis: Utilice
FOR SYSTEM_TIME AS OF PROCTIME()para indicar que cada fila del flujo principal consulta los datos actuales de la tabla de dimensiones en tiempo de procesamiento, no una instantánea.Estrategia de caché: Almacene en caché los datos de dimensión en la capa del conector para reducir la carga sobre el sistema externo.
Comportamiento de búsqueda: Use un hint LOOKUP para configurar el modo síncrono o asíncrono, la capacidad del búfer, la estrategia de reintentos, entre otros.
Estrategia de unión física: Emplee hints como SHUFFLE_HASH para controlar el comportamiento de redistribución (shuffle) y mitigar el sesgo de datos.
Configuración a nivel de trabajo: Utilice el comando SET para ajustar parámetros globales de la familia
table.exec.*.
Sintaxis del JOIN de tabla de dimensiones
SELECT column-names
FROM table1 [AS <alias1>]
[LEFT] JOIN table2 FOR SYSTEM_TIME AS OF PROCTIME() [AS <alias2>]
ON table1.column-name1 = table2.key-name1;
FOR SYSTEM_TIME AS OF PROCTIME()es obligatorio. Indica al motor que combine cada fila del flujo principal con la instantánea de la tabla de dimensiones correspondiente al tiempo de procesamiento actual.La cláusula ON debe contener una condición de igualdad en una columna que la tabla de dimensiones pueda consultar de forma aleatoria.
En la cláusula ON, puede aplicar funciones de conversión de tipos como CAST a las columnas de la tabla origen. Si los tipos de columna de la fuente y la dimensión no coinciden, convierta la columna de origen para que coincida con el tipo de la columna de dimensión.
Límites y consideraciones
El JOIN de tabla de dimensiones solo admite combinaciones con la instantánea actual de dicha tabla.
Las tablas de dimensiones solo son compatibles con INNER JOIN y LEFT JOIN. No se admiten RIGHT JOIN ni FULL JOIN.
Para una unión uno a uno, asegúrese de que la condición de combinación contenga un predicado de igualdad en una columna única dentro de la tabla de dimensiones.
Para cada dato de streaming, la unión solo considera la versión más reciente de la tabla de dimensiones en el momento del procesamiento. Una vez realizada la unión, cualquier cambio posterior (inserción, actualización o eliminación) en los datos de dimensión no se propaga a las filas ya combinadas. Para conocer el comportamiento específico de cada conector, consulte la documentación correspondiente de connector.
Estrategias de caché para tablas de dimensiones
La mayoría de los conectores admiten estrategias de caché para los JOIN de tablas de dimensiones. La compatibilidad varía ligeramente entre conectores; consulte la documentación del conector correspondiente para obtener detalles. Las estrategias de caché comunes son:
|
Estrategia |
Comportamiento |
|
None (predeterminado) |
Sin almacenamiento en caché. |
|
LRU |
Almacena en caché un subconjunto de la tabla de dimensiones. Cada fila del flujo de origen busca primero en la caché; si no encuentra el dato, el motor consulta la tabla de dimensiones física. |
|
ALL |
Almacena en caché toda la tabla de dimensiones. Antes de iniciar el trabajo, el motor carga todas las filas en la caché; cada búsqueda posterior se resuelve desde allí, y un fallo de caché significa que la clave no existe. Las opciones específicas del conector controlan las recargas periódicas o programadas de la tabla de dimensiones. Esta estrategia es adecuada para tablas de dimensiones pequeñas que no requieren actualizaciones frecuentes y exigen el máximo rendimiento. |
Elija entre frescura de datos y rendimiento según sus requisitos de negocio. Si la frescura es crítica, puede deshabilitar la caché y leer directamente desde la tabla de dimensiones.
Cuando la caché está habilitada, combine LRU con un TTL para mantener los datos razonablemente actualizados. El TTL puede establecerse en un valor corto (por ejemplo, de varios segundos a decenas de segundos) para que la caché se actualice periódicamente desde el origen.
Al usar la estrategia ALL, supervise cuidadosamente la memoria del nodo para evitar errores OOM.
Con la estrategia ALL, el motor carga los datos de dimensión de forma asíncrona, por lo que debe aumentar la memoria del nodo de JOIN de dimensión. Como regla general, increméntela al doble del tamaño de los datos de la tabla de dimensiones remota.
Ajuste de un JOIN de tabla de dimensiones
Categorías de parámetros de ajuste y cómo pasarlos
El ajuste de un JOIN de tabla de dimensiones implica tres categorías de parámetros. Cada categoría tiene su propio mecanismo de transmisión, y usar el mecanismo incorrecto provoca que el hint se ignore silenciosamente.
|
Categoría |
Parámetros típicos |
Cómo pasarlos |
Alcance |
|
Opciones WITH del conector |
|
Establézcalos en la cláusula |
Tabla individual |
|
Opciones del hint LOOKUP |
|
|
Operación de unión individual |
|
TableConfig a nivel de trabajo de Flink |
Configuraciones globales cuyas claves comienzan con |
Ejecute |
Todo el trabajo |
Un hint
OPTIONS()solo anula las opciones que el conector acepta en la cláusula WITH. No acepta claves TableConfigtable.exec.*; las claves no válidas se ignoran silenciosamente.Un hint
LOOKUP()solo reconoce las claves fijas listadas en la tabla anterior. No acepta claves de configuración completas de Flink. Por ejemplo, para ajustar el búfer de búsqueda asíncrona debe escribir'capacity'='100'; escribir'table.exec.async-lookup.buffer-capacity'='100'no surte efecto.Para cambiar un parámetro global que no está expuesto mediante ningún hint, use
SET 'xxx' = 'yyy';.
Anular opciones del conector con el hint OPTIONS
Un hint OPTIONS() le permite anular las opciones WITH de una tabla directamente en una consulta, sin modificar la sentencia CREATE TABLE. Se usa comúnmente para ajustar el comportamiento de la caché.
SELECT t.id, t.name, w.phoneNumber
FROM kafka_input AS t
LEFT JOIN phoneNumber /*+ OPTIONS(
'lookup.cache' = 'PARTIAL',
'lookup.partial-cache.max-rows' = '1000'
) */ FOR SYSTEM_TIME AS OF PROCTIME() AS w
ON t.name = w.name;
Cada clave dentro de OPTIONS debe ser una opción WITH que el conector realmente admita.
Las opciones lookup.cache y lookup.partial-cache.max-rows mostradas arriba pertenecen a la interfaz genérica LookupCache de Flink (FLIP-221), y solo aplican a conectores más recientes que implementan esta interfaz (como Fluss y JDBC). Las estrategias None/LRU/ALL descritas en la sección "Estrategias de caché para tablas de dimensiones" corresponden a conectores heredados, los cuales exponen su comportamiento de caché mediante diferentes opciones WITH. Consulte la documentación del connector de destino para confirmar qué opciones y estrategias de caché admite, y evite usar parámetros que el conector no reconozca.
Configurar el comportamiento de búsqueda con el hint LOOKUP
El hint LOOKUP se comporta de manera consistente con la versión de la comunidad de Apache Flink. Configura el modo síncrono/asíncrono, los reintentos y el comportamiento de redistribución (shuffle) para una operación de unión individual. Para más detalles, consulte Apache Flink Lookup Hint.
El hint LOOKUP está disponible solo en VVR 8.0 y versiones posteriores.
La opción
'shuffle' = 'true'está disponible solo en VVR 8.0.8 y versiones posteriores.Los alias son compatibles en VVR 8.0 y versiones posteriores. Cuando la tabla de dimensiones se referencia mediante un alias, el hint debe usar ese alias.
Opciones compatibles
|
Opción |
Significado |
Valor |
|
|
Nombre o alias de la tabla de dimensiones a la que aplica el hint. |
Cadena de nombre de tabla o alias. |
|
|
Indica si se habilita la búsqueda asíncrona. |
|
|
|
Orden de salida de las búsquedas asíncronas. |
|
|
|
Capacidad de la cola de búfer para búsquedas asíncronas. |
Entero. |
|
|
Tiempo de espera para una búsqueda asíncrona. |
Duración (por ejemplo, |
|
|
Condición que desencadena un reintento. |
Actualmente solo se admite |
|
|
Estrategia de reintento. |
Actualmente solo se admite |
|
|
Intervalo fijo entre reintentos. |
Duración (por ejemplo, |
|
|
Número máximo de intentos de reintento. |
Entero. |
|
|
Indica si se realiza una redistribución antes de la unión con la tabla de dimensiones. |
|
Comportamiento de la opción shuffle
La opción shuffle afecta la estrategia de redistribución de la unión de tabla de dimensiones. El comportamiento efectivo en cada escenario es el siguiente.
|
Escenario |
Estrategia de unión |
|
No se establece |
Se utiliza la estrategia de redistribución predeterminada del motor. |
|
No se establece |
Se utiliza la estrategia de redistribución predeterminada del motor. |
|
Se establece |
Se usa SHUFFLE_HASH de forma predeterminada. Para más detalles, consulte la sección SHUFFLE_HASH a continuación. |
|
Se establece |
Se utiliza la estrategia de redistribución personalizada proporcionada por el conector. |
Actualmente, solo el conector Streaming Lakehouse Paimon proporciona una estrategia de redistribución personalizada. Cuando las columnas de unión cubren todas las columnas de bucket, la redistribución se basa en el bucket.
Ejemplos de código
-- Apply the shuffle strategy only to dimension table dim1.
SELECT /*+ LOOKUP('table'='dim1', 'shuffle' = 'true') */ ...
FROM src AS T
LEFT JOIN dim1 FOR SYSTEM_TIME AS OF PROCTIME() ON T.a = dim1.a
LEFT JOIN dim2 FOR SYSTEM_TIME AS OF PROCTIME() ON T.b = dim2.b;
-- Apply the shuffle strategy to both dim1 and dim2.
SELECT /*+ LOOKUP('table'='dim1', 'shuffle' = 'true'), LOOKUP('table'='dim2', 'shuffle' = 'true') */ ...
FROM src AS T
LEFT JOIN dim1 FOR SYSTEM_TIME AS OF PROCTIME() ON T.a = dim1.a
LEFT JOIN dim2 FOR SYSTEM_TIME AS OF PROCTIME() ON T.b = dim2.b;
-- When dim1 is referenced by the alias D1, the hint must use the alias.
SELECT /*+ LOOKUP('table'='D1', 'shuffle' = 'true') */ ...
FROM src AS T
LEFT JOIN dim1 FOR SYSTEM_TIME AS OF PROCTIME() AS D1 ON T.a = D1.a
LEFT JOIN dim2 FOR SYSTEM_TIME AS OF PROCTIME() AS D2 ON T.b = D2.b;
-- Apply the shuffle strategy to both dim1 and dim2 using their aliases.
SELECT /*+ LOOKUP('table'='D1', 'shuffle' = 'true'), LOOKUP('table'='D2', 'shuffle' = 'true') */ ...
FROM src AS T
LEFT JOIN dim1 FOR SYSTEM_TIME AS OF PROCTIME() AS D1 ON T.a = D1.a
LEFT JOIN dim2 FOR SYSTEM_TIME AS OF PROCTIME() AS D2 ON T.b = D2.b;
Establecer TableConfig a nivel de trabajo con SET
Las claves table.exec.* y similares de TableConfig son configuraciones globales a nivel de trabajo. Deben pasarse mediante SET; ni el hint LOOKUP ni el hint OPTIONS las aceptan.
Claves comunes de TableConfig para JOIN de tabla de dimensiones:
|
Clave |
Significado |
Predeterminado |
|
|
Capacidad de la cola de búfer para búsquedas asíncronas (valor predeterminado a nivel de trabajo). |
100 |
|
|
Tiempo de espera para búsquedas asíncronas (valor predeterminado a nivel de trabajo). |
3 min |
|
|
Orden de salida de las búsquedas asíncronas (valor predeterminado a nivel de trabajo). |
|
-- Set job-level defaults at the beginning of the job.
SET 'table.exec.async-lookup.buffer-capacity' = '3072';
SET 'table.exec.async-lookup.timeout' = '180s';
INSERT INTO sink_table
SELECT ...
FROM src_table
LEFT JOIN dim_table FOR SYSTEM_TIME AS OF PROCTIME() AS d
ON src_table.key = d.key;
Cuando SET y un hint LOOKUP establecen el mismo parámetro, el hint LOOKUP en la unión individual tiene prioridad.
Controlar la redistribución con hints de estrategia de unión
Los hints de estrategia de unión controlan el comportamiento de redistribución para las uniones de tablas de dimensiones. Los hints disponibles son SHUFFLE_HASH, REPLICATED_SHUFFLE_HASH y SKEW. La siguiente tabla muestra qué estrategia de unión se ajusta a cada estrategia de caché.
|
Estrategia de caché |
SHUFFLE_HASH |
REPLICATED_SHUFFLE_HASH (equivalente a SKEW) |
|
None |
No se recomienda esta estrategia; el flujo principal incurre en sobrecarga de red adicional. |
No se recomienda esta estrategia; el flujo principal incurre en sobrecarga de red adicional. |
|
LRU |
Considere esta estrategia cuando la E/S de búsqueda de dimensiones sea el cuello de botella. Si el flujo principal presenta localidad temporal en la clave de unión, mejora la tasa de aciertos de caché, reduce las solicitudes de E/S y aumenta el rendimiento general. Importante
El flujo principal incurre en sobrecarga de red adicional. Si el flujo principal tiene sesgo en la clave de unión y encuentra un cuello de botella de rendimiento, considere usar REPLICATED_SHUFFLE_HASH en su lugar. |
Considere esta estrategia cuando la E/S de búsqueda de dimensiones sea el cuello de botella y el flujo principal tenga sesgo en la clave de unión. Si el flujo principal presenta localidad temporal en la clave de unión, mejora la tasa de aciertos de caché, reduce las solicitudes de E/S y aumenta el rendimiento general. |
|
ALL |
Recomendada cuando la memoria de la tabla de dimensiones se convierte en el cuello de botella. El uso de memoria puede reducirse a 1 / paralelismo. Importante
El flujo principal incurre en sobrecarga de red adicional. Si el flujo principal tiene sesgo en la clave de unión y encuentra un cuello de botella de rendimiento, considere usar REPLICATED_SHUFFLE_HASH en su lugar. |
Recomendada cuando la memoria de la tabla de dimensiones se convierte en el cuello de botella y el flujo principal tiene sesgo en la clave de unión. El uso de memoria se reduce a cantidad de buckets / paralelismo. |
La opción
shuffledel hint LOOKUP ya cubre lo que hace SHUFFLE_HASH. Cuando ambos están presentes, prevalece la opciónshuffledel hint LOOKUP.La opción
shuffledel hint LOOKUP aún no maneja el sesgo de datos. Cuando se combina con REPLICATED_SHUFFLE_HASH o SKEW, la estrategia especificada por REPLICATED_SHUFFLE_HASH o SKEW tiene prioridad.
SHUFFLE_HASH
Efecto: Usar una estrategia shuffle-hash en un JOIN de tabla de dimensiones redistribuye el flujo principal por clave de unión antes de la unión. Combinada con la estrategia de caché LRU, aumenta la tasa de aciertos de caché y reduce las solicitudes de E/S; combinada con la estrategia de caché ALL, reduce el uso de memoria. Un solo hint SHUFFLE_HASH puede especificar varias tablas de dimensiones.
Límites: SHUFFLE_HASH reduce el uso de memoria, pero los datos ascendentes deben redistribuirse por clave de unión, lo que introduce sobrecarga de red adicional. Evítelo en los siguientes dos casos:
El flujo principal tiene un sesgo significativo en la clave de unión. Usar SHUFFLE_HASH convertiría el nodo de unión en un cuello de botella, causando contrapresión severa en trabajos de streaming o colas largas en trabajos por lotes. Use REPLICATED_SHUFFLE_HASH en su lugar.
La tabla de dimensiones es pequeña y la estrategia de caché ALL no presenta cuellos de botella de memoria. En este caso, la memoria ahorrada por SHUFFLE_HASH no justifica la sobrecarga de red adicional.
Ejemplo de código:
-- Enable SHUFFLE_HASH for dim1 only.
SELECT /*+ SHUFFLE_HASH(dim1) */ ...
-- Enable SHUFFLE_HASH for both dim1 and dim2.
SELECT /*+ SHUFFLE_HASH(dim1, dim2) */ ...
-- When dim1 is referenced by the alias D1, the hint must use the alias.
SELECT /*+ SHUFFLE_HASH(D1) */ ...
REPLICATED_SHUFFLE_HASH
Efecto: REPLICATED_SHUFFLE_HASH se comporta esencialmente igual que SHUFFLE_HASH, excepto que distribuye aleatoriamente las filas del flujo principal con la misma clave entre N instancias paralelas. Esto aborda el cuello de botella de rendimiento causado por el sesgo de datos. Un solo hint REPLICATED_SHUFFLE_HASH puede especificar varias tablas de dimensiones.
Límites:
Configure la cantidad de buckets para datos sesgados con
table.exec.skew-join.replicate-num(predeterminado: 16). Su valor no puede exceder el paralelismo del nodo de unión de dimensiones. Para más detalles, consulte Ajuste SQL a nivel de trabajo.No se admiten flujos de actualización. Usar este hint cuando el flujo principal es un flujo de actualización genera un error.
Ejemplo de código:
SELECT /*+ REPLICATED_SHUFFLE_HASH(dim1) */ ...
SKEW
Efecto: Cuando se sabe que la tabla especificada tiene sesgo, el optimizador aplica la estrategia replicated-shuffle-hash. SKEW es azúcar sintáctica; internamente se implementa con redistributed shuffle hash.
Límites:
Cada hint SKEW solo puede especificar una tabla.
El nombre de la tabla debe referirse a la tabla principal que tiene el sesgo, no a la tabla de dimensiones.
No se admiten flujos de actualización. Usar este hint cuando el flujo principal es un flujo de actualización genera un error.
Ejemplo de código:
SELECT /*+ SKEW(src) */ ...
Ejemplos
Ejemplo 1: Un JOIN básico de tabla de dimensiones
La forma más básica: enriquecer un flujo de Kafka con una tabla de dimensiones de MySQL. No se utilizan hints de ajuste.
CREATE TEMPORARY TABLE kafka_input (
id BIGINT,
name VARCHAR,
age BIGINT
) WITH (
'connector' = 'kafka',
'topic' = '<yourTopic>',
'properties.bootstrap.servers' = '<yourKafkaBrokers>',
'properties.group.id' = '<yourKafkaConsumerGroupId>',
'format' = 'csv'
);
CREATE TEMPORARY TABLE phoneNumber (
name VARCHAR,
phoneNumber BIGINT,
PRIMARY KEY (name) NOT ENFORCED
) WITH (
'connector' = 'mysql',
'hostname' = '<yourHostname>',
'port' = '3306',
'username' = '<yourUsername>',
'password' = '<yourPassword>',
'database-name' = '<yourDatabaseName>',
'table-name' = '<yourTableName>'
);
CREATE TEMPORARY TABLE result_infor (
id BIGINT,
phoneNumber BIGINT,
name VARCHAR
) WITH (
'connector' = 'blackhole'
);
INSERT INTO result_infor
SELECT
t.id,
w.phoneNumber,
t.name
FROM kafka_input AS t
JOIN phoneNumber FOR SYSTEM_TIME AS OF PROCTIME() AS w
ON t.name = w.name;
Ejemplo 2: Habilitar caché con el hint OPTIONS y configurar búsqueda asíncrona con el hint LOOKUP
Escenario de ajuste: el QPS de la tabla de dimensiones es insuficiente. Desea activar la caché parcial en la tabla de dimensiones y habilitar la búsqueda asíncrona con una cola de búfer más grande.
INSERT INTO user_behavior_wide
SELECT /*+ LOOKUP('table' = 't2', 'async' = 'true', 'capacity' = '3072') */
t1.member_id AS member_id,
t2.tag AS tag
FROM user_behavior_datagen AS t1
LEFT JOIN fluss.fluss.user_active_info /*+ OPTIONS(
'lookup.cache' = 'PARTIAL',
'lookup.partial-cache.max-rows' = '1000'
) */
FOR SYSTEM_TIME AS OF PROCTIME() AS t2
ON t1.member_id = t2.member_id;
Las opciones del conector (
lookup.cache,lookup.partial-cache.max-rows) se pasan mediante un hintOPTIONS()colocado justo después de la referencia a la tabla de dimensiones; esto anula las opciones WITH correspondientes en tiempo de consulta.El comportamiento de búsqueda (
async,capacity) se configura mediante un hintLOOKUP()colocado después de SELECT. OPTIONS y LOOKUP son dos tipos de hints diferentes y deben escribirse por separado; combinarlos desactiva silenciosamente el hint.
Ejemplo 3: Ajustar el búfer de búsqueda asíncrona a nivel de trabajo con SET
Escenario de ajuste: varios JOIN de tablas de dimensiones en el mismo trabajo necesitan un búfer de búsqueda asíncrona más grande. Desea que el cambio se aplique globalmente y habilitar el modo asíncrono mediante el hint LOOKUP.
SET 'table.exec.async-lookup.buffer-capacity' = '3072';
SET 'table.exec.async-lookup.timeout' = '180s';
INSERT INTO user_behavior_wide
SELECT /*+ LOOKUP('table' = 't2', 'async' = 'true') */
t1.member_id,
t2.tag
FROM user_behavior_datagen AS t1
LEFT JOIN fluss.fluss.user_active_info
FOR SYSTEM_TIME AS OF PROCTIME() AS t2
ON t1.member_id = t2.member_id;
table.exec.async-lookup.buffer-capacityy parámetros similares son claves TableConfig a nivel de trabajo y solo pueden establecerse con SET. Incluirlos dentro de OPTIONS o LOOKUP no surte efecto.Cuando el hint LOOKUP no especifica
capacity, se utiliza el valor global de SET. Si ambos se especifican, el hint LOOKUP tiene prioridad en esa unión individual.