Objectif: Nous espérons utiliser AWS Glue Data Catalog pour créer une seule table pour les données JSON résidant dans un compartiment S3, que nous interrogerions et analyserions ensuite via Redshift Spectrum.
Background: Les données JSON proviennent de DynamoDB Streams et sont profondément imbriquées. Le premier niveau de JSON comporte un ensemble cohérent d'éléments: Keys, NewImage, OldImage, SequenceNumber, ApproximateCreationDateTime, SizeBytes et EventName. La seule variation est que certains enregistrements n'ont pas de NewImage et que certains n'ont pas de OldImage. En dessous de ce premier niveau, cependant, le schéma varie beaucoup.
Idéalement, nous aimerions utiliser Glue pour analyser uniquement ce premier niveau de JSON, et traiter les niveaux inférieurs comme des objets STRING volumineux (que nous analyserions ensuite au besoin avec Redshift Spectrum). Actuellement, nous chargeons l'intégralité de l'enregistrement dans une seule colonne VARCHAR dans Redshift, mais les enregistrements approchent de la taille maximale d'un type de données dans Redshift (la longueur maximale de VARCHAR est de 65535). En conséquence, nous aimerions effectuer ce premier niveau d’analyse avant que les enregistrements n’atteignent Redshift.
Ce que nous avons essayé/référencé jusqu'à présent:
Question: Comment pourrions-nous utiliser Glue (ou une autre méthode) pour nous permettre d’analyser uniquement le premier niveau de ces enregistrements, tout en ignorant les divers schémas situés au-dessous des éléments situés au niveau supérieur, afin de pouvoir y accéder depuis Spectrum ou le charger physiquement dans Redshift?
Je suis nouveau dans la colle. J'ai passé pas mal de temps dans la documentation de Glue et à parcourir les informations (plutôt rares) sur les forums. Il se peut que je manque quelque chose d'évident - ou peut-être s'agit-il d'une limitation de la colle dans sa forme actuelle. Toutes les recommandations sont les bienvenues.
Merci!
Je ne suis pas sûr que vous puissiez le faire avec une définition de table, mais vous pouvez le faire avec un travail ETL en utilisant une fonction de mappage pour convertir les valeurs de niveau supérieur en chaînes JSON. Documentation: [ link ]
import json
# Your mapping function
def flatten(rec):
for key in rec:
rec[key] = json.dumps(rec[key])
return rec
old_df = glueContext.create_dynamic_frame.from_options(
's3',
{"paths": ['s3://...']},
"json")
# Apply mapping function f to all DynamicRecords in DynamicFrame
new_df = Map.apply(frame=old_df, f=flatten)
À partir de là, vous avez la possibilité d’exporter vers S3 (peut-être dans Parquet ou un autre format de colonne à optimiser pour l’interrogation) ou directement vers Redshift, même si je n’ai pas essayé.
À compter du 20/12/2018, j'ai été en mesure de définir manuellement une table avec des champs JSON de premier niveau sous forme de colonnes de type STRING. Ensuite, dans le script de collage, le cadre dynamique contient la colonne sous forme de chaîne. À partir de là, vous pouvez effectuer une opération Unbox
de type json
sur les champs. Cela analysera les champs et en déduira le schéma réel. La combinaison de Unbox
avec Filter
vous permet de parcourir et de traiter des schémas json hétérogènes à partir de la même entrée si vous pouvez parcourir une liste de schémas.
Cependant, un mot d'avertissement, c'est incroyablement lent. Je pense que la colle télécharge les fichiers sources de s3 à chaque itération de la boucle. J'ai essayé de trouver un moyen de conserver les données source initiales, mais il semble que .toDF
dérive le schéma des champs de chaîne json, même si vous les spécifiez sous la forme de glue StringType. J'ajouterai un commentaire ici si je peux trouver une solution offrant de meilleures performances.
vous devriez ajouter un classificateur de colle de préférence $ [*]
Lorsque vous analysez le fichier JSON dans S3, il lit la première ligne du fichier.
Vous pouvez créer un travail de collage afin de charger la table de catalogue de données de ce fichier json dans le redshift.
Mon seul problème ici est que Redshift Spectrum a des problèmes pour lire les tables JSON dans le catalogue de données.
laissez-moi savoir si vous avez trouvé une solution
La procédure que j'ai trouvée utile pour json imbriqué peu profond:
ApplyMapping pour le premier niveau en tant que datasource0
;
Eclater les objets struct
ou array
pour supprimer le niveau d'élément df1 = datasource0.toDF().select(id,col1,col2,...,explode(coln).alias(coln)
, où explode
requiert from pyspark.sql.functions import explode
;
Sélectionnez les objets JSON que vous souhaitez conserver intacts via intact_json = df1.select(id, itct1, itct2,..., itctm)
;
Transformez df1
en dynamicFrame et relationalisez le DynamicFrame et supprimez les colonnes intactes de dataframe.drop_fields(itct1, itct2,..., itctm)
;
Joignez une table relationnelle à la table intacte en vous basant sur la colonne 'id'
Ceci est une limitation de la colle à partir de maintenant. Avez-vous jeté un coup d'oeil aux classificateurs de colle? C'est le seul article que je n'ai pas encore utilisé, mais qui pourrait répondre à vos besoins. Vous pouvez définir un chemin JSON pour un champ ou quelque chose comme ça.
Autre que cela - Glue Jobs sont la voie à suivre. C'est Spark en arrière-plan, vous pouvez donc pratiquement tout faire. Configurez un noeud final de développement et jouez avec. Je me suis heurté à divers obstacles au cours des trois dernières semaines et j'ai décidé de renoncer complètement à toutes les fonctionnalités de Glue et à Spark. Il est ainsi portable et fonctionne réellement.
N'oubliez pas, lors de la configuration du noeud final de développement, que le rôle IAM doit avoir un chemin d'accès "/". Vous devrez donc probablement créer manuellement un rôle distinct comportant ce chemin. Celui créé automatiquement a un chemin "/ service-role /".