Génération de données synthétiques à grande échelle – Partie 1

Génération de données synthétiques à grande échelle – Partie 1


Au cours d’un de mes projets récents, le client m’a demandé d’effectuer une comparaison des performances entre Snowflake et son système existant, avec la mise en garde que je ne pouvais pas utiliser ses données (pas même une copie épurée) pour effectuer la comparaison. Sans accès au les données du clientma seule option était de créer des données synthétiques qui correspondaient aux données du client en termes de structure (schéma) et Taille.

Bien que les professionnels des bases de données aient souvent besoin de générer des données à des fins de test ou de démonstration, il s’agit d’un défi séculaire malgré le fait qu’il existe de nombreux outils et frameworks open source disponibles pour créer des données synthétiques. La question était de savoir si l’une de ces solutions serait capable de gérer les exigences de mise à l’échelle de ce projet, car nous ne parlions pas de centaines de milliers de lignes, mais de milliards de lignes pour la plupart des tables de faits.

Paquets de génération de données Python

Python a un excellent support pour la génération de données synthétiques. Des forfaits tels que pydbgenqui est une enveloppe autour Fauxrendent très facile la génération de données synthétiques qui ressemblent à des données du monde réel, j’ai donc décidé de l’essayer.

L’installation de pydbgen est très simple. Voici un lien vers le documentation pydbgenqui décrit comment utiliser le gestionnaire de packages pip pour installer pydbgen.

Pour l’exemple de test suivant, je crée une simple boîte AWS EC2 exécutant Ubuntu 18.04. Bien que l’AMI (Amazon Machine Image) soit préinstallée avec Python3, elle n’est pas fournie avec le gestionnaire de packages pip. Par conséquent, vous devez installer pip3 avant d’installer pydbgen.

sudo apt install python3-pip

Si vous voyez l’erreur Impossible de localiser le paquet python3-pipcela signifie que vous devez d’abord corriger votre serveur car la liste des dépôts de packages n’est pas synchronisée.

Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Unable to locate package python3-pip

Patcher votre serveur est facile et ne nécessite que les deux commandes ci-dessous.

sudo apt update
sudo apt upgrade

Vous êtes maintenant prêt à installer pydbgen via pip3. Selon les packages précédemment installés dans votre environnement, votre message de réussite peut répertorier plus ou moins de packages que ce qui est indiqué ci-dessous.

pip3 install pydbgen
Successfully installed Faker-1.0.7 Pandas-0.24.2 numpy-1.16.4 
pydbgen-1.0.5 python-dateutil-2.8.0 pytz-2019.1 six-1.12.0 
text-unidecode-1.2

Étant donné que pydbgen nécessite Python 3.x, si vous essayez d’installer pydbgen sur Python 2.x, vous obtiendrez un message d’erreur indiquant que pydbgen est introuvable.

ERROR: Could not find a version that satisfies the requirement pydbgen 
(from versions: none)
ERROR: No matching distribution found for pydbgen

Nous sommes maintenant prêts à créer des données de test à l’aide du shell Python. Suivant le documentation pydbgennous devons d’abord créer un objet de classe pydb avant de pouvoir commencer à générer des données.

import pydbgen
from pydbgen import pydbgen
myDB = pydbgen.pydb()

Supposons maintenant que nous voulions générer des données pour une table ayant le schéma suivant :

  • Nom
  • ville
  • plaque d’immatriculation
  • e-mail
testdf=myDB.gen_dataframe(5,fields=['name','city','license_plate','email'
],real_email=False,phone_simple=True)
print(testdf)
License table 1
Avec la puissance de pydbgen, les données semblent réelles mais elles sont complètement fausses.

La génération de cinq lignes s’exécute très rapidement, alors exécutons un autre test et avec 100 000 lignes. Si vous êtes assez courageux pour essayer, vous constaterez probablement que cela prend environ 10 minutes. Et si nous avions besoin de 1 million de lignes ? Comme on dit, « s’il vous plaît, n’essayez pas cela à la maison ». Cela prendrait environ deux heures, ce qui pourrait encore être acceptable si vous êtes prêt à dépenser de l’argent et du temps. Cependant, le mettre à l’échelle jusqu’à 1 milliard de lignes ne serait pas du tout gérable, j’ai donc dû chercher une solution différente.

Fonctions de génération de données de flocon de neige

Cela m’est venu à l’esprit : pourquoi ne pas utiliser Snowflake lui-même pour générer les données dont j’avais besoin pour tester Snowflake ? Snowflake, avec son approche très unique de l’évolutivité et de l’élasticité, prend également en charge un certain nombre de fonctions pour générer des données véritablement à grande échelle.

Blocs de construction

La génération de données synthétiques dans Snowflake est simple et ne nécessite rien d’autre que SQL.

Le premier bloc de construction est le flocon de neige Générateur fonction. La Générateur fonction crée des lignes de données en fonction d’un nombre cible de lignes spécifié, d’une période de génération spécifiée (en secondes) ou des deux. Pour les besoins de ce test, utilisons uniquement l’option row.

L’instruction suivante crée 10 lignes, chaque ligne ayant une colonne avec la valeur statique de 1.

select 
  1 as col1 from 
table(generator(rowcount=>10));

Bien sûr, nous ne voulons pas simplement générer des valeurs statiques de 1 ; nous avons donc besoin d’un autre bloc de construction pour générer des valeurs aléatoires. Et c’est ce que fait Snowflake Aléatoire la fonction le fait. Chaque fois Aléatoire() est appelé, il renvoie une valeur entière aléatoire de 64 bits.

select 
  random() as col1 
from table(generator(rowcount=>10));

Jusqu’ici tout va bien. Mais très probablement, vous voulez générer plus de une valeur aléatoire dans une seule instruction SQL.

select 
  random() as col1
  ,random() as col2 
from table(generator(rowcount=>10));

Malheureusement, tous les appels au Aléatoire() fonction renvoie la même valeur dans la même ligne.

Ce comportement est causé par la façon dont le Aléatoire() la fonction est « ensemencée ». La Aléatoire() La fonction prend un paramètre facultatif, appelé valeur de départ. Lorsque vous appelez le Aléatoire() fonction sans valeur de départ, une valeur de départ aléatoire est attribuée. Quand vous appelez Aléatoire() sans valeur de départ, tous les appels sont amorcés avec la même valeur de départ et, par conséquent, ils renvoient la même valeur pour la même ligne. Utilisation de valeurs de départ uniques pour chaque appel au Aléatoire() fonction assure que chaque appel à Aléatoire()renvoie une valeur différente.

select 
  random(1) as col1
  ,random(2) as col2 
from table(generator(rowcount=>10));

À ce stade, j’ai pu générer un certain nombre de lignes avec plusieurs attributs de valeurs numériques indépendantes. L’étape suivante consiste à déterminer comment générer des valeurs comprises dans une certaine plage de valeurs (par exemple, entre une valeur minimale et une valeur maximale).

Vous pouvez gérer cela en utilisant le bloc de construction suivant, qui est celui de Snowflake uniforme fonction. La uniforme() fonction prend trois paramètres : min pour spécifier une valeur minimale, maximum pour spécifier une valeur maximale, et gén, qui est une expression génératrice. Une fonction de générateur statique produit la même valeur encore et encore dans la plage de min et maximum. Par conséquent, nous utiliserons le Aléatoire() fonction, qui génère des valeurs aléatoires, et par conséquent, la uniforme() fonction renverra des valeurs aléatoires entre min et maximum.

select 
  uniform(1,10,random(1)) as col1 
from table(generator(rowcount=>100));

Enfin, nous avons besoin d’un moyen de générer des chaînes aléatoires de longueur variable. Dans Snowflake, des chaînes aléatoires peuvent être générées via le randstr fonction, qui accepte deux paramètres. Le premier paramètre est la longueur de la chaîne et le second est l’expression du générateur. Le même modèle s’applique également pour le randstr() fonction. Une expression de générateur statique produit une chaîne statique. Pour cette raison, nous utiliserons le Aléatoire fonction pour produire des chaînes aléatoires via randstr().

L’instruction suivante rassemble tous ces blocs de construction.

select 
  randstr(uniform(3,10,random(1)),uniform(1,100,random(1))) as col1 
from table(generator(rowcount=>10));

Le premier paramètre spécifie la longueur de la chaîne à générer. Dans ce cas, il s’agit d’une valeur aléatoire comprise entre 3 et 10 ; en d’autres termes, nous créons des chaînes de longueur variable de 3 à 10 caractères. Le deuxième paramètre, l’expression du générateur, est un nombre aléatoire compris entre 1 et 100. Cela signifie que nous générons 100 chaînes différentes avec une longueur variable de 3 à 10 caractères.

Veuillez noter que nous utilisons la même valeur de départ pour les deux Aléatoire() les fonctions. L’utilisation de la même valeur de départ garantit que la même valeur de chaîne a la même longueur. L’utilisation de valeurs de départ différentes augmenterait considérablement le nombre de valeurs de chaîne uniques.

Considérez la déclaration suivante.

select count(distinct col1) from (
select 
  randstr(uniform(3,10,random(1)),uniform(1,1000,random(1))) as col1 
from table(generator(rowcount=>10000)));

L’instruction SELECT interne crée 1 000 chaînes distinctes car l’expression du générateur dans randstr() La fonction crée 1 000 numéros uniques. Toutefois, si vous modifiez la valeur de départ du premier Aléatoire() fonction à autre chose que 1, l’instruction crée plus de 5 000 valeurs distinctes. Cela se produit car pour la même valeur de la fonction génératrice, vous aurez plusieurs valeurs (différentes) pour la longueur.

Mettre tous ensemble

Avec tous les blocs de construction en place, vous pouvez maintenant créer une instruction SQL pour générer un ensemble de données, similaire à celui ci-dessus qui a été créé à l’aide de pydbgen.

create or replace table testdf as
select 
   randstr(uniform(10,30,random(1)),uniform(1,100000,random(1)))::varchar(30) as name
  ,randstr(uniform(10,30,random(2)),uniform(1,10000,random(2)))::varchar(30) as city
  ,randstr(10,uniform(1,100000,random(3)))::varchar(10) as license_plate
  ,randstr(uniform(10,30,random(4)),uniform(1,200000,random(4)))::varchar(30) as email
from table(generator(rowcount=>1000000000));

L’instruction SQL ci-dessus crée 1 000 000 000 lignes, les convertit dans le type de données souhaité et spécifie également le nombre de valeurs distinctes par attribut (c’est-à-dire qu’elle définit la distribution des valeurs dans l’ensemble de données).

Run Worksheet 2

Pour récapituler, il a fallu 10 minutes pour créer 100 000 lignes pour le schéma ci-dessus à l’aide de pydbgen. Maintenant, nous essayons de générer 1 milliard de lignes. L’exécution de l’instruction ci-dessus sur le plus petit cluster de Snowflake prend environ 8 minutes. Si nous extrapolons le temps d’exécution pour le script Python, cela prendrait environ 87 jours et échouerait sans une quantité suffisante de RAM sur votre machine. Comme vous pouvez le voir, Snowflake fait une sacrée amélioration.

Si vous devez créer des ensembles de données encore plus volumineux avec des tables plus larges, vous pouvez augmenter de manière élastique la capacité de calcul, ce qui réduit le temps d’exécution. Pour illustrer ce comportement, réexécutons l’instruction ci-dessus sur un cluster Snowflake de petite taille. Étant donné qu’un cluster de petite taille fournit deux fois plus de capacité de calcul que le cluster de taille X-Small par défaut, la génération des données prend environ la moitié du temps. Dans mon test, la déclaration terminée en 4 minutes. Si ce n’est pas assez rapide, essayez un cluster de taille moyenne, qui a exécuté l’instruction en 2 minutes dans mon test.

Run Worksheet 1

Enfin, voici un échantillon du jeu de données Snowflake, qui, comme prévu, est complètement aléatoire.

Run All Queries

Conclusion

Python offre un excellent support pour générer des données synthétiques via des packages tels que pydbgen et Faker. Pydbgen prend en charge la génération de données pour les types de données de base tels que nombre, chaîne et date, ainsi que pour les types conceptuels tels que SSN, plaque d’immatriculation, e-mail, etc. Cependant, la création de données à grande échelle nécessite une technologie beaucoup plus évolutive et élastique. Snowflake peut générer des milliards de lignes en quelques minutes et évolue de manière presque linéaire pour des ensembles de données suffisamment volumineux. Doubler la taille du cluster, par exemple, redimensionner le cluster de X-Small à Small, réduit de moitié le temps d’exécution.

Dans la partie 2 de ce billetje vais montrer comment gérer des types de données supplémentaires comme Date et horodatages et comment automatiser la génération des instructions SQL pour générer des données synthétiques purement basées sur un schéma (tables, colonnes et types de données) et des informations statistiques sur la distribution des données. Je décrirai également comment générer du SQL non seulement pour une table, mais pour un schéma complet, ainsi que des stratégies de couverture pour gérer les relations de clé étrangère.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.