如何在Parellel中从Spark中读取数据

时间:2016-08-11 20:44:16

标签: apache-spark apache-spark-sql spark-dataframe

我需要使用Spark SQL从DB2数据库读取数据(因为Sqoop不存在)

我知道这个函数会通过打开多个连接来读取parellel中的数据

jdbc(url: String, table: String, columnName: String, lowerBound: Long,upperBound: Long, numPartitions: Int, connectionProperties: Properties)

我的问题是我没有像这样增量的列。此外,我需要通过查询读取数据,因为我的表非常大。有没有人知道通过API读取数据的方法,或者我必须自己创建

3 个答案:

答案 0 :(得分:4)

您不需要标识列并行读取,table变量仅指定源。注册表后,您可以使用WHERE子句使用Spark SQL查询限制从中读取的数据。如果这不是一个选项,您可以使用视图,或者如此post中所述,您也可以使用任意子查询作为表输入。

val dataframe = sqlContext.read.format("jdbc").option("url", "jdbc:db2://localhost/sparksql").option("driver", "com.mysql.jdbc.Driver").option("dbtable", "table").option("user", "root").option("password", "root").load()
dataframe.registerTempTable("table")
dataframe.sqlContext.sql("select * from table where dummy_flag=1").collect.foreach(println)

答案 1 :(得分:4)

Saurabh,为了使用标准的Spark JDBC数据源支持并行读取,您确实需要使用numPartitions选项。

但是你需要给Spark一些线索如何将读取SQL语句拆分成多个并行的语句。因此,您需要某种整数分区列,其中您具有明确的最大值和最小值。

如果您的DB2系统是MPP分区的,那么已存在隐式分区,您实际上可以利用这一事实并并行读取每个DB2数据库分区:

var df = spark.read.
format("jdbc").
option("url", "jdbc:db2://<DB2 server>:<DB2 port>/<dbname>").
option("user", "<username>").
option("password", "<password>").
option("dbtable", "<your table>").
option("partitionColumn", "DBPARTITIONNUM(<a column name>)").
option("lowerBound", "<lowest partition number>").
option("upperBound", "<largest partition number>").
option("numPartitions", "<number of partitions>").
load()

因此,您可以看到DBPARTITIONNUM()函数是此处的分区键。

如果您不了解DB2 MPP系统的分区,以下是使用SQL找到它的方法:

SELECT min(member_number), max(member_number), count(member_number) 
FROM TABLE(SYSPROC.DB_MEMBERS())

如果您使用多个分区组并且不同的表可以分布在不同的分区集上,您可以使用此SQL来确定每个表的分区列表:

SELECT t2.DBPARTITIONNUM, t3.HOST_NAME
 FROM SYSCAT.TABLESPACES as t1,  SYSCAT.DBPARTITIONGROUPDEF as t2,
      SYSCAT.TABLES t4, TABLE(SYSPROC.DB_MEMBERS()) as t3 
 WHERE t1.TBSPACEID = t4.TBSPACEID AND
       t4.TABSCHEMA='<myschema>' AND
       t4.TABNAME='<mytab>' AND
       t1.DBPGNAME = t2.DBPGNAME AND
       t2.DBPARTITIONNUM = t3.PARTITION_NUMBER;

答案 2 :(得分:0)

如果您没有某种标识列,最好的选择是使用所谓的“谓词”选项(

https://spark.apache.org/docs/2.2.1/api/scala/index.html#org.apache.spark.sql.DataFrameReader@jdbc(url:String,table:String,predicates:Array[String],connectionProperties:java.util.Properties):org.apache.spark.sql.DataFrame

每个谓词都应该仅使用索引列构建,您应该尝试确保它们均匀分布。 Spark将为您提供的每个谓词创建一个任务,并根据可用的核心并行执行任意数量的任务。

我见过的典型方法是使用哈希函数将唯一字符串列转换为int,希望您的数据库支持(类似https://www.ibm.com/support/knowledgecenter/en/SSEPGG_9.7.0/com.ibm.db2.luw.sql.rtn.doc/doc/r0055167.html)。然后你可以把它分成像

这样的桶

mod(abs(yourhashfunction(yourstringid)),numOfBuckets)+ 1 = bucketNumber

如果你有复合唯一性,你可以在散列之前将它们连接起来。

您还可以通过附加匹配其他索引或分区的条件来改进谓词(即AND partitiondate = somemeaningfuldate)。

最后应该注意的是,这通常不如标识列那么好,因为它可能需要对目标索引进行完整或更广泛的扫描 - 但它仍然远远超过其他任何操作。