web-dev-qa-db-fra.com

Comment puis-je me connecter à une base de données postgreSQL dans Apache Spark en utilisant scala?

Je veux savoir comment faire les choses suivantes en scala?

  1. Connectez-vous à une base de données postgreSQL en utilisant Spark scala.
  2. Écrivez des requêtes SQL comme SELECT, UPDATE etc. pour modifier une table dans cette base de données.

Je sais le faire en utilisant scala mais comment importer le pot de connecteur de psql scala dans sbt en le conditionnant?

37
febinsathar

Notre objectif est d'exécuter des requêtes SQL parallèles à partir des travailleurs Spark.

Configuration de la construction

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
)

Code

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:

  • Vous avez le SparkContext.
  • Ensuite, une fonction qui crée la connexion. Cela sera appelé sur chaque travailleur pour se connecter à la base de données.
  • Ensuite, la requête SQL. Cela doit être similaire à l'exemple et contenir des espaces réservés pour la clé de début et de fin.
  • Ensuite, vous spécifiez la plage de clés (0 à 1000 dans mon exemple) et le nombre de partitions. La plage sera divisée entre les partitions. Un thread exécuteur finira donc par exécuter SELECT * FROM FOO WHERE 0 <= KEY AND KEY <= 100 dans l'exemple.
  • Et enfin, nous avons une fonction qui convertit le 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).

Mises à jour

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!)

DataFrames prend en charge les mises à jour via les méthodes createJDBCTable et insertIntoJDBC.

43
Daniel Darabos