Le connecteur Upsert Kafka lit et écrit dans des rubriques Kafka en utilisant la sémantique upsert. En tant que source, il convertit les enregistrements Kafka en un flux de journal des modifications (changelog). En tant que puits (sink), il écrit les données du journal des modifications dans Kafka, en utilisant des messages à valeur nulle (tombstones) pour représenter les suppressions.
| Catégorie | Détails |
|---|---|
| Types pris en charge | Table source, table puits et puits d'ingestion de données |
| Mode d'exécution | Mode streaming |
| Formats de données | avro, avro-confluent, csv, json et raw |
| Types d'API | SQL et job YAML d'ingestion de données |
| Mise à jour ou suppression des données dans une table puits | Oui |
Prérequis
Avant de commencer, assurez-vous de disposer des éléments suivants :
Un cluster Kafka. Créez un cluster DataFlow Kafka ou créez des ressources dans ApsaraMQ for Kafka
Une connectivité réseau entre votre cluster Flink et votre cluster Kafka. Pour Kafka sur EMR, configurez un VPC et un groupe de sécurité. Pour ApsaraMQ for Kafka, configurez une liste d'autorisation
Fonctionnement
Le flux de journal des modifications upsert produit par la source contient uniquement des entrées +I (insertion), +U (mise à jour après) et -D (suppression). Contrairement à un flux retract, il ne contient jamais de messages -U (mise à jour avant).
| Type d'entrée de changelog | Nom long | Sémantique |
|---|---|---|
+I |
Insertion | Un nouvel enregistrement dont la clé n'existait pas auparavant |
+U |
Mise à jour après | Un enregistrement dont la clé existe déjà — écrase la valeur précédente |
-D |
Suppression | Un enregistrement avec une valeur nulle — supprime l'entrée pour cette clé |
En tant que table source, le connecteur lit les enregistrements Kafka et les convertit en un flux de journal des modifications :
Un enregistrement dont la clé existe déjà est interprété comme une MISE À JOUR (
+U)Un enregistrement dont la clé n'existe pas est interprété comme une INSERTION (
+I)Un enregistrement avec une valeur nulle est interprété comme une SUPPRESSION (
-D)
En tant que table puits ou puits d'ingestion de données, le connecteur consomme un flux de journal des modifications en amont :
Les enregistrements
INSERTetUPDATE_AFTERsont écrits sous forme de messages Kafka normauxLes enregistrements
DELETEsont écrits sous forme de messages tombstone (valeur nulle pour la clé correspondante)
Flink partitionne la sortie du puits par clé primaire, de sorte que tous les messages pour la même clé arrivent dans la même partition dans le bon ordre.
Exemple
L'exemple de bout en bout suivant lit les événements de consultation de pages web depuis une table source Kafka, agrège les vues de page (PV) et les visiteurs uniques (UV) par région, et écrit les résultats dans un puits Upsert Kafka.
Table source — lit les événements bruts de consultation de pages :
CREATE TABLE pageviews (
user_id BIGINT,
page_id BIGINT,
viewtime TIMESTAMP,
user_region STRING,
WATERMARK FOR viewtime AS viewtime - INTERVAL '2' SECOND
) WITH (
'connector' = 'kafka',
'topic' = '<yourTopicName>',
'properties.bootstrap.servers' = '<host:port>',
'format' = 'json'
);
Table puits — stocke les résultats agrégés avec la sémantique upsert :
CREATE TABLE pageviews_per_region (
user_region STRING,
pv BIGINT,
uv BIGINT,
PRIMARY KEY (user_region) NOT ENFORCED
) WITH (
'connector' = 'upsert-kafka',
'topic' = '<yourTopicName>',
'properties.bootstrap.servers' = '<host:port>',
'key.format' = 'avro',
'value.format' = 'avro'
);
Chaque table Upsert Kafka doit définir une clé primaire. Les colonnes de la clé primaire déterminent comment Flink partitionne la sortie et comment le connecteur identifie les opérations upsert.
Requête — agrège et écrit les résultats :
INSERT INTO pageviews_per_region
SELECT
user_region,
COUNT(*),
COUNT(DISTINCT user_id)
FROM pageviews
GROUP BY user_region;
SQL
Syntaxe
CREATE TABLE upsert_kafka_sink (
user_region STRING,
pv BIGINT,
uv BIGINT,
PRIMARY KEY (user_region) NOT ENFORCED
) WITH (
'connector' = 'upsert-kafka',
'topic' = '<yourTopicName>',
'properties.bootstrap.servers' = '...',
'key.format' = 'avro',
'value.format' = 'avro'
);
Paramètres WITH
Paramètres généraux
| Paramètre | Obligatoire | Par défaut | Description |
|---|---|---|---|
connector |
Oui | — | Définissez sur upsert-kafka. |
properties.bootstrap.servers |
Oui | — | Liste séparée par des virgules des adresses des courtiers Kafka au format host:port. |
topic |
Oui | — | La rubrique Kafka à partir de laquelle lire ou dans laquelle écrire. |
key.format |
Oui | — | Format de sérialisation pour la clé du message. Valeurs valides : csv, json, avro, debezium-json, canal-json, maxwell-json, avro-confluent, raw. |
value.format |
Oui | — | Format de sérialisation pour la valeur du message. Équivalent à format — configurez-en un seul. |
value.fields-include |
Oui | ALL |
Contrôle les colonnes incluses dans la valeur du message. ALL inclut toutes les colonnes ; EXCEPT_KEY exclut les champs de clé définis par key.fields. |
properties.* |
Non | — | Paramètres client Kafka supplémentaires. Le préfixe properties. est supprimé avant de transmettre la configuration au client. Le suffixe doit être une clé de configuration producteur ou consommateur Kafka valide. N'utilisez pas ce mécanisme pour définir key.deserializer ou value.deserializer — le connecteur gère ces paramètres en interne. |
key.fields-prefix |
Non | — | Un préfixe personnalisé appliqué à tous les noms de champs de clé dans le schéma de la table, utilisé pour éviter les collisions de noms avec les champs de valeur. Le préfixe est supprimé lors de l'analyse et de la génération de la clé. Si défini, value.fields-include doit être défini sur EXCEPT_KEY. |
Paramètres spécifiques au puits
| Paramètre | Obligatoire | Par défaut | Description |
|---|---|---|---|
sink.parallelism |
Non | Hérité de l'opérateur en amont | Parallélisme de l'opérateur de puits Kafka. |
sink.buffer-flush.max-rows |
Non | 0 (désactivé) |
Nombre maximal d'enregistrements à mettre en mémoire tampon avant vidage. Lorsque plusieurs mises à jour arrivent pour la même clé, seul le dernier enregistrement est conservé dans la mémoire tampon, ce qui réduit le volume d'écriture et évite les messages tombstone inutiles. sink.buffer-flush.max-rows et sink.buffer-flush.interval doivent tous deux être supérieurs à zéro pour activer la mise en mémoire tampon. |
sink.buffer-flush.interval |
Non | 0 (désactivé) |
Fréquence de vidage de la mémoire tampon. Unités prises en charge : ms, s, min, h — par exemple, '1 s'. Le même comportement de déduplication par clé que sink.buffer-flush.max-rows s'applique. Les deux paramètres doivent être supérieurs à zéro pour activer la mise en mémoire tampon. |
Ingestion de données
Le connecteur Upsert Kafka peut servir de puits dans un job YAML d'ingestion de données. Les données sont écrites au format JSON, et les champs de clé primaire sont inclus dans le corps du message.
Syntaxe
sink:
type: upsert-kafka
name: upsert-kafka Sink
properties.bootstrap.servers: localhost:9092
# ApsaraMQ for Kafka
aliyun.kafka.accessKeyId: ${secret_values.kafka-ak}
aliyun.kafka.accessKeySecret: ${secret_values.kafka-sk}
aliyun.kafka.instanceId: ${instancd-id}
aliyun.kafka.endpoint: ${endpoint}
aliyun.kafka.regionId: ${region-id}
Paramètres
| Paramètre | Obligatoire | Par défaut | Description |
|---|---|---|---|
type |
Oui | — | Définissez sur upsert-kafka. |
name |
Non | — | Nom d'affichage pour le puits. |
properties.bootstrap.servers |
Oui | — | Liste séparée par des virgules des adresses des courtiers Kafka au format host:port. |
properties.* |
Non | — | Paramètres de producteur Kafka supplémentaires. Le préfixe properties. est supprimé avant de transmettre la configuration au client. Le suffixe doit être une clé de configuration producteur Kafka valide. |
sink.delivery-guarantee |
Non | at-least-once |
Sémantique de livraison. Valeurs valides : none (aucune garantie ; les données peuvent être perdues ou dupliquées), at-least-once (par défaut ; aucune perte de données, mais des doublons sont possibles), exactly-once (utilise les transactions Kafka pour éliminer les doublons). |
sink.add-tableId-to-header-enabled |
Non | false |
Lorsqu'il est activé, écrit namespace, schemaName et tableName dans l'en-tête du message Kafka. |
aliyun.kafka.accessKeyId |
Non | — | ID AccessKey de votre compte Alibaba Cloud. Requis lors de l'écriture dans ApsaraMQ for Kafka. Consultez Créer une paire de clés AccessKey. |
aliyun.kafka.accessKeySecret |
Non | — | Secret AccessKey de votre compte Alibaba Cloud. Requis lors de l'écriture dans ApsaraMQ for Kafka. Consultez Créer une paire de clés AccessKey. |
aliyun.kafka.instanceId |
Non | — | ID d'instance de l'instance ApsaraMQ for Kafka. Vous pouvez afficher les détails de l'instance dans la console Kafka d'Alibaba Cloud. Requis lors de l'écriture dans ApsaraMQ for Kafka. |
aliyun.kafka.endpoint |
Non | — | Point de terminaison API pour ApsaraMQ for Kafka. Requis lors de l'écriture dans ApsaraMQ for Kafka. Consultez Points de terminaison. |
aliyun.kafka.regionId |
Non | — | ID de région de l'instance ApsaraMQ for Kafka. Requis lors de l'écriture dans ApsaraMQ for Kafka. Consultez Points de terminaison. |
Modifications de type prises en charge
Le connecteur d'ingestion de données Upsert Kafka prend en charge tous les types d'opération de journal des modifications. Pour consommer les données écrites en aval, utilisez le connecteur SQL Flink Upsert Kafka avec un schéma fixe.
Exemple d'ingestion de données
L'exemple suivant synchronise une table MySQL vers ApsaraMQ for Kafka à l'aide d'un job YAML d'ingestion de données :
source:
type: mysql
name: MySQL Source
hostname: ${mysql.hostname}
port: ${mysql.port}
username: ${mysql.username}
password: ${mysql.password}
tables: ${mysql.source.table}
server-id: 8601-8604
sink:
type: upsert-kafka
name: Upsert Kafka Sink
properties.bootstrap.servers: ${upsert.kafka.bootstraps.server}
aliyun.kafka.accessKeyId: ${upsert.kafka.aliyun.ak}
aliyun.kafka.accessKeySecret: ${upsert.kafka.aliyun.sk}
aliyun.kafka.instanceId: ${upsert.kafka.aliyun.instanceid}
aliyun.kafka.endpoint: ${upsert.kafka.aliyun.endpoint}
aliyun.kafka.regionId: ${upsert.kafka.aliyun.regionid}
route:
- source-table: ${mysql.source.table}
sink-table: ${upsert.kafka.topic}
Sémantique de livraison
Par défaut, le puits Upsert Kafka écrit avec des garanties au moins une fois (at-least-once). Flink peut écrire des enregistrements en double avec la même clé dans la rubrique. Comme le connecteur fonctionne en mode upsert, le dernier enregistrement pour une clé donnée prend effet lors de la relecture de la rubrique en tant que source. Les écritures en double sont donc idempotentes — elles ne corrompent pas l'état final.
Pour activer la sémantique exactement une fois (exactly-once), le cluster Kafka doit prendre en charge les transactions Kafka (Apache Kafka 0.11 ou version ultérieure) et la fonctionnalité de transaction doit être activée. Définissez sink.delivery-guarantee sur exactly-once dans la configuration YAML d'ingestion de données.
Limites
Nécessite Flink exécuté sur Ververica Runtime (VVR) 2.0.0 ou version ultérieure.
Prend en charge Apache Kafka 0.10 ou version ultérieure pour les lectures et les écritures.
Prend en charge uniquement les paramètres client documentés pour Apache Kafka 2.8. Consultez les références de configuration consommateur et producteur.
Les tables source Upsert Kafka commencent toujours à partir de
earliest-offset— ce paramètre est fixe et non configurable. Le connecteur doit lire toutes les données de modification historiques pour reconstruire un journal des modifications complet. L'utilisation de tout autre mode de démarrage (tel quelatest-offsetou par horodatage) entraînerait un journal des modifications incomplet et provoquerait des problèmes d'exactitude des données dans les calculs en aval.Pour la sémantique de puits exactly-once, le cluster Kafka de destination doit exécuter Apache Kafka 0.11 ou version ultérieure avec les transactions activées.
Métriques de surveillance
Métriques de la table source
| Métrique | Description |
|---|---|
numRecordsIn |
Nombre total d'enregistrements lus |
numRecordsInPerSecond |
Enregistrements lus par seconde |
numBytesIn |
Total des octets lus |
numBytesInPerSecond |
Octets lus par seconde |
currentEmitEventTimeLag |
Décalage entre l'heure de l'événement et l'heure d'émission |
currentFetchEventTimeLag |
Décalage entre l'heure de l'événement et l'heure de récupération |
sourceIdleTime |
Temps pendant lequel la source a été inactive |
pendingRecords |
Nombre d'enregistrements en attente de traitement |
Métriques de la table puits
| Métrique | Description |
|---|---|
numRecordsOut |
Nombre total d'enregistrements écrits |
numRecordsOutPerSecond |
Enregistrements écrits par seconde |
numBytesOut |
Total des octets écrits |
numBytesOutPerSecond |
Octets écrits par seconde |
currentSendTime |
Temps nécessaire pour envoyer le dernier lot |