Tous les produits
Search
Centre de documentation

Realtime Compute for Apache Flink:Instructions JOIN pour les tables de dimension

Dernière mise à jour :Aug 09, 2026

Vous pouvez joindre chaque élément de données en continu à une source de données externe de type table de dimension. Realtime Compute for Apache Flink utilise cette fonctionnalité pour enrichir les flux avec des données de dimension.

Vue d'ensemble de la jointure de table de dimension

Une jointure de table de dimension (également appelée Lookup Join) interroge une table de dimension externe au moment du traitement pour chaque élément de données du flux, et enrichit le flux principal avec les colonnes de dimension. Elle est couramment utilisée pour ajouter des informations de dictionnaire ou de dimension aux flux d'événements.

Points clés d'une jointure de table de dimension :

  • Syntaxe : Utilisez FOR SYSTEM_TIME AS OF PROCTIME() pour indiquer que chaque ligne du flux principal interroge les données actuelles de la table de dimension au moment du traitement, et non un instantané.

  • Stratégie de cache : Mettez en cache les données de dimension au niveau du connecteur afin de réduire la pression exercée sur le système externe.

  • Comportement de recherche : Utilisez un indice LOOKUP pour configurer le mode synchrone/asynchrone, la capacité du tampon, la stratégie de nouvelle tentative, etc.

  • Stratégie de jointure physique : Utilisez des indices tels que SHUFFLE_HASH pour contrôler le comportement de brassage et atténuer le déséquilibre des données.

  • Configuration au niveau du job : Utilisez la commande SET pour ajuster les paramètres globaux de la famille table.exec.*.

Syntaxe de la jointure de table de dimension

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;
Remarque
  • FOR SYSTEM_TIME AS OF PROCTIME() est obligatoire. Cette clause indique au moteur de joindre chaque ligne du flux principal à l'instantané de la table de dimension tel qu'il existe au moment actuel du traitement.

  • La clause ON doit contenir une condition d'égalité sur une colonne que la table de dimension peut rechercher de manière aléatoire.

  • Dans la clause ON, vous pouvez appliquer des fonctions de conversion de type telles que CAST aux colonnes de la table source. Lorsque les types des colonnes source et de dimension ne correspondent pas, convertissez la colonne source pour qu'elle s'aligne sur le type de la colonne de dimension.

Limites et considérations

  • Une jointure de table de dimension prend uniquement en charge la jointure avec l'instantané actuel de la table de dimension.

  • Les tables de dimension prennent en charge uniquement INNER JOIN et LEFT JOIN. RIGHT JOIN et FULL JOIN ne sont pas pris en charge.

  • Pour une jointure un-à-un, assurez-vous que la condition de jointure contient un prédicat d'égalité sur une colonne unique dans la table de dimension.

  • Pour chaque élément de données en continu, la jointure ne voit que la dernière version de la table de dimension au moment du traitement. Une fois la jointure effectuée, les modifications ultérieures (insertion, mise à jour ou suppression) apportées aux données de dimension ne sont pas propagées aux lignes déjà jointes. Pour connaître le comportement spécifique à chaque connecteur, consultez la documentation correspondante du connecteur.

Stratégies de cache des tables de dimension

La plupart des connecteurs prennent en charge des stratégies de cache pour les jointures de tables de dimension. Le support varie légèrement selon les connecteurs ; reportez-vous à la documentation du connecteur correspondant pour plus de détails. Les stratégies de cache courantes sont les suivantes :

Stratégie

Comportement

None (par défaut)

Aucun cache.

LRU

Met en cache un sous-ensemble de la table de dimension. Chaque ligne du flux source consulte d'abord le cache ; en cas d'échec, le moteur interroge la table de dimension physique.

ALL

Met en cache l'intégralité de la table de dimension. Avant le démarrage du job, le moteur charge toutes les lignes dans le cache ; chaque recherche ultérieure est servie depuis le cache, et un échec de cache signifie que la clé n'existe pas. Des options spécifiques au connecteur contrôlent les rechargements périodiques ou planifiés de la table de dimension. Cette stratégie convient aux petites tables de dimension qui ne nécessitent pas de mises à jour fréquentes et exigent un débit maximal.

Remarque
  • Choisissez entre la fraîcheur des données et les performances en fonction de vos exigences métier. Si la fraîcheur des données est critique, vous pouvez désactiver la mise en cache et lire directement depuis la table de dimension.

  • Lorsque la mise en cache est activée, combinez LRU avec un TTL (Time To Live) pour maintenir les données mises en cache raisonnablement fraîches. Le TTL peut être défini sur une valeur courte, par exemple de quelques secondes à quelques dizaines de secondes, afin que le cache soit régulièrement actualisé depuis la source.

  • Lorsque vous utilisez la stratégie ALL, surveillez attentivement la mémoire du nœud pour éviter les erreurs OOM (Out Of Memory).

  • Avec la stratégie ALL, le moteur charge les données de dimension de manière asynchrone ; vous devez donc augmenter la mémoire du nœud de jointure de dimension. En règle générale, augmentez-la du double de la taille des données de la table de dimension distante.

Optimisation d'une jointure de table de dimension

Catégories de paramètres d'optimisation et méthode de transmission

L'optimisation d'une jointure de table de dimension implique trois catégories de paramètres. Chaque catégorie possède sa propre méthode de transmission, et l'utilisation du mécanisme incorrect entraîne l'ignorance silencieuse de l'indice.

Catégorie

Paramètres typiques

Méthode de transmission

Portée

Options WITH du connecteur

lookup.cache, lookup.partial-cache.max-rows, lookup.partial-cache.expire-after-write

Définissez-les dans la clause WITH (...) lors de la création de la table, ou remplacez-les au moment de la requête avec /*+ OPTIONS('key'='value') */.

Table unique

Options de l'indice LOOKUP

table, async, capacity, timeout, output-mode, retry-predicate, retry-strategy, fixed-delay, max-attempts, shuffle

/*+ LOOKUP('table'='dim', 'async'='true', 'capacity'='100') */

Opération de jointure unique

TableConfig au niveau du job Flink

Configurations globales dont les clés commencent par table.exec.* ou table.optimizer.*

Exécutez SET 'table.exec.async-lookup.buffer-capacity' = '3072'; au début du job.

Job entier

Important
  • Un indice OPTIONS() remplace uniquement les options que le connecteur accepte dans la clause WITH. Il n'accepte pas les clés TableConfig table.exec.* ; les clés invalides sont ignorées silencieusement.

  • Un indice LOOKUP() reconnaît uniquement les clés fixes répertoriées dans le tableau ci-dessus. Il n'accepte pas les clés de configuration Flink complètes. Par exemple, pour ajuster le tampon de recherche asynchrone, vous devez écrire 'capacity'='100' ; écrire 'table.exec.async-lookup.buffer-capacity'='100' n'a aucun effet.

  • Pour modifier un paramètre global qui n'est exposé par aucun indice, utilisez SET 'xxx' = 'yyy';.

Remplacer les options du connecteur avec l'indice OPTIONS

Un indice OPTIONS() vous permet de remplacer les options WITH d'une table directement dans une requête, sans modifier l'instruction CREATE TABLE. Il est couramment utilisé pour optimiser le comportement du cache.

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;

Chaque clé à l'intérieur de OPTIONS doit être une option WITH effectivement prise en charge par le connecteur.

Remarque

Les options lookup.cache et lookup.partial-cache.max-rows illustrées ci-dessus appartiennent à l'interface générique LookupCache de Flink (FLIP-221) et s'appliquent uniquement aux connecteurs plus récents qui implémentent cette interface (tels que Fluss et JDBC). Les stratégies None/LRU/ALL décrites dans la section « Stratégies de cache des tables de dimension » correspondent aux connecteurs hérités, qui exposent leur comportement de cache via différentes options WITH. Reportez-vous à la documentation du connecteur cible pour confirmer les options et stratégies de cache qu'il prend en charge, et évitez d'utiliser des paramètres non reconnus par le connecteur.

Configurer le comportement de recherche avec l'indice LOOKUP

L'indice LOOKUP se comporte de manière cohérente avec la version communautaire d'Apache Flink. Il configure le mode synchrone/asynchrone, les nouvelles tentatives et le comportement de brassage pour une opération de jointure unique. Pour plus de détails, consultez la page Apache Flink Lookup Hint.

  • L'indice LOOKUP est disponible uniquement dans VVR 8.0 et versions ultérieures.

  • L'option 'shuffle' = 'true' est disponible uniquement dans VVR 8.0.8 et versions ultérieures.

  • Les alias sont pris en charge dans VVR 8.0 et versions ultérieures. Lorsque la table de dimension est référencée par un alias, l'indice doit utiliser cet alias.

Options prises en charge

Option

Signification

Valeur

table

Nom ou alias de la table de dimension auquel l'indice s'applique.

Nom de table ou chaîne d'alias.

async

Indique s'il faut activer la recherche asynchrone.

true / false

output-mode

Ordre de sortie des recherches asynchrones.

ordered / allow_unordered

capacity

Capacité de la file d'attente du tampon pour les recherches asynchrones.

Entier.

timeout

Délai d'expiration pour une recherche asynchrone.

Durée (par exemple, 180s).

retry-predicate

Condition qui déclenche une nouvelle tentative.

Seule l'option lookup_miss est actuellement prise en charge.

retry-strategy

Stratégie de nouvelle tentative.

Seule l'option fixed_delay est actuellement prise en charge.

fixed-delay

Intervalle fixe entre les nouvelles tentatives.

Durée (par exemple, 10s).

max-attempts

Nombre maximal de tentatives de nouvelle tentative.

Entier.

shuffle

Indique s'il faut effectuer un brassage avant la jointure de la table de dimension.

true / false

Comportement de l'option shuffle

L'option shuffle affecte la stratégie de brassage de la jointure de la table de dimension. Le comportement effectif dans chaque scénario est le suivant.

Scénario

Stratégie de jointure

'shuffle' = 'true' n'est pas défini.

La stratégie de brassage par défaut du moteur est utilisée.

'shuffle' = 'true' n'est pas défini et le connecteur de dimension ne fournit pas de stratégie de jointure personnalisée.

La stratégie de brassage par défaut du moteur est utilisée.

'shuffle' = 'true' est défini et le connecteur de dimension ne fournit pas de stratégie de jointure personnalisée.

SHUFFLE_HASH est utilisé par défaut. Pour plus de détails, consultez la section SHUFFLE_HASH ci-dessous.

'shuffle' = 'true' est défini et le connecteur de dimension fournit une stratégie de jointure personnalisée.

La stratégie de brassage personnalisée fournie par le connecteur est utilisée.

Remarque

Actuellement, seul le connecteur Streaming Lakehouse Paimon fournit une stratégie de brassage personnalisée. Lorsque les colonnes de jointure couvrent toutes les colonnes de bucket, le brassage est effectué en fonction du bucket.

Exemples de code
-- 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;

Définir TableConfig au niveau du job avec SET

Les clés TableConfig table.exec.* et similaires sont des paramètres globaux au niveau du job. Elles doivent être transmises via SET ; ni l'indice LOOKUP ni l'indice OPTIONS ne les acceptent.

Clés TableConfig courantes pour la jointure de table de dimension :

Clé

Signification

Par défaut

table.exec.async-lookup.buffer-capacity

Capacité de la file d'attente du tampon pour les recherches asynchrones (valeur par défaut au niveau du job).

100

table.exec.async-lookup.timeout

Délai d'expiration pour les recherches asynchrones (valeur par défaut au niveau du job).

3 min

table.exec.async-lookup.output-mode

Ordre de sortie des recherches asynchrones (valeur par défaut au niveau du job).

ordered

-- 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;
Remarque

Lorsque SET et un indice LOOKUP définissent le même paramètre, l'indice LOOKUP sur la jointure individuelle est prioritaire.

Contrôler le brassage avec les indices de stratégie de jointure

Les indices de stratégie de jointure contrôlent le comportement de brassage pour les jointures de tables de dimension. Les indices disponibles sont SHUFFLE_HASH, REPLICATED_SHUFFLE_HASH et SKEW. Le tableau ci-dessous indique quelle stratégie de jointure convient à chaque stratégie de cache.

Stratégie de cache

SHUFFLE_HASH

REPLICATED_SHUFFLE_HASH (équivalent à SKEW)

None

Cette stratégie n'est pas recommandée ; le flux principal engendre une surcharge réseau supplémentaire.

Cette stratégie n'est pas recommandée ; le flux principal engendre une surcharge réseau supplémentaire.

LRU

Envisagez cette stratégie lorsque les E/S de recherche de dimension constituent le goulot d'étranglement. Lorsque le flux principal présente une localité temporelle sur la clé de jointure, cela améliore le taux de succès du cache, réduit les requêtes d'E/S et augmente le débit global.

Important

Le flux principal engendre une surcharge réseau supplémentaire. Si le flux principal est déséquilibré sur la clé de jointure et que vous rencontrez un goulot d'étranglement de performance, envisagez plutôt REPLICATED_SHUFFLE_HASH.

Envisagez cette stratégie lorsque les E/S de recherche de dimension constituent le goulot d'étranglement et que le flux principal est déséquilibré sur la clé de jointure. Lorsque le flux principal présente une localité temporelle sur la clé de jointure, cela améliore le taux de succès du cache, réduit les requêtes d'E/S et augmente le débit global.

ALL

Recommandé lorsque la mémoire de la table de dimension devient le goulot d'étranglement. L'utilisation de la mémoire peut descendre à 1 / parallélisme.

Important

Le flux principal engendre une surcharge réseau supplémentaire. Si le flux principal est déséquilibré sur la clé de jointure et que vous rencontrez un goulot d'étranglement de performance, envisagez plutôt REPLICATED_SHUFFLE_HASH.

Recommandé lorsque la mémoire de la table de dimension devient le goulot d'étranglement et que le flux principal est déséquilibré sur la clé de jointure. L'utilisation de la mémoire descend à nombre de buckets / parallélisme.

Important
  • L'option shuffle de l'indice LOOKUP couvre déjà ce que fait SHUFFLE_HASH. Lorsque les deux sont présents, l'option shuffle de l'indice LOOKUP est prioritaire.

  • L'option shuffle de l'indice LOOKUP ne gère pas encore le déséquilibre des données. Lorsqu'elle est combinée avec REPLICATED_SHUFFLE_HASH ou SKEW, la stratégie spécifiée par REPLICATED_SHUFFLE_HASH ou SKEW est prioritaire.

SHUFFLE_HASH

Effet : L'utilisation d'une stratégie de hachage avec brassage dans une jointure de table de dimension brasse le flux principal par clé de jointure avant la jointure. Combinée à la stratégie de cache LRU, elle augmente le taux de succès du cache et réduit les requêtes d'E/S ; combinée à la stratégie de cache ALL, elle réduit l'utilisation de la mémoire. Un seul indice SHUFFLE_HASH peut spécifier plusieurs tables de dimension.

Limites : SHUFFLE_HASH réduit l'utilisation de la mémoire, mais les données en amont doivent être brassées par clé de jointure, ce qui introduit une surcharge réseau supplémentaire. Évitez-le dans les deux cas suivants :

  • Le flux principal est fortement déséquilibré sur la clé de jointure. L'utilisation de SHUFFLE_HASH ferait du nœud de jointure un goulot d'étranglement, provoquant une contre-pression sévère pour les jobs en continu ou des queues longues pour les jobs par lots. Utilisez plutôt REPLICATED_SHUFFLE_HASH.

  • La table de dimension est petite et la stratégie de cache ALL ne présente aucun goulot d'étranglement mémoire. Dans ce cas, la mémoire économisée par SHUFFLE_HASH ne justifie pas la surcharge réseau supplémentaire.

Exemple de code :

-- 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

Effet : REPLICATED_SHUFFLE_HASH se comporte essentiellement de la même manière que SHUFFLE_HASH, sauf qu'il distribue aléatoirement les lignes du flux principal ayant la même clé sur N instances parallèles. Cela permet de résoudre le goulot d'étranglement de performance causé par le déséquilibre des données. Un seul indice REPLICATED_SHUFFLE_HASH peut spécifier plusieurs tables de dimension.

Limites :

  • Configurez le nombre de buckets pour les données déséquilibrées avec table.exec.skew-join.replicate-num (par défaut : 16). Sa valeur ne peut pas dépasser le parallélisme du nœud de jointure de dimension. Pour plus de détails, consultez la section Optimisation SQL au niveau du job.

  • Les flux de mise à jour ne sont pas pris en charge. L'utilisation de cet indice lorsque le flux principal est un flux de mise à jour génère une erreur.

Exemple de code :

SELECT /*+ REPLICATED_SHUFFLE_HASH(dim1) */ ...

SKEW

Effet : Lorsque la table spécifiée est connue pour être déséquilibrée, l'optimiseur applique la stratégie de hachage avec brassage répliqué. SKEW est un sucre syntaxique ; il est implémenté en interne avec un hachage de brassage répliqué.

Limites :

  • Chaque indice SKEW ne peut spécifier qu'une seule table.

  • Le nom de la table doit faire référence à la table principale présentant le déséquilibre, et non à la table de dimension.

  • Les flux de mise à jour ne sont pas pris en charge. L'utilisation de cet indice lorsque le flux principal est un flux de mise à jour génère une erreur.

Exemple de code :

SELECT /*+ SKEW(src) */  ...

Exemples

Exemple 1 : Une jointure de table de dimension basique

La forme la plus basique : enrichir un flux Kafka avec une table de dimension MySQL. Aucun indice d'optimisation n'est utilisé.

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;

Exemple 2 : Activer le cache avec l'indice OPTIONS et configurer la recherche asynchrone avec l'indice LOOKUP

Scénario d'optimisation : le QPS de la table de dimension est insuffisant. Vous souhaitez activer le cache partiel sur la table de dimension et activer la recherche asynchrone avec une file d'attente de tampon plus 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;
  • Options du connecteur (lookup.cache, lookup.partial-cache.max-rows) sont transmises via un indice OPTIONS() placé juste après la référence de la table de dimension ; cela remplace les options WITH correspondantes au moment de la requête.

  • Comportement de recherche (async, capacity) est configuré via un indice LOOKUP() placé après SELECT. OPTIONS et LOOKUP sont deux types d'indices différents et doivent être écrits séparément ; les combiner désactive silencieusement l'indice.

Exemple 3 : Optimiser le tampon de recherche asynchrone au niveau du job avec SET

Scénario d'optimisation : plusieurs jointures de tables de dimension dans le même job nécessitent un tampon de recherche asynchrone plus grand. Vous souhaitez que la modification s'applique globalement et activer l'asynchrone via l'indice 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-capacity et les paramètres similaires sont des clés TableConfig au niveau du job et ne peuvent être définis qu'avec SET. Les placer à l'intérieur de OPTIONS ou LOOKUP n'a aucun effet.

  • Lorsque l'indice LOOKUP ne spécifie pas capacity, la valeur globale issue de SET est utilisée. Si les deux sont spécifiés, l'indice LOOKUP est prioritaire sur cette jointure individuelle.