Voici quelques points essentiels en ce qui concerne ma configuration:
En ré-exécutant un travail, je reçois des lignes en double dans redshift (comme prévu). Cependant, existe-t-il un moyen de remplacer ou de supprimer des lignes avant d'insérer les nouvelles données, à l'aide d'une clé ou de la configuration des partitions?
import sys
from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from pyspark.context import SparkContext
from awsglue.context import GlueContext
from awsglue.job import Job
from awsglue.dynamicframe import DynamicFrame
from awsglue.transforms import SelectFields
from pyspark.sql.functions import lit
## @params: [TempDir, JOB_NAME]
args = getResolvedOptions(sys.argv, ['TempDir','JOB_NAME'])
sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session
job = Job(glueContext)
job.init(args['JOB_NAME'], args)
columnMapping = [
("id", "int", "id", "int"),
("name", "string", "name", "string"),
]
datasource1 = glueContext.create_dynamic_frame.from_catalog(database = "db01", table_name = "table01", transformation_ctx = "datasource0")
applymapping1 = ApplyMapping.apply(frame = datasource1, mappings = columnMapping, transformation_ctx = "applymapping1")
resolvechoice1 = ResolveChoice.apply(frame = applymapping1, choice = "make_cols", transformation_ctx = "resolvechoice1")
dropnullfields1 = DropNullFields.apply(frame = resolvechoice1, transformation_ctx = "dropnullfields1")
df1 = dropnullfields1.toDF()
data1 = df1.withColumn('platform', lit('test'))
data1 = DynamicFrame.fromDF(data1, glueContext, "data_tmp1")
## Write data to redshift
datasink1 = glueContext.write_dynamic_frame.from_jdbc_conf(frame = data1, catalog_connection = "Test Connection", connection_options = {"dbtable": "table01", "database": "db01"}, redshift_tmp_dir = args["TempDir"], transformation_ctx = "datasink1")
job.commit()
C'est la solution que j'ai obtenue de l'assistance AWS Glue:
Comme vous le savez peut-être, bien que vous puissiez créer des clés primaires, Redshift n'impose pas l'unicité. Par conséquent, si vous réexécutez des travaux Glue, des lignes en double peuvent être insérées. Certains des moyens de maintenir l'unicité sont:
Utilisez une table intermédiaire pour insérer toutes les lignes, puis effectuez une opération upsert/fusion [1] dans la table principale. Cette opération doit être effectuée en dehors de la colle.
Ajoutez une autre colonne dans votre tableau redshift [1], comme un horodatage d'insertion, pour permettre la duplication, mais pour savoir laquelle est arrivée en premier ou en dernier, puis supprimez la duplication ultérieurement si vous en avez besoin.
Chargez les données précédemment insérées dans le cadre de données, puis comparez les données à insérer pour éviter d'insérer des doublons [3].
[1] - http://docs.aws.Amazon.com/redshift/latest/dg/c_best-practices-upsert.html et http://www.silota.com/blog/Amazon- redshift-upsert-support-staging-table-replace-rows/
[2] - https://github.com/databricks/spark-redshift/issues/238
[3] - https://docs.databricks.com/spark/latest/faq/join-two-dataframes-duplicated-column.html
Les signets de travail sont la clé. Il vous suffit de modifier le travail et d'activer les "signets de travail" pour éviter de traiter les données déjà traitées . Notez que le travail doit être relancé une fois avant de pouvoir être détecté.
Pour plus d'informations, voir: http://docs.aws.Amazon.com/glue/latest/dg/monitor-continuations.html
Le nom "bookmark" est un peu tiré par les cheveux, à mon avis. Je ne l'aurais jamais regardée si je n'avais pas coïncidé par hasard lors de ma recherche.
Aujourd'hui, j'ai testé et obtenu une solution de contournement pour mettre à jour/supprimer de la table cible à l'aide d'une connexion JDBC.
J'ai utilisé comme ci-dessous
import sys
from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from pyspark.context import SparkContext
from awsglue.context import GlueContext
from awsglue.job import Job
import pg8000
args = getResolvedOptions(sys.argv, [
'JOB_NAME',
'PW',
'Host',
'USER',
'DB'
])
# ...
# Create Spark & Glue context
sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session
job = Job(glueContext)
job.init(args['JOB_NAME'], args)
# ...
config_port = ****
conn = pg8000.connect(
database=args['DB'],
user=args['USER'],
password=args['PW'],
Host=args['Host'],
port=config_port
)
query = "UPDATE table .....;"
cur = conn.cursor()
cur.execute(query)
conn.commit()
cur.close()
query1 = "DELETE AAA FROM AAA A, BBB B WHERE A.id = B.id"
cur1 = conn.cursor()
cur1.execute(query1)
conn.commit()
cur1.close()
conn.close()
Veuillez vérifier ceci répondre. Des explications et des exemples de code expliquent comment transférer des données dans Redshift à l'aide d'une table intermédiaire. La même approche peut être utilisée pour exécuter des requêtes SQL avant ou après que Glue ait écrit des données en utilisant les options preactions
et postactions
:
// Write data to staging table in Redshift
glueContext.getJDBCSink(
catalogConnection = "redshift-glue-connections-test",
options = JsonOptions(Map(
"database" -> "conndb",
"dbtable" -> staging,
"overwrite" -> "true",
"preactions" -> "<another SQL queries>",
"postactions" -> "<some SQL queries>"
)),
redshiftTmpDir = tempDir,
transformationContext = "redshift-output"
).writeDynamicFrame(datasetDf)
L'option de bookmarking dans Glue devrait faire l'affaire, comme suggéré ci-dessus. Je l'utilise avec succès lorsque mon source est S3 . http://docs.aws.Amazon.com/glue/latest/dg/monitor-continuations.html
Selon mes tests (avec le même scénario), la fonctionnalité BOOKMARK ne fonctionne pas. Les données dupliquées sont insérées lorsque le travail est exécuté plusieurs fois. J'ai résolu ce problème en supprimant les fichiers de l'emplacement S3 quotidiennement (via lambda) et en implémentant les tables Staging & Target. les données seront insérées/mises à jour en fonction des colonnes clés correspondantes.