使用jdbc驱动程序读取大表的超时和内存不足错误

时间:2017-08-17 11:19:40

标签: oracle scala apache-spark jdbc

我正在尝试使用scala中的spark本地read.jdbc从Oracle数据库中读取一个大型表格到一个spark数据帧。我用中小型表(最多11M行)测试了它,它工作得很好。但是,当试图引入更大的表(大约70M行)时,我一直都会遇到错误。

示例代码,用于显示我如何阅读:

val df = sparkSession.read.jdbc(
   url = jdbcUrl,
   table = "( SELECT * FROM keyspace.table WHERE EXTRACT(year FROM date_column) BETWEEN 2012 AND 2016)"
      columnName = "id_column", // numeric column, 40% NULL
      lowerBound = 1L,
      upperBound = 100000L,
      numPartitions = 60, // same as number of cores
      connectionProperties = connectionProperties) // this contains login & password

我正在尝试并行操作,因为我正在使用一个具有60个内核和6 x 32GB RAM的群集专用于此应用程序。但是,我仍然遇到有关超时和内存不足问题的错误,例如:

17/08/16 14:01:18 WARN Executor: Issue communicating with driver in heartbeater
org.apache.spark.rpc.RpcTimeoutException: Futures timed out after [10 seconds]. This timeout is controlled by spark.executor.heartbeatInterval
at org.apache.spark.rpc.RpcTimeout.org$apache$spark$rpc$RpcTimeout$$createRpcTimeoutException(RpcTimeout.scala:47)
....

Caused by: java.util.concurrent.TimeoutException: Futures timed out after [10 seconds} 

...

17/08/16 14:17:14 ERROR RetryingBlockFetcher: Failed to fetch block rdd_2_89, and will not retry (0 retries)
    org.apache.spark.network.client.ChunkFetchFailureException: Failure while fetching StreamChunkId{streamId=398908024000, chunkIndex=0}: java.lang.IllegalArgumentException: Size exceeds Integer.MAX_VALUE
      at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:869)
      at org.apache.spark.storage.DiskStore$$anonfun$getBytes$4.apply(DiskStore.scala:125)
...


17/08/16 14:17:14 WARN BlockManager: Failed to fetch block after 1 fetch failures. Most recent failure cause:
org.apache.spark.SparkException: Exception thrown in awaitResult: 
at org.apache.spark.util.ThreadUtils$.awaitResult(ThreadUtils.scala:205)

群集中应该有足够多的RAM用于这种大小的表(我在本地表中读取的内容大10倍),所以我感觉由于某种原因读取的数据可能不会发生在平行?查看spark UI中的时间轴,我可以看到一个执行程序挂起并且正在计算'很长一段时间。现在,分区列中有很多NULL值(大约40%),但它是唯一的数字列(其他的是日期和字符串) - 这会有所不同吗?还有另一种方法可以并行化jdbc读取吗?

1 个答案:

答案 0 :(得分:0)

  

分区列中有很多NULL值(大约40%),但它是唯一的数字列(其他的是日期和字符串) - 这会有所不同吗?

它有很大的不同。 NULL will go to the last partition的所有值:

val whereClause =
  if (uBound == null) {
    lBound
  } else if (lBound == null) {
    s"$uBound or $column is null"
  } else {
    s"$lBound AND $uBound"
}
  

是否有其他方法可以并行化jdbc读取?

您可以将predicates与除数字列之外的其他列一起使用。例如,您可以在ROWID中使用table伪文本,并使用一系列基于前缀的谓词。