Je veux savoir comment faire les choses suivantes en scala?
Je sais le faire en utilisant scala mais comment importer le pot de connecteur de psql scala dans sbt en le conditionnant?
Notre objectif est d'exécuter des requêtes SQL parallèles à partir des travailleurs Spark.
Ajoutez le connecteur et JDBC au libraryDependencies
dans build.sbt
. J'ai seulement essayé ceci avec MySQL, donc je vais l'utiliser dans mes exemples, mais Postgres devrait être à peu près le même.
libraryDependencies ++= Seq(
jdbc,
"mysql" % "mysql-connector-Java" % "5.1.29",
"org.Apache.spark" %% "spark-core" % "1.0.1",
// etc
)
Lorsque vous créez le SparkContext
, vous lui indiquez les fichiers à copier vers les exécuteurs. Inclure le pot de connecteur. Une belle façon de le faire:
val classes = Seq(
getClass, // To get the jar with our own code.
classOf[mysql.jdbc.Driver] // To get the connector.
)
val jars = classes.map(_.getProtectionDomain().getCodeSource().getLocation().getPath())
val conf = new SparkConf().setJars(jars)
Maintenant Spark est prêt à se connecter à la base de données. Chaque exécuteur exécutera une partie de la requête, de sorte que les résultats soient prêts pour le calcul distribué.
Il y a deux options pour cela. L'ancienne approche consiste à utiliser org.Apache.spark.rdd.JdbcRDD
:
val rdd = new org.Apache.spark.rdd.JdbcRDD(
sc,
() => {
sql.DriverManager.getConnection("jdbc:mysql://mysql.example.com/?user=batman&password=alfred")
},
"SELECT * FROM BOOKS WHERE ? <= KEY AND KEY <= ?",
0, 1000, 10,
row => row.getString("BOOK_TITLE")
)
Consultez la documentation des paramètres. Brièvement:
SparkContext
.SELECT * FROM FOO WHERE 0 <= KEY AND KEY <= 100
dans l'exemple.ResultSet
en quelque chose. Dans l'exemple, nous le convertissons en String
, donc vous vous retrouvez avec un RDD[String]
.Depuis Apache Spark version 1.3.0, une autre méthode est disponible via l'API DataFrame. Au lieu de JdbcRDD
, vous créez un org.Apache.spark.sql.DataFrame
:
val df = sqlContext.load("jdbc", Map(
"url" -> "jdbc:mysql://mysql.example.com/?user=batman&password=alfred",
"dbtable" -> "BOOKS"))
Voir https://spark.Apache.org/docs/1.3.1/sql-programming-guide.html#jdbc-to-other-databases pour la liste complète des options (la plage de clés et nombre de partitions peut être défini comme avec JdbcRDD
).
JdbcRDD
ne prend pas en charge les mises à jour. Mais vous pouvez simplement les faire dans un foreachPartition
.
rdd.foreachPartition { it =>
val conn = sql.DriverManager.getConnection("jdbc:mysql://mysql.example.com/?user=batman&password=alfred")
val del = conn.prepareStatement("DELETE FROM BOOKS WHERE BOOK_TITLE = ?")
for (bookTitle <- it) {
del.setString(1, bookTitle)
del.executeUpdate
}
}
(Cela crée une connexion par partition. Si cela vous inquiète, utilisez un pool de connexions!)
DataFrame
s prend en charge les mises à jour via les méthodes createJDBCTable
et insertIntoJDBC
.