Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

BigQuery : des données toujours fraîches, sans mutations à grande échelle

By Nadav WeissmanMay 7, 20239 min read

Cette page est également disponible en English, Deutsch, Español, Italiano, 日本語 et Português.

BigQuery — données fraîches sans mutations massives

Photo d'eMotion Tech sur Unsplash

Préambule

Ce billet présente une manière d'aborder un défi bien connu des entrepôts de données : maintenir des données fraîches et à jour tout en évitant les mutations massives.

Chez DoiT, nous accompagnons de nombreux clients dans la conception de systèmes bien architecturés et dans l'utilisation efficace des services cloud. Ce qui suit s'inspire d'un cas client.

Contexte

Une entreprise SaaS propose à ses clients une plateforme analytique bâtie sur BigQuery. Pour certains d'entre eux, les données sont reçues sous forme de snapshot complet, incluant tout l'historique et pas uniquement les modifications incrémentales depuis la dernière mise à jour.

L'un de ces clients fournit un snapshot de 15 To, alors que les données réellement modifiées ne représentent que 0,1 %. Faute de pouvoir récupérer uniquement les changements incrémentaux, l'équipe devait identifier la méthode la plus fiable et la plus efficace, en termes de performance et de coût, pour maintenir la table existante à jour avec les données du nouveau snapshot.

Exigences du pipeline de données

L'entreprise et son client ont défini un contrat de données pour le pipeline afin de répondre aux exigences métier et techniques :

  • Les données entrantes constituent un snapshot complet sur une fenêtre glissante de deux ans, livré six fois par jour.
  • Les données sont partitionnées par jour (taille de partition d'environ 20 Gio) et marquées par un identifiant de snapshot (valeur incrémentale).
  • Les données contiennent à la fois des données nouvelles (nouvelles clés) et des données modifiées (mises à jour de données existantes).
  • Expiration des partitions configurée à 2 ans.
  • Les enregistrements présents dans le snapshot actuel mais absents du nouveau sont conservés jusqu'à l'expiration de la partition.

L'équipe produit de l'entreprise SaaS a défini les exigences suivantes :

  • Modélisation : deux tables contiendront les données existantes et les nouvelles.

Les nouvelles données résident dans une table nommée staging, et les données existantes (exposées au client) dans une table nommée target.

  • Unicité : les données de la table target sont uniques (pas de clés en doublon).
  • Fraîcheur : la table target est rafraîchie dans l'heure suivant l'arrivée du snapshot.
  • Disponibilité : l'utilisateur final obtient toujours une réponse à sa requête.

Le défi des mutations

Comme l'explique le billet Google ci-dessous, BigQuery n'est pas conçu comme les bases OLTP transactionnelles, qui sont nativement efficaces pour les mutations volumineuses.

" BigQuery n'est pas la seule base OLAP à être contrainte sur la fréquence des mutations, que ce soit explicitement (via des quotas) ou implicitement (avec une dégradation significative des performances). Ces bases sont en effet optimisées pour l'ingestion à grande échelle et les requêtes analytiques, et non pour le traitement transactionnel. BigQuery permet par ailleurs de lire l'état d'une table à n'importe quel instant des sept jours précédents. Cette fenêtre rétrospective impose de conserver des données déjà supprimées par l'utilisateur. Pour garantir des requêtes efficaces et économiques à grande échelle, BigQuery limite la fréquence des mutations via des quotas. " ( Performing large-scale mutations in BigQuery | Google Cloud Blog )

Un remplacement complet, qui supprimerait l'ancien snapshot pour le remplacer par le nouveau, ne suffit pas : il faut conserver les enregistrements présents dans le snapshot actuel mais absents du dernier.

Il faut donc comparer les enregistrements entre le snapshot existant et le nouveau, ce qu'une approche de remplacement classique ne permet pas.

Pour illustrer les différentes options de mutation, posons précisément le problème avec une modélisation de données et une logique de mutation concrètes.

Modélisation des données

Schéma de la table :

Un enregistrement d'une table (staging ou target) contient un attribute_id et une valeur pour un produit donné (product_id) dans un magasin (store_id), à une date précise, ainsi que la référence du snapshot dans lequel il figurait (update_id).

Data

Exemple d'enregistrement :

D'après les données reçues dans le snapshot ID 1 (update_id), la quantité totale (attribute_id = 1) d'un produit (product_id = 301865) dans un magasin (store_id = 2072) était de 20 le 21/04/2017.

Exemple de logique de mutation :

Voici un échantillon issu d'une partition (date donnée) provenant de deux snapshots différents.

a

Table target après ingestion du snapshot n°1

Le snapshot n°2 contenait les changements suivants :

1. Bleu : enregistrements mis à jour (présents dans les deux snapshots), valeur modifiée.

2. Rouge : enregistrement historique (présent uniquement dans le snapshot n°1).

3. Vert : nouvel enregistrement (présent uniquement dans le snapshot n°2).

DoiT

Table target après ingestion du snapshot n°2

BigQuery prenant en charge l'opération MERGE, fusionner la table de staging avec la table cible constitue l'approche intuitive, suivant la logique ci-dessous.

Logique de fusion

Cette logique MERGE définit les mutations à appliquer selon les différents cas, à partir d'une clé de jointure composée des champs suivants : product_id, store_id, attribute_id.

Merge

Voici l'implémentation de la logique MERGE :

MERGE
nw-playground.demo.merge_target T
USING
nw-playground.demo.merge_staging S
ON
T.product_Id = S.product_Id
AND T.store_Id = S.store_Id
AND T.attribute_Id = S.attribute_Id
AND T.date = S.date
WHEN MATCHED and T.date >= '2017-04-14' and T.date <= '2017-04-26'
THEN UPDATE SET T.attr_value = S.attr_value, T.update_id = S.update_id
WHEN NOT MATCHED BY TARGET and S.date >= '2017-04-14' and S.date <= '2017-04-26'
THEN
INSERT
(date,
product_Id,
store_Id,
attribute_Id,
attr_value,
update_id)
VALUES
(S.date, S.Product_id, S.store_Id, S.attribute_Id, S.attr_value, S.update_id);

Problème :

Comme indiqué plus haut, l'opération MERGE est très coûteuse dans BigQuery et soumise à des limitations, car elle implique un brassage (shuffle) et la mutation des données existantes.

Il faut donc trouver un moyen d'éviter à la fois le MERGE et la mutation des données.

Approche : déduplication et clonage

Que va-t-on faire ?

Au lieu de fusionner les enregistrements des tables staging et target, voici la marche à suivre :

  1. Ajouter le nouveau snapshot à la table staging.
  2. Dédupliquer les enregistrements similaires en ne conservant que le plus récent.

La déduplication est un procédé qui supprime les enregistrements dupliqués pour n'en conserver qu'un seul. Elle s'appuie sur une clé permettant d'identifier les enregistrements similaires et sur une logique pour décider lequel conserver parmi les doublons (par exemple, le plus récent). 3. Remplacer la table staging par le résultat de la déduplication. 4. Remplacer la table target par un CLONE de la table staging.

CLONE : un clone de table est une copie légère et modifiable d'une autre table (appelée table de base). Vous n'êtes facturé que pour le stockage des données du clone qui diffèrent de la table de base ; au départ, il n'y a donc aucun coût de stockage pour le clone. Hormis le modèle de facturation du stockage et quelques métadonnées supplémentaires concernant la table de base, un clone se comporte comme une table standard : vous pouvez l'interroger, le copier, le supprimer, etc. Une fois créé, un clone est indépendant de la table de base. Aucune modification apportée à la table de base ou au clone n'est répercutée sur l'autre. table-clones-intro

Comment va-t-on procéder ?

  1. Ajout des données : utiliser la commande LOAD en mode append, afin que la table de staging contienne le snapshot actuel et le nouveau.
  2. Déduplication : dédupliquer les enregistrements similaires (même clé) en gardant le plus récent. La déduplication n'affecte ni les enregistrements entièrement nouveaux, ni ceux présents uniquement dans le snapshot actuel. Elle s'appuie sur des window functions, qui calculent des valeurs sur un groupe de lignes, et sur l'opération QUALIFY. La clause QUALIFY permet de filtrer les résultats d'une window function.
  3. Remplacer staging : utiliser CREATE OR REPLACE à partir du résultat de la requête de déduplication.
  4. Cloner staging vers target : utiliser CREATE OR REPLACE TABLE ... CLONE.

Pourquoi cette approche ?

Elle présente plusieurs atouts par rapport au MERGE :

1. Moins de brassage : ajouter le nouveau snapshot à l'existant évite le shuffle entre deux tables.

2. Pas d'opération MERGE/JOIN : la déduplication s'appuie sur une window function avec une clause PARTITION BY, qui découpe les lignes en partitions évaluées indépendamment.

3. Pas de mutation : on crée ou on clone des tables, ce qui contourne les quotas de mutation BigQuery.

Exemple de logique d'ajout et de déduplication :

appending

Table de staging après ajout du nouveau snapshot (n°2)

À l'instar du résultat du MERGE, la table après déduplication contient les changements suivants :

1. Bleu : enregistrements mis à jour (présents dans les deux snapshots), valeur modifiée.

2. Rouge : anciens enregistrements à conserver (présents uniquement dans le snapshot n°1).

3. Vert : nouvel enregistrement (présent uniquement dans le snapshot n°2).

after

Table de staging après déduplication

À l'arrivée du snapshot suivant, le même processus s'applique.

Exemples de code

Le code ci-dessous crée une nouvelle table de staging à partir du résultat de la déduplication, qui remplace la table existante grâce à la clause QUALIFY. Un numéro de ligne est attribué à chaque enregistrement partageant une clé identique, le 1er enregistrement (rownum=1) étant le plus récent.

CREATE OR REPLACE TABLE
`nw-playground.demo.dedup_staging`
(date DATE, product_id INT64,
store_id INT64, attribute_id INT64,
attr_value INT64, update_id INT64)
PARTITION BY
date
CLUSTER BY
product_id,
store_id AS
SELECT
date, product_id, store_id, attribute_id, attr_value, update_id
FROM
`nw-playground.demo.dedup_staging`
WHERE
date >= '2017-04-14' AND date <= '2017-04-26'
QUALIFY ROW_NUMBER()
OVER(PARTITION BY date, product_id, store_id, attribute_id
ORDER BY update_id DESC ) = 1;
CREATE OR REPLACE TABLE
`nw-playground.nw_demo.dedup_target`
CLONE `nw-playground.nw_demo.dedup_staging`;

Données de test et résultats

Les tests ont porté sur 7,8 milliards d'enregistrements (350 Gio) répartis sur 13 partitions, avec environ 3 M de différences (tous types de changements) entre les tables staging et target. Ils ont été réalisés avec le modèle de facturation à la demande.

Dans le cas du MERGE, la table staging ne contenait que le nouveau snapshot et la table target contenait le snapshot existant. Dans le cas de la déduplication, la table de staging contenait à la fois le snapshot existant et le nouveau, tandis que la target était un clone du premier snapshot de staging.

Comparaison des résultats

Le tableau suivant compare les résultats :

table

Synthèse comparative des détails d'exécution

La comparaison des plans d'exécution montre clairement que la durée et l'utilisation des slots du MERGE sont le double de celles du DEDUPLICATE :

execution

Durée de l'opération principale et utilisation des slots

Comparaison des coûts

Modèle de facturation à la demande

Le coût est similaire entre les tests, puisque le volume de données scanné est identique. Il atteindrait environ 187 $ (6,25 $ par Tio) en charge complète, sur la base de 30 To pour 2 snapshots.

BigQuery Editions

Le nouveau modèle de facturation introduit par Google en avril 2023 repose sur une tarification à la capacité (paiement par slot/heure) et propose trois éditions. Pour estimer le coût d'une charge en production, on peut multiplier par 56 le test réalisé (730 partitions journalières sur 2 ans, soit 730/13 = 56).

L'estimation reste approximative, car différentes configurations et commitments peuvent s'appliquer. Elle illustre surtout l'écart entre les deux options.

BigQuery

Comparaison des coûts estimés entre les éditions BigQuery (tarifs US)

Annexe

Il existe plusieurs façons d'implémenter la déduplication (avec ROWNUM ou GROUP BY). La déduplication via ROWNUM offre des performances comparables à la clause QUALIFY ; QUALIFY reste néanmoins une commande plus claire. Avec GROUP BY, les performances étaient moins bonnes, équivalentes à celles du MERGE.

Une autre approche efficace est envisageable : les procédures stockées pour Apache Spark. À suivre dans un prochain billet…

Comme l'illustre ce cas d'usage, on est confronté à une mutation à grande échelle qui repose sur une logique de jointure avec les données existantes. La meilleure approche consiste à éviter toute modification des données.

Le recours à la déduplication et au CLONE contourne les limitations DML et améliore considérablement les deux dimensions, durée d'exécution et utilisation des slots, soit une réduction d'environ 50 % en performance et en coût.