Interroger Amazon Redshift à l'aide de Databricks
Vous pouvez lire et écrire des tables à partir d'Amazon Redshift avec Databricks.
Expérimental
La documentation sur la fédération des query héritées a été retirée et pourrait ne pas être mise à jour. Les configurations mentionnées dans ce contenu ne sont ni officiellement approuvées ni testées par Databricks. Si la Lakehouse Federation prend en charge votre base de données source, Databricks recommande d'utiliser cette dernière.
La source de données Databricks Redshift utilise Amazon S3 pour transférer efficacement les données vers et depuis Redshift et utilise JDBC pour déclencher automatiquement les commandes COPY et UNLOAD appropriées sur Redshift.
Dans Databricks Runtime 11.3 LTS et versions supérieures, Databricks Runtime inclut le Driver JDBC Redshift, accessible en utilisant le mot-clé redshift pour l'option de format. Consultez les notes de publication de Databricks Runtime, versions et compatibilité pour connaître les versions Driver incluses dans chaque Databricks Runtime. Les Drivers fournis par l'utilisateur sont toujours pris en charge et ont la priorité sur le Driver JDBC intégré.
Dans Databricks Runtime 10.4 LTS et versions inférieures, l'installation manuelle du Driver JDBC Redshift est requise, et les queries doivent utiliser le Driver (com.databricks.spark.redshift) pour le format. Voir l’installation du Driver Redshift.
Utilisation
Les exemples suivants démontrent la connexion avec le driver Redshift. Remplacez les valeurs du paramètre url si vous utilisez le driver JDBC PostgreSQL.
Une fois que vous avez configuré vos identifiants AWS, vous pouvez utiliser la source de données avec l'API de source de données Spark en Python, SQL, R ou Scala.
Les emplacements externes définis dans Unity Catalog ne sont pas pris en charge en tant qu'emplacements tempdir.
- Python
- SQL
- R
- Scala
# Read data from a table using Databricks Runtime 10.4 LTS and below
df = (spark.read
.format("redshift")
.option("dbtable", table_name)
.option("tempdir", "s3a://<bucket>/<directory-path>")
.option("url", "jdbc:redshift://<database-host-url>")
.option("user", username)
.option("password", password)
.option("forward_spark_s3_credentials", True)
.load()
)
# Read data from a table using Databricks Runtime 11.3 LTS and above
df = (spark.read
.format("redshift")
.option("host", "hostname")
.option("port", "port") # Optional - will use default port 5439 if not specified.
.option("user", "username")
.option("password", "password")
.option("database", "database-name")
.option("dbtable", "schema-name.table-name") # if schema-name is not specified, default to "public".
.option("tempdir", "s3a://<bucket>/<directory-path>")
.option("forward_spark_s3_credentials", True)
.load()
)
# Read data from a query
df = (spark.read
.format("redshift")
.option("query", "select x, count(*) <your-table-name> group by x")
.option("tempdir", "s3a://<bucket>/<directory-path>")
.option("url", "jdbc:redshift://<database-host-url>")
.option("user", username)
.option("password", password)
.option("forward_spark_s3_credentials", True)
.load()
)
# After you have applied transformations to the data, you can use
# the data source API to write the data back to another table
# Write back to a table
(df.write
.format("redshift")
.option("dbtable", table_name)
.option("tempdir", "s3a://<bucket>/<directory-path>")
.option("url", "jdbc:redshift://<database-host-url>")
.option("user", username)
.option("password", password)
.mode("error")
.save()
)
# Write back to a table using IAM Role based authentication
(df.write
.format("redshift")
.option("dbtable", table_name)
.option("tempdir", "s3a://<bucket>/<directory-path>")
.option("url", "jdbc:redshift://<database-host-url>")
.option("user", username)
.option("password", password)
.option("aws_iam_role", "arn:aws:iam::123456789000:role/redshift_iam_role")
.mode("error")
.save()
)
Lisez les données en utilisant SQL sur Databricks Runtime 10.4 LTS et versions antérieures :
DROP TABLE IF EXISTS redshift_table;
CREATE TABLE redshift_table
USING redshift
OPTIONS (
dbtable '<table-name>',
tempdir 's3a://<bucket>/<directory-path>',
url 'jdbc:redshift://<database-host-url>',
user '<username>',
password '<password>',
forward_spark_s3_credentials 'true'
);
SELECT * FROM redshift_table;
Lisez les données à l'aide de SQL sur Databricks Runtime 11.3 LTS et versions ultérieures :
DROP TABLE IF EXISTS redshift_table;
CREATE TABLE redshift_table
USING redshift
OPTIONS (
host '<hostname>',
port '<port>', /* Optional - will use default port 5439 if not specified. *./
user '<username>',
password '<password>',
database '<database-name>'
dbtable '<schema-name>.<table-name>', /* if schema-name not provided, default to "public". */
tempdir 's3a://<bucket>/<directory-path>',
forward_spark_s3_credentials 'true'
);
SELECT * FROM redshift_table;
Écrire des données à l'aide de SQL :
DROP TABLE IF EXISTS redshift_table;
CREATE TABLE redshift_table_new
USING redshift
OPTIONS (
dbtable '<new-table-name>',
tempdir 's3a://<bucket>/<directory-path>',
url 'jdbc:redshift://<database-host-url>',
user '<username>',
password '<password>',
forward_spark_s3_credentials 'true'
) AS
SELECT * FROM table_name;
L'API SQL prend en charge uniquement la création de nouvelles tables et non l'écrasement ou l'ajout.
Lisez les données à l'aide de R sur Databricks Runtime 10,4 LTS et versions antérieures :
df <- read.df(
NULL,
"com.databricks.spark.redshift",
tempdir = "s3a://<your-bucket>/<your-directory-path>",
dbtable = "<your-table-name>",
url = "jdbc:redshift://<the-rest-of-the-connection-string>")
Lire les données à l’aide de R sur Databricks Runtime 11.3 LTS et versions supérieures :
df <- read.df(
NULL,
"redshift",
host = "hostname",
port = "port",
user = "username",
password = "password",
database = "database-name",
dbtable = "schema-name.table-name",
tempdir = "s3a://<your-bucket>/<your-directory-path>",
forward_spark_s3_credentials = "true",
dbtable = "<your-table-name>")
// Read data from a table using Databricks Runtime 10.4 LTS and below
val df = spark.read
.format("redshift")
.option("dbtable", table_name)
.option("tempdir", "s3a://<bucket>/<directory-path>")
.option("url", "jdbc:redshift://<database-host-url>")
.option("user", username)
.option("password", password)
.option("forward_spark_s3_credentials", True)
.load()
// Read data from a table using Databricks Runtime 11.3 LTS and above
val df = spark.read
.format("redshift")
.option("host", "hostname")
.option("port", "port") /* Optional - will use default port 5439 if not specified. */
.option("user", "username")
.option("password", "password")
.option("database", "database-name")
.option("dbtable", "schema-name.table-name") /* if schema-name is not specified, default to "public". */
.option("tempdir", "s3a://<bucket>/<directory-path>")
.option("forward_spark_s3_credentials", true)
.load()
// Read data from a query
val df = spark.read
.format("redshift")
.option("query", "select x, count(*) <your-table-name> group by x")
.option("tempdir", "s3a://<bucket>/<directory-path>")
.option("url", "jdbc:redshift://<database-host-url>")
.option("user", username)
.option("password", password)
.option("forward_spark_s3_credentials", True)
.load()
// After you have applied transformations to the data, you can use
// the data source API to write the data back to another table
// Write back to a table
df.write
.format("redshift")
.option("dbtable", table_name)
.option("tempdir", "s3a://<bucket>/<directory-path>")
.option("url", "jdbc:redshift://<database-host-url>")
.option("user", username)
.option("password", password)
.mode("error")
.save()
// Write back to a table using IAM Role based authentication
df.write
.format("redshift")
.option("dbtable", table_name)
.option("tempdir", "s3a://<bucket>/<directory-path>")
.option("url", "jdbc:redshift://<database-host-url>")
.option("user", username)
.option("password", password)
.option("aws_iam_role", "arn:aws:iam::123456789000:role/redshift_iam_role")
.mode("error")
.save()
Recommandations pour travailler avec Redshift
L'exécution de la query peut extraire de grandes quantités de données vers S3. Si vous prévoyez d'effectuer plusieurs requêtes sur les mêmes données dans Redshift, Databricks recommande de sauvegarder les données extraites à l'aide de Delta Lake.
Vous ne devez pas créer de cluster Redshift à l’intérieur du Virtual Private Cloud (VPC) géré par Databricks, car cela peut entraîner des problèmes d’autorisations en raison du modèle de sécurité dans le Virtual Private Cloud (VPC) Databricks. Vous devriez créer votre propre Virtual Private Cloud (VPC) et ensuite effectuer un VPC peering pour connecter Databricks à votre instance Redshift.
Configuration
Authentification auprès de S3 et Redshift
La source de données implique plusieurs connexions réseau, illustrées dans le diagramme suivant :
┌───────┐
┌───────────────────>│ S3 │<─────────────────┐
│ IAM or keys └───────┘ IAM or keys │
│ ^ │
│ │ IAM or keys │
v v ┌──────v────┐
┌────────────┐ ┌───────────┐ │┌──────────┴┐
│ Redshift │ │ Spark │ ││ Spark │
│ │<──────────>│ Driver │<────────>| Executors │
└────────────┘ └───────────┘ └───────────┘
JDBC with Configured
username / in
password Spark
(SSL enabled by default)
La source de données lit et écrit des données vers S3 lors du transfert de données vers/depuis Redshift. Par conséquent, cela nécessite des informations d'identification AWS avec un accès en lecture et écriture à un compartiment S3 (spécifié à l'aide du paramètre de configuration tempdir).
La source de données ne nettoie pas les fichiers temporaires qu'elle crée dans S3. En conséquence, nous vous recommandons d'utiliser un compartiment S3 temporaire dédié avec une configuration de cycle de vie des objets pour garantir que les fichiers temporaires sont automatiquement supprimés après une période d'expiration spécifiée. Reportez-vous à la section Chiffrement de ce document pour une discussion sur la manière de chiffrer ces fichiers. Vous ne pouvez pas utiliser un emplacement externe défini dans Unity Catalog comme emplacement tempdir.
Les sections suivantes décrivent les options de configuration d'authentification de chaque connexion :
Driver Spark vers Redshift
Le driver Spark se connecte à Redshift via JDBC à l'aide d'un nom d'utilisateur et d'un mot de passe. Redshift ne prend pas en charge l'utilisation des rôles IAM pour authentifier cette connexion. Par défaut, cette connexion utilise le chiffrement SSL ; pour plus de détails, consultez Chiffrement.
Spark vers S3
S3 sert d'intermédiaire pour stocker des données en masse lors de la lecture ou de l'écriture dans Redshift. Spark se connecte à S3 en utilisant à la fois les interfaces Hadoop FileSystem et directement le client S3 du SDK Java d'Amazon.
Vous ne pouvez pas utiliser les montages DBFS pour configurer l'accès à S3 pour Redshift.
-
Chaîne de fournisseur d'identifiants par Default (meilleure option pour la plupart des utilisateurs) : Les identifiants AWS sont automatiquement récupérés via le DefaultAWSCredentialsProviderChain. Si vous utilisez des profils d'instance pour vous authentifier auprès de S3, vous devriez probablement utiliser cette méthode.
Les méthodes suivantes de fourniture d'identifiants ont préséance sur cette valeur par default.
-
En assumant un rôle IAM : Vous pouvez utiliser un rôle IAM que le profil d'instance peut assumer. Pour spécifier l'ARN du rôle, vous devez attacher un profil d'instance au cluster et fournir les clés de configuration suivantes :
- Scala
- Python
sc.hadoopConfiguration.set("fs.s3a.credentialsType", "AssumeRole")
sc.hadoopConfiguration.set("fs.s3a.stsAssumeRole.arn", <iam-role-arn-to-be-assumed>)
// An optional duration, expressed as a quantity and a unit of
// time, such as "15m" or "1h"
sc.hadoopConfiguration.set("fs.s3a.assumed.role.session.duration", <duration>)
sc._jsc.hadoopConfiguration().set("fs.s3a.credentialsType", "AssumeRole")
sc._jsc.hadoopConfiguration().set("fs.s3a.stsAssumeRole.arn", <iam-role-arn-to-be-assumed>)
# An optional duration, expressed as a quantity and a unit of
# time, such as "15m" or "1h"
sc._jsc.hadoopConfiguration().set("fs.s3a.assumed.role.session.duration", <duration>)
- Définir les clés dans la configuration Hadoop : vous pouvez spécifier les clés AWS à l'aide des propriétés de configuration Hadoop. Si votre configuration
tempdirpointe vers un système de fichierss3a://, vous pouvez définir les propriétésfs.s3a.access.keyetfs.s3a.secret.keydans un fichier de configuration XML Hadoop ou appelersc.hadoopConfiguration.set()pour configurer la configuration Hadoop globale de Spark. Si vous utilisez un système de fichierss3n://, vous pouvez fournir les clés de configuration héritées comme illustré dans l'exemple suivant.
- Scala
- Python
Par exemple, si vous utilisez le système de fichiers s3a, ajoutez :
sc.hadoopConfiguration.set("fs.s3a.access.key", "<your-access-key-id>")
sc.hadoopConfiguration.set("fs.s3a.secret.key", "<your-secret-key>")
Pour l'ancien système de fichiers s3n, ajoutez :
sc.hadoopConfiguration.set("fs.s3n.awsAccessKeyId", "<your-access-key-id>")
sc.hadoopConfiguration.set("fs.s3n.awsSecretAccessKey", "<your-secret-key>")
La commande suivante s'appuie sur des mécanismes internes de Spark, mais devrait fonctionner avec toutes les versions de PySpark et il est peu probable qu'elle change à l'avenir :
sc._jsc.hadoopConfiguration().set("fs.s3a.access.key", "<your-access-key-id>")
sc._jsc.hadoopConfiguration().set("fs.s3a.secret.key", "<your-secret-key>")
De Redshift à S3
Redshift se connecte également à S3 pendant les queries COPY et UNLOAD. Il existe trois méthodes d'authentification de cette connexion :
-
Demander à Redshift d’assumer un rôle IAM (le plus sécurisé) : vous pouvez accorder à Redshift l’autorisation d’assumer un rôle IAM pendant les
COPYouUNLOADopérations, puis configurer la source de données pour demander à Redshift d’utiliser ce rôle :- Créez un rôle IAM accordant les autorisations S3 appropriées à votre compartiment.
- Suivez le guide Autorisation pour Amazon Redshift d'accéder à d'autres services AWS en votre nom pour configurer la stratégie d'approbation de ce rôle afin de permettre à Redshift d'assumer ce rôle.
- Suivez les étapes du guide Autorisation des Opérations COPY et UNLOAD à l'aide des rôles IAM pour associer ce rôle IAM à votre cluster Redshift.
- Définissez l'option
aws_iam_rolede la source de données sur l'ARN du rôle.
-
Transmettre les informations d'identification S3 de Spark à Redshift : si l'option
forward_spark_s3_credentialsest définie surtrue, alors la source de données découvre automatiquement les informations d'identification que Spark utilise pour se connecter à S3 et les transmet à Redshift via JDBC. Si Spark s'authentifie auprès de S3 à l'aide d'un profil d'instance, un ensemble d'informations d'identification STS temporaires est transmis à Redshift ; sinon, les clés AWS sont transmises. La query JDBC intègre ces informations d'identification, c'est pourquoi Databricks vous recommande vivement d'activer le chiffrement SSL de la connexion JDBC lorsque vous utilisez cette méthode d'authentification. -
Utilisez les informations d'identification du Security Token Service (STS) : vous pouvez configurer les propriétés de configuration
temporary_aws_access_key_id,temporary_aws_secret_access_keyettemporary_aws_session_tokenpour qu'elles pointent vers des clés temporaires créées via l'AWS Security Token Service. La query JDBC intègre ces identifiants, c'est pourquoi il est **fortement recommandé** d'activer le chiffrement SSL de la connexion JDBC lorsque vous utilisez cette méthode d'authentification. Si vous choisissez cette option, veuillez prendre conscience du risque que les identifiants expirent avant que l'opération de lecture/écriture ne réussisse.
Ces trois options s’excluent mutuellement, et vous devez choisir explicitement celle à utiliser.
Chiffrement
-
Sécurisation de JDBC : À moins que des paramètres liés à SSL ne soient présents dans l'URL JDBC, la source de données active by default le chiffrement SSL et vérifie également que le serveur Redshift est fiable (c'est-à-dire,
sslmode=verify-full). Pour cela, un certificat de serveur est automatiquement download à partir des serveurs Amazon la première fois qu'il est nécessaire. En cas d'échec, un fichier de certificat pré-assemblé est utilisé comme fallback. Ceci s'applique aux drivers JDBC Redshift et PostgreSQL.Si vous rencontrez des problèmes avec cette fonctionnalité, ou si vous souhaitez simplement désactiver SSL, vous pouvez appeler
.option("autoenablessl", "false")sur votreDataFrameReaderouDataFrameWriter.Si vous souhaitez spécifier des paramètres liés à SSL personnalisés, vous pouvez suivre les instructions de la documentation Redshift : Utilisation de SSL et des certificats de serveur en Java et Options de configuration du Driver JDBC Toutes les options liées à SSL présentes dans le
urlJDBC utilisé avec la source de données priment (c'est-à-dire que l'auto-configuration ne se déclenchera pas). -
Chiffrement des données UNLOAD stockées dans S3 (données stockées lors de la lecture depuis Redshift) : Selon la documentation Redshift sur le déchargement des données vers S3, « UNLOAD chiffre automatiquement les fichiers de données à l’aide du chiffrement côté serveur Amazon S3 (SSE-S3). »
Redshift prend également en charge le chiffrement côté client avec une clé personnalisée (voir : Déchargement de fichiers de données chiffrés) mais la source de données ne permet pas de spécifier la clé symétrique requise.
-
Chiffrement des données COPY stockées dans S3 (données stockées lors de l'écriture dans Redshift) : Selon la documentation Redshift sur le Chargement de fichiers de données chiffrés depuis Amazon S3:
Vous pouvez utiliser la commande COPY pour charger des fichiers de données qui ont été upload vers Amazon S3 en utilisant le chiffrement côté serveur avec des clés de chiffrement gérées par AWS (SSE-S3 ou SSE-KMS), le chiffrement côté client, ou les deux. COPY ne prend pas en charge le chiffrement côté serveur Amazon S3 avec une clé fournie par le client (SSE-C).
Pour utiliser cette fonctionnalité, configurez votre système de fichiers Hadoop S3 pour utiliser le chiffrement Amazon S3. Ceci ne chiffrera pas le fichier MANIFEST qui contient une liste de tous les fichiers écrits.
parameter
La carte des paramètres ou les OPTIONS fournies dans Spark SQL prennent en charge les paramètres suivants :
parameter | Obligatoire | Par défaut | Description |
|---|---|---|---|
dbtable | Oui, sauf si la query est spécifiée. | Aucun | La table à créer ou à lire dans Redshift. Ce parameter est requis lors de l'enregistrement des données dans Redshift. |
Saisir une requête | Oui, sauf si dbtable est spécifié. | Aucun | La query à lire dans Redshift. |
Utilisateur | Non | Aucun | Le nom d'utilisateur Redshift. Doit être utilisé conjointement avec l'option de mot de passe. Ne peut être utilisé que si l'utilisateur et le mot de passe ne sont pas transmis dans l'URL, le fait de transmettre les deux entraînera une erreur. Utilisez ce paramètre lorsque le nom d'utilisateur contient des caractères spéciaux qui doivent être échappés. |
Mot de passe | Non | Aucun | Le mot de passe Redshift. Doit être utilisé conjointement avec l'option |
URL | Oui | Aucun | Une URL JDBC, du format
|
search_path | Non | Aucun | Définir le chemin de recherche de schéma dans Redshift. Sera défini à l'aide de la commande |
aws_iam_role | Uniquement si vous utilisez des rôles IAM pour autoriser. | Aucun | ARN entièrement spécifié du rôle d'opérations IAM Redshift COPY/UNLOAD attaché au cluster Redshift. Par exemple, |
forward_spark_s3_credentials | Non |
| Si |
temporary_aws_access_key_id | Non | Aucun | Clé d'accès AWS, doit disposer d'autorisations d'écriture sur le compartiment S3. |
temporary_aws_secret_access_key | Non | Aucun | Clé d'accès secrète AWS correspondant à la clé d'accès fournie. |
temporary_aws_session_token | Non | Aucun | Jeton de session AWS correspondant à la clé d’accès fournie. |
tempdir | Oui | Aucun | Un emplacement accessible en écriture dans Amazon S3, à utiliser pour les données déchargées lors de la lecture et les données Avro à charger dans Redshift lors de l'écriture. Si vous utilisez la source de données Redshift pour Spark dans le cadre d'un pipeline ETL régulier, il peut être utile de définir une politique de cycle de vie sur un bucket et de l'utiliser comme emplacement temporaire pour ces données. Vous ne pouvez pas utiliser des emplacements externes définis dans Unity Catalog comme emplacements |
jdbcdriver | Non | Déterminé par le sous-protocole de l'URL JDBC. | Le nom de classe du Driver JDBC à utiliser. Cette classe doit se trouver dans le classpath. Dans la plupart des cas, il ne devrait pas être nécessaire de spécifier cette option, car le nom de classe Driver approprié devrait être automatiquement déterminé par le sous-protocole de l’URL JDBC. |
diststyle | Non |
| Le Redshift style de distribution à utiliser lors de la création d'une table. Peut être l'un des |
distkey | Non, sauf si | Aucun | Le nom d'une colonne dans la table à utiliser comme clé de distribution lors de la création d'une table. |
sortkeyspec | Non | Aucun | Une définition complète de la clé de tri Redshift. Exemples :
|
usestagingtable (Obsolète) | Non |
| Définir cette option obsolète sur Puisque la définition de l'opération |
Description | Non | Aucun | Une description pour la table. Sera défini à l'aide de la commande SQL COMMENT, et devrait apparaître dans la plupart des outils de query. Voir aussi les métadonnées |
préactions | Non | Aucun | Une liste de commandes SQL séparées par Sachez que si ces commandes échouent, elles sont considérées comme une erreur et une exception est levée. Si vous utilisez une table de préproduction, les modifications sont annulées et la table de sauvegarde est restaurée si les actions préalables échouent. |
Post-actions | Non | Aucun | Une liste de commandes SQL séparées par Sachez que si ces commandes échouent, elles sont considérées comme une erreur et une exception est levée. Si vous utilisez une table de staging, les modifications sont annulées et la table de sauvegarde restaurée si les actions post-exécution échouent. |
extracopyoptions | Non | Aucun | Une liste d'options supplémentaires à ajouter à la commande Redshift Étant donné que ces options sont ajoutées à la fin de la commande |
tempformat | Non |
| Le format dans lequel enregistrer les fichiers temporaires dans S3 lors de l'écriture dans Redshift. La valeur par default est Redshift est significativement plus rapide lors du chargement de fichiers CSV que lors du chargement de fichiers Avro, de sorte que l'utilisation de ce format temporaire peut considérablement améliorer les performances lors de l'écriture vers Redshift. |
csvnullstring | Non |
| La valeur de chaîne à écrire pour les valeurs nulles lors de l'utilisation du format temporaire CSV. Ceci devrait être une valeur qui n'apparaît pas dans vos données réelles. |
csvseparator | Non |
| Séparateur à utiliser lors de l'écriture de fichiers temporaires avec le format temporaire défini sur |
csvignoreleadingwhitespace | Non |
| Lorsque la valeur est true, supprime les espaces blancs en début de ligne des valeurs lors des écritures lorsque |
csvignoretrailingwhitespace | Non |
| Lorsqu'il est défini sur vrai, supprime les espaces de fin des valeurs lors des écritures lorsque |
infer_timestamp_ntz_type | Non |
| Si |
Options de configuration supplémentaires
Configuration de la taille maximale des colonnes de chaîne
Lors de la création de tables Redshift, le comportement par default est de créer TEXT colonnes pour les colonnes de chaînes de caractères. Redshift stocke les colonnes TEXT en tant que VARCHAR(256), donc ces colonnes ont une taille maximale de 256 caractères (source).
Pour prendre en charge des colonnes plus grandes, vous pouvez utiliser le champ de métadonnées de colonne maxlength pour spécifier la longueur maximale des colonnes de chaînes individuelles. Ceci est également utile pour implémenter des optimisations de performances permettant de gagner de l'espace en déclarant des colonnes avec une longueur maximale inférieure à la valeur par default.
En raison des limitations de Spark, les APIs de langage SQL et R ne prennent pas en charge la modification des métadonnées de colonne.
- Python
- Scala
df = ... # the dataframe you'll want to write to Redshift
# Specify the custom width of each column
columnLengthMap = {
"language_code": 2,
"country_code": 2,
"url": 2083,
}
# Apply each column metadata customization
for (colName, length) in columnLengthMap.iteritems():
metadata = {'maxlength': length}
df = df.withColumn(colName, df[colName].alias(colName, metadata=metadata))
df.write \
.format("com.databricks.spark.redshift") \
.option("url", jdbcURL) \
.option("tempdir", s3TempDirectory) \
.option("dbtable", sessionTable) \
.save()
Voici un exemple de mise à jour des champs de métadonnées de plusieurs colonnes à l'aide de l'API Scala de Spark :
import org.apache.spark.sql.types.MetadataBuilder
// Specify the custom width of each column
val columnLengthMap = Map(
"language_code" -> 2,
"country_code" -> 2,
"url" -> 2083
)
var df = ... // the dataframe you'll want to write to Redshift
// Apply each column metadata customization
columnLengthMap.foreach { case (colName, length) =>
val metadata = new MetadataBuilder().putLong("maxlength", length).build()
df = df.withColumn(colName, df(colName).as(colName, metadata))
}
df.write
.format("com.databricks.spark.redshift")
.option("url", jdbcURL)
.option("tempdir", s3TempDirectory)
.option("dbtable", sessionTable)
.save()
Définir un type de colonne personnalisé
Si vous devez définir manuellement un type de colonne, vous pouvez utiliser les métadonnées de colonne redshift_type. Par exemple, si vous souhaitez remplacer le comparateur de type Spark SQL Schema -> Redshift SQL pour attribuer un type de colonne défini par l'utilisateur, vous pouvez faire ce qui suit :
- Python
- Scala
# Specify the custom type of each column
columnTypeMap = {
"language_code": "CHAR(2)",
"country_code": "CHAR(2)",
"url": "BPCHAR(111)",
}
df = ... # the dataframe you'll want to write to Redshift
# Apply each column metadata customization
for colName, colType in columnTypeMap.items():
metadata = {'redshift_type': colType}
df = df.withColumn(colName, df[colName].alias(colName, metadata=metadata))
import org.apache.spark.sql.types.MetadataBuilder
// Specify the custom type of each column
val columnTypeMap = Map(
"language_code" -> "CHAR(2)",
"country_code" -> "CHAR(2)",
"url" -> "BPCHAR(111)"
)
var df = ... // the dataframe you'll want to write to Redshift
// Apply each column metadata customization
columnTypeMap.foreach { case (colName, colType) =>
val metadata = new MetadataBuilder().putString("redshift_type", colType).build()
df = df.withColumn(colName, df(colName).as(colName, metadata))
}
Configurer l'encodage des colonnes
Lorsque vous créez une table, utilisez le champ de métadonnées de colonne encoding pour spécifier un encodage de compression pour chaque colonne (consultez les docs Amazon pour les encodages disponibles).
Définir des descriptions sur les colonnes
Redshift permet aux colonnes d’avoir des descriptions jointes qui devraient apparaître dans la plupart des outils de query (à l’aide de la commande COMMENT). Vous pouvez définir le champ de métadonnées de colonne description pour spécifier une description pour les colonnes individuelles.
Query pushdown dans Redshift
L'optimiseur Spark transfère les opérateurs suivants dans Redshift :
FilterProjectSortLimitAggregationJoin
Dans Project et Filter, il prend en charge les expressions suivantes :
- La plupart des opérateurs logiques booléens
- Comparaisons
- Opérations arithmétiques de base
- Conversions numériques et de chaînes
- La plupart des fonctions de chaîne
- Sous-requêtes scalaires, si elles peuvent être entièrement transférées dans Redshift.
Ce pushdown ne prend pas en charge les expressions opérant sur les dates et les Timestamp.
Au sein de Aggregation, elle prend en charge les fonctions d'agrégation suivantes :
AVGCOUNTMAXMINSUMSTDDEV_SAMPSTDDEV_POPVAR_SAMPVAR_POP
combiné(e) avec la clause DISTINCT, le cas échéant.
Dans Join, il prend en charge les types de jointures suivants :
INNER JOINLEFT OUTER JOINRIGHT OUTER JOINLEFT SEMI JOINLEFT ANTI JOIN- Sous-requêtes réécrites en
Joinpar l'optimiseur, par exemple,WHERE EXISTS,WHERE NOT EXISTS
L'optimisation des jointures (pushdown) ne prend pas en charge FULL OUTER JOIN.
Le pushdown pourrait être le plus bénéfique dans les requêtes avec LIMIT. Une requête telle que SELECT * FROM large_redshift_table LIMIT 10 pourrait prendre beaucoup de temps, car la table entière serait d’abord UNLOADed vers S3 en tant que résultat intermédiaire. Avec le pushdown, le LIMIT est exécuté dans Redshift. Dans les requêtes avec agrégations, le pushdown de l’agrégation vers Redshift contribue également à réduire la quantité de données à transférer.
La poussée de query dans Redshift est activée par default. Il peut être désactivé en définissant spark.databricks.redshift.pushdown sur false. Même lorsqu'il est désactivé, Spark pousse toujours les filtres et effectue l'élimination de colonnes dans Redshift.
Installation du Driver Redshift
La source de données Redshift nécessite également un Driver JDBC compatible avec Redshift. Étant donné que Redshift est basé sur le système de base de données PostgreSQL, vous pouvez utiliser le Driver JDBC PostgreSQL inclus avec Databricks Runtime ou le Driver JDBC Redshift recommandé par Amazon. Aucune installation n'est requise pour utiliser le Driver JDBC PostgreSQL. La version du Driver JDBC PostgreSQL incluse dans chaque version de Databricks Runtime est indiquée dans les notes de version de Databricks Runtime.
Pour installer manuellement le Driver JDBC Redshift :
- Download le driver depuis Amazon.
- Upload le driver vers votre Databricks Workspace. Consultez Installer les bibliothèques.
- Installez la bibliothèque sur votre cluster.
Databricks recommande d'utiliser la dernière version du Driver JDBC Redshift. Les versions du Driver JDBC Redshift inférieures à 1.2.41 présentent les limitations suivantes :
- La version 1.2.16 du Driver retourne des données vides lors de l'utilisation d'une clause
wheredans une query SQL. - Les versions du Driver inférieures à 1.2.41 peuvent renvoyer des résultats non valides, car la nullité d'une colonne est signalée de manière incorrecte comme « Non-nullable » au lieu de « Inconnu ».
Garanties transactionnelles
Cette section décrit les garanties transactionnelles de la source de données Redshift pour Spark.
Contexte général sur les propriétés Redshift et S3
Pour des information générales sur les garanties transactionnelles de Redshift, consultez le chapitre Managing Concurrent Write Opérations dans la documentation Redshift. En bref, Redshift fournit une isolation sérialisable selon la documentation de la commande BEGIN de Redshift :
[Bien que] vous puissiez utiliser l'un des quatre niveaux d'isolation des transactions, Amazon Redshift traite tous les niveaux d'isolation comme sérialisables.
Selon la documentation Redshift:
Amazon Redshift prend en charge un comportement de commit automatique par default dans lequel chaque commande SQL exécutée séparément est commit individuellement.
Ainsi, les commandes individuelles comme COPY et UNLOAD sont atomiques et transactionnelles, tandis que les BEGIN et END explicites ne devraient être nécessaires que pour garantir l'atomicité de plusieurs commandes ou query.
Lors de la lecture et de l'écriture vers Redshift, la source de données lit et écrit des données dans S3. Spark et Redshift produisent tous deux une sortie partitionnée et la stockent dans plusieurs fichiers dans S3. Selon la documentation du modèle de cohérence des données Amazon S3, les Opérations de listage des buckets S3 sont éventuellement cohérentes, donc les fichiers doivent faire l'objet de mesures spéciales pour éviter les données manquantes ou incomplètes en raison de cette source de cohérence éventuelle.
Garanties de la source de données Redshift pour Spark
Ajouter à une table existante
Lors de l’insertion de lignes dans Redshift, la source de données utilise la commande COPY et spécifie des manifests pour se prémunir contre certaines opérations S3 à cohérence à terme. Par conséquent, les ajouts spark-redshift aux tables existantes ont les mêmes propriétés atomiques et transactionnelles que les commandes Redshift COPY classiques.
Créer une nouvelle table (SaveMode.CreateIfNotExists)
La création d’une nouvelle table est un processus en deux étapes, consistant en une commande CREATE TABLE suivie d’une commande COPY pour ajouter l’ensemble initial de lignes. Les deux Opérations sont effectuées dans la même transaction.
Écraser une table existante
Par default, la source de données utilise des transactions pour effectuer des remplacements, qui sont implémentés en supprimant la table de destination, en créant une nouvelle table vide et en y ajoutant des lignes.
Si le paramètre déprécié usestagingtable est défini sur false, la source de données commit la commande DELETE TABLE avant d'ajouter des lignes à la nouvelle table, sacrifiant l'atomicité de l'opération d'écrasement mais réduisant la quantité d'espace de staging dont Redshift a besoin pendant l'écrasement.
Interroger la table Redshift
Les queries utilisent la commande Redshift UNLOAD pour exécuter une query et sauvegarder ses résultats dans S3 et utilisent des manifests pour se prémunir contre certaines opérations S3 éventuellement cohérentes. Par conséquent, les requêtes issues de la source de données Redshift pour Spark doivent avoir les mêmes propriétés de cohérence que les requêtes Redshift régulières.
Problèmes courants et solutions
Le compartiment S3 et le cluster Redshift se trouvent dans des régions AWS différentes
Par default, les copies S3 <-> Redshift ne fonctionnent pas si le bucket S3 et le cluster Redshift se trouvent dans des régions AWS différentes.
Si vous tentez de lire une table Redshift lorsque le compartiment S3 se trouve dans une autre région, vous pouvez voir une erreur telle que :
ERROR: S3ServiceException:The S3 bucket addressed by the query is in a different region from this cluster.,Status 301,Error PermanentRedirect.
De même, tenter d'écrire dans Redshift en utilisant un compartiment S3 dans une autre région peut entraîner l'erreur suivante :
error: Problem reading manifest file - S3ServiceException:The S3 bucket addressed by the query is in a different region from this cluster.,Status 301,Error PermanentRedirect
-
Écritures : La commande COPY de Redshift prend en charge la spécification explicite de la région du bucket S3, afin que vous puissiez faire fonctionner correctement les écritures vers Redshift dans ces cas en ajoutant
region 'the-region-name'au paramètreextracopyoptions. Par exemple, avec un bucket dans la région Est des États-Unis (Virginie) et l'API Scala, utilisez :Scala.option("extracopyoptions", "region 'us-east-1'")Vous pouvez alternativement utiliser le paramètre
awsregion:Scala.option("awsregion", "us-east-1") -
**Lectures :** La commande Redshift UNLOAD prend également en charge la spécification explicite de la région du bucket S3. Vous pouvez faire en sorte que les lectures fonctionnent correctement en ajoutant la région au paramètre
awsregion:Scala.option("awsregion", "us-east-1")
Erreur de connexion S3ServiceException inattendue lorsque vous utilisez des profils d'instance pour vous authentifier auprès de S3
Si vous utilisez des profils d'instance pour vous authentifier auprès de S3 et recevez une erreur S3ServiceException inattendue, vérifiez si les clés d'accès AWS sont spécifiées dans l'URI S3 tempdir, dans les configurations Hadoop ou dans l'une des sources vérifiées par la DefaultAWSCredentialsProviderChain: ces sources ont priorité sur les identifiants de profil d'instance.
Voici un exemple de message d'erreur qui peut être le symptôme de clés prenant accidentellement le pas sur les profils d'instance :
com.amazonaws.services.s3.model.AmazonS3Exception: The AWS Access Key Id you provided does not exist in our records. (Service: Amazon S3; Status Code: 403; Error Code: InvalidAccessKeyId;
Erreur d'authentification lors de l'utilisation d'un mot de passe avec des caractères spéciaux dans l'URL JDBC
Si vous fournissez le nom d'utilisateur et le mot de passe dans l'URL JDBC et que le mot de passe contient des caractères spéciaux tels que ;, ? ou &, l'exception suivante peut s'afficher :
java.sql.SQLException: [Amazon](500310) Invalid operation: password authentication failed for user 'xyz'
Ceci est dû à des caractères spéciaux dans le nom d'utilisateur ou le mot de passe qui ne sont pas échappés correctement par le Driver JDBC. Assurez-vous de spécifier le nom d'utilisateur et le mot de passe en utilisant les options DataFrame correspondantes user et password. Pour plus d’informations, consultez Parameters.
La query Spark de longue durée se bloque indéfiniment, même si l’opération Redshift correspondante est terminée
Si vous lisez ou écrivez de grandes quantités de données depuis et vers Redshift, votre query Spark peut se bloquer indéfiniment, même si la page AWS Redshift monitoring indique que l'Opérations LOAD ou UNLOAD correspondante est terminée et que le cluster est inactif. Ceci est dû à l'expiration du délai d'attente de la connexion entre Redshift et Spark. Pour éviter cela, assurez-vous que l'indicateur JDBC tcpKeepAlive est activé et que TCPKeepAliveMinutes est défini sur une valeur basse (par exemple, 1).
Pour plus d'informations, consultez la configuration du Driver JDBC Amazon Redshift.
Timestamp avec sémantique de fuseau horaire
Lors de la lecture de données, les types de données Redshift TIMESTAMP et TIMESTAMPTZ sont mappés à Spark TimestampType, et une valeur est convertie en temps universel coordonné (UTC) et stockée en tant que timestamp UTC. Pour un Redshift TIMESTAMP, le fuseau horaire local est supposé car la valeur n'a pas d'information de fuseau horaire. Lors de l'écriture de données dans une table Redshift, un Spark TimestampType est mappé au type de données Redshift TIMESTAMP.
Guide de migration
La source de données vous oblige maintenant à définir explicitement forward_spark_s3_credentials avant que les identifiants Spark S3 ne soient transférés à Redshift. Ce changement n'a aucun impact si vous utilisez les mécanismes d'authentification aws_iam_role ou temporary_aws_*. Cependant, si vous vous êtes appuyé sur l'ancien comportement par default, vous devez maintenant définir explicitement forward_spark_s3_credentials sur true pour continuer à utiliser votre précédent mécanisme d'authentification de Redshift à S3. Pour une discussion sur les trois mécanismes d'authentification et leurs compromis en matière de sécurité, consultez la section Authentification à S3 et Redshift de ce document.