Apache Paimon prend en charge deux types de tables : les tables à clé primaire et les tables en ajout seul. Chaque type possède une sémantique distincte pour les écritures, les mises à jour et la consommation en streaming. Choisissez le type adapté aux exigences de votre pipeline de données.
Choisir un type de table
| Scénario | Configuration recommandée |
|---|---|
| Synchronisation CDC, upserts standard | Table à clé primaire, moteur deduplicate |
| Déduplication où seul le premier enregistrement importe | Table à clé primaire, moteur first-row |
| Agrégation en temps réel (totaux cumulés, suivi des max/min) | Table à clé primaire, moteur aggregation |
| Assemblage de tables larges à partir de plusieurs sources de streaming | Table à clé primaire, moteur partial-update |
| Ingestion de logs, streaming d'événements | Table en ajout seul (évolutive) |
| Remplacement de file d'attente de messages préservant l'ordre | Table de file d'attente en ajout |
Tables à clé primaire
Une table à clé primaire nécessite une ou plusieurs clés primaires, spécifiées lors de sa création. Les enregistrements partageant les mêmes clés primaires sont fusionnés selon le moteur de fusion configuré.
CREATE TABLE T (
dt STRING,
shop_id BIGINT,
user_id BIGINT,
num_orders INT,
total_amount INT,
PRIMARY KEY (dt, shop_id, user_id) NOT ENFORCED
) PARTITIONED BY (dt) WITH (
'bucket' = '4'
);
Dans cet exemple, la clé de partition est dt, les clés primaires sont dt, shop_id et user_id. La table utilise 4 buckets fixes.
Modes de bucket
Le bucket est la plus petite unité de lecture et d'écriture dans une table Paimon. Les partitions (ou la table entière pour les tables non partitionnées) sont subdivisées en buckets, ce qui permet des lectures et des écritures parallèles.
| Mode | Configuration | Notes |
|---|---|---|
| Mode de bucket dynamique (par défaut) | Omettre bucket ou définir 'bucket' = '-1' |
Ne prend pas en charge les écritures concurrentes depuis plusieurs déploiements Flink ; prend en charge les mises à jour inter-partitions |
| Mode de bucket fixe | Définir 'bucket' = '<num>' (entier > 0) |
<num> correspond au nombre de buckets pour toute la table (non partitionnée) ou par partition (partitionnée) ; prend en charge la modification du nombre de buckets |
Dimensionnement des buckets : Nous recommandons de limiter la taille totale des données dans chaque bucket à 2 Go et de ne pas dépasser 5 Go. Un nombre insuffisant de buckets limite le parallélisme d'écriture, tandis qu'un nombre excessif génère trop de petits fichiers.
Mode de bucket dynamique : mise à jour des données
Les mises à jour inter-partitions se produisent lorsque les clés primaires n'incluent pas toutes les clés de partition. Paimon ne pouvant pas déterminer la partition cible à partir de la seule clé primaire, il utilise RocksDB pour maintenir une correspondance entre la clé primaire et la partition/bucket. Sur les grandes tables, cela peut réduire considérablement les performances par rapport au mode de bucket fixe. L'initialisation du déploiement prend également plus de temps, le temps que la correspondance soit chargée dans RocksDB.
Le comportement de mise à jour dépend du moteur de fusion :
deduplicate: supprime l'enregistrement existant et insère un nouvel enregistrement dans la partition cible.aggregationoupartial-update: met à jour l'enregistrement existant sur place dans la partition actuelle.first-row: conserve l'enregistrement existant et ignore l'enregistrement entrant.
Les mises à jour intra-partition se produisent lorsque les clés primaires incluent toutes les clés de partition. Paimon peut déterminer la partition à partir de la clé primaire, mais pas le bucket. Il maintient donc un index en mémoire mappant les clés primaires aux buckets. Chaque tranche de 100 millions d'entrées de mappage consomme environ 1 Go de mémoire heap, imputée uniquement aux partitions en cours d'écriture active.
Mode de bucket dynamique : attribution des buckets
Les nouveaux enregistrements sont d'abord écrits dans les buckets existants. Si tous les buckets sont saturés, un nouveau bucket est créé automatiquement.
Configurez ce comportement avec les paramètres suivants dans la clause WITH :
| Paramètre | Description | Valeur par défaut |
|---|---|---|
dynamic-bucket.target-row-num |
Nombre maximal d'enregistrements par bucket | 2000000 |
dynamic-bucket.initial-buckets |
Nombre initial de buckets | Parallélisme de l'opérateur d'écriture |
Mode de bucket fixe : attribution des buckets
Par défaut, Paimon attribue les enregistrements aux buckets en hachant les valeurs de clé primaire. Pour utiliser un autre ensemble de colonnes, définissez bucket-key dans la clause WITH. Spécifiez plusieurs colonnes séparées par des virgules. Les colonnes de bucket-key doivent être un sous-ensemble des clés primaires.
Par exemple, 'bucket-key' = 'c1,c2' achemine les enregistrements selon le hachage de c1 et c2.
En mode de bucket fixe, incluez toutes les clés de partition dans les clés primaires pour éviter les mises à jour inter-partitions.
Modifier le nombre de buckets en mode de bucket fixe
Le nombre de buckets détermine le parallélisme de lecture et d'écriture. Pour le modifier sur une table existante :
Suspendez tous les déploiements qui lisent ou écrivent dans la table.
-
Créez un script et exécutez l'instruction SQL suivante pour configurer le paramètre de bucket :
ALTER TABLE `<catalog-name>`.`<database-name>`.`<table-name>` SET ('bucket' = '<bucket-num>'); -
Réorganisez les données en exécutant un déploiement par lots.
Table non partitionnée : Créez un Blank Batch Draft, collez le code SQL suivant, puis cliquez sur Deploy et Start : ``
sql INSERT OVERWRITE<catalog-name>.<database-name>.<table-name>SELECT * FROM<catalog-name>.<database-name>.<table-name>;``-
Table partitionnée : Exécutez un lot par partition à réorganiser :
INSERT OVERWRITE `<catalog-name>`.`<database-name>`.`<table-name>` PARTITION (<partition-spec>) SELECT * FROM `<catalog-name>`.`<database-name>`.`<table-name>` WHERE <partition-condition>;Exemple — réorganisation de la partition où
dt = '20240312'ethh = '08':INSERT OVERWRITE `<catalog-name>`.`<database-name>`.`<table-name>` PARTITION (dt = '20240312', hh = '08') SELECT * FROM `<catalog-name>`.`<database-name>`.`<table-name>` WHERE dt = '20240312' AND hh = '08';
Une fois le déploiement par lots terminé avec succès, reprenez les déploiements suspendus.
Moteurs de fusion
Lorsque plusieurs enregistrements partagent les mêmes clés primaires, Paimon les fusionne selon le paramètre merge-engine.
Si votre flux d'entrée contient des enregistrements hors ordre, configurez le paramètre sequence.field pour contrôler l'ordre de fusion. Consultez la section Gestion des données hors ordre .
Gestion des données hors ordre
Par défaut, Paimon fusionne les enregistrements dans l'ordre d'arrivée : le dernier enregistrement reçu est le dernier à être fusionné. Pour les flux contenant des enregistrements hors ordre, définissez sequence.field dans la clause WITH. Les enregistrements partageant les mêmes clés primaires sont alors fusionnés dans l'ordre croissant de la valeur de la colonne spécifiée.
Types de données pris en charge pour sequence.field : TINYINT, SMALLINT, INTEGER, BIGINT, TIMESTAMP, TIMESTAMP_LTZ.
Lors de l'utilisation de MySQL comme source d'entrée avec la colonne de métadonnéesop_tcomme champ de séquence, les paires de modifications UPDATE_BEFORE et UPDATE_AFTER partagent la même valeur de séquence. Pour garantir que Paimon traite UPDATE_BEFORE avant UPDATE_AFTER, définissez'sequence.auto-padding' = 'row-kind-flag'.
Producteur de journal des modifications
Une table à clé primaire doit générer un journal des modifications complet (couvrant les opérations INSERT, DELETE et UPDATE) pour prendre en charge la consommation en streaming en aval, à l'instar d'un journal binaire de base de données. Configurez la méthode de génération avec le paramètre changelog-producer.
| Valeur | Description | Latence | Utilisation des ressources | À utiliser lorsque |
|---|---|---|---|---|
none |
Aucun journal des modifications n'est généré. | — | La plus faible | Aucune consommation en streaming nécessaire |
input |
Transmet directement les enregistrements d'entrée aux consommateurs en aval. | La plus faible | La plus faible | L'entrée contient déjà un journal des modifications complet, tel qu'un journal binaire de base de données. Option la plus efficace : aucun calcul supplémentaire requis. |
lookup (recommandé) |
Exécute une recherche sur les résultats de compaction des petits fichiers pour produire un journal des modifications complet. Déclenché à chaque point de contrôle Flink. | Niveau minute | Plus élevée | Tout type d'entrée ; exigences de latence au niveau de la minute |
full-compaction |
Génère un journal des modifications après chaque compaction complète des petits fichiers. | Jusqu'à plusieurs heures | Inférieure à lookup |
Tout type d'entrée ; une latence plus élevée est acceptable |
Pour full-compaction, définissez 'full-compaction.delta-commits' = '<num>' pour déclencher une compaction complète après chaque <num> points de contrôle Flink. La compaction complète est gourmande en ressources : définissez l'intervalle entre 30 minutes et 1 heure.
Par défaut, Paimon génère un enregistrement de journal des modifications même lorsque la valeur mise à jour est identique à la précédente. Pour supprimer ces enregistrements sans effet, définissez'changelog-producer.row-deduplicate' = 'true'. Cette option s'applique uniquement àlookupetfull-compactionet nécessite un calcul supplémentaire pour comparer les valeurs avant/après ; activez-la uniquement lorsqu'un grand nombre d'enregistrements inutiles est attendu.
Tables en ajout seul
Une table en ajout seul ne possède pas de clés primaires et n'accepte que les opérations INSERT en mode streaming. Utilisez ce type de table pour les charges de travail ne nécessitant pas de mises à jour en streaming, telles que la synchronisation de données de journal.
CREATE TABLE T (
dt STRING,
order_id BIGINT,
item_id BIGINT,
amount INT,
address STRING
) PARTITIONED BY (dt) WITH (
'bucket' = '-1'
);
Sous-types
| Sous-type | Configuration | Caractéristiques |
|---|---|---|
| Table évolutive en ajout | 'bucket' = '-1' |
Débit d'écriture élevé ; aucun partitionnement par hachage requis ; tri des données pris en charge ; configuration flexible du parallélisme ; prise en charge de la conversion directe depuis les tables Hive ; compaction de fichiers complètement asynchrone ; l'ordre de consommation diffère de l'ordre d'écriture ; skew des données possible lorsque le parallélisme amont est égal au parallélisme de l'writer |
| Table de file d'attente en ajout | 'bucket' = '<num>' (entier > 0) |
Préservation de l'ordre par bucket ; latence de quelques minutes ; équivalent à une partition de topic Kafka ou au nombre de shards dans une instance ApsaraMQ for MQTT |
Attribution des buckets
Table évolutive en ajout : Les données sont écrites directement dans une seule partition en parallèle. Le concept de bucket est ignoré et aucun partitionnement par hachage n'est requis, ce qui maximise les performances d'écriture.
Table de file d'attente en ajout : Par défaut, les enregistrements sont attribués aux buckets en fonction des valeurs de toutes les colonnes de l'enregistrement de données. Pour contrôler l'attribution, définissez bucket-key dans la clause WITH avec une liste de noms de colonnes séparés par des virgules.
Par exemple, 'bucket-key' = 'c1,c2' achemine les enregistrements selon le hachage de c1 et c2.
Spécifiez bucket-key pour réduire le calcul d'attribution des buckets et améliorer l'efficacité d'écriture.
Ordre de consommation des données
Table évolutive en ajout : Les enregistrements peuvent être consommés dans un ordre différent de l'ordre d'écriture.
Table de file d'attente en ajout : Les enregistrements au sein de chaque bucket sont consommés dans l'ordre d'écriture. Pour les enregistrements provenant de différentes partitions ou buckets :
Deux enregistrements provenant de partitions différentes : si
'scan.plan-sort-partition' = 'true', l'enregistrement ayant la valeur de partition la plus petite est consommé en premier ; sinon, l'enregistrement de la partition créée le plus tôt est consommé en premier.Deux enregistrements provenant de la même partition, même bucket : l'enregistrement écrit le plus tôt est consommé en premier.
Deux enregistrements provenant de la même partition, buckets différents : l'ordre de consommation n'est pas garanti car différents buckets peuvent être traités simultanément.
Étapes suivantes
Pour créer un catalogue et une table Paimon, consultez la page Gérer les catalogues Paimon.
Pour optimiser les performances des tables à clé primaire, consultez la page Optimisation des performances.