web-dev-qa-db-ja.com

DataFrameとJDBC接続を使用して遅いSparkジョブのパフォーマンスを向上させる方法は?

単一ノード(ローカル[*])でスタンドアロンモードのJDBCを介して中規模のTeradataテーブル(約1億行)にアクセスしようとしています。

私はSpark 1.4.1。を使用しており、非常に強力なマシン(2 cpu、24コア、126G RAM)でセットアップされています。

私はそれをより速く動作させるためにいくつかのメモリセットアップとチューニングオプションを試しましたが、どちらも大きな影響を与えませんでした。

足りないものがあると確信しています。以下は、この単純なカウントを取得するのに約11分かかった最後の試行ですが、Rを介したJDBC接続を使用してカウントを取得するのに40秒しかかかりませんでした。

bin/pyspark --driver-memory 40g --executor-memory 40g

df = sqlContext.read.jdbc("jdbc:teradata://......)
df.count()

BIGテーブル(5Bレコード)で試したところ、クエリの完了時に結果が返されませんでした。

8
Dev Patel

すべての集計操作は、データセット全体がメモリのDataFrameコレクションに取得された後に実行されます。したがって、Sparkでカウントを行うことは、TeraDataで直接行う場合ほど効率的ではありません。ビューを作成し、JDBCを使用してそれらのビューをマッピングすることにより、データベースに計算をプッシュする価値がある場合があります。 API。

JDBCドライバーを使用して大きなテーブルにアクセスするたびに、パーティション化戦略を指定する必要があります。そうしないと、単一のパーティションでDataFrame/RDDが作成され、単一のJDBC接続が過負荷になります。

代わりに、次のAIを試してみます(Spark 1.4.0+以降):

sqlctx.read.jdbc(
  url = "<URL>",
  table = "<TABLE>",
  columnName = "<INTEGRAL_COLUMN_TO_PARTITION>", 
  lowerBound = minValue,
  upperBound = maxValue,
  numPartitions = 20,
  connectionProperties = new Java.util.Properties()
)

一部のフィルタリングをプッシュダウンするオプションもあります。

均一に分散された積分列がない場合は、カスタム述語(whereステートメント)を指定してカスタムパーティションを作成します。たとえば、タイムスタンプ列があり、日付範囲でパーティション化するとします。

    val predicates = 
  Array(
    "2015-06-20" -> "2015-06-30",
    "2015-07-01" -> "2015-07-10",
    "2015-07-11" -> "2015-07-20",
    "2015-07-21" -> "2015-07-31"
  )
  .map {
    case (start, end) => 
      s"cast(DAT_TME as date) >= date '$start'  AND cast(DAT_TME as date) <= date '$end'"
  }

 predicates.foreach(println) 

// Below is the result of how predicates were formed 
//cast(DAT_TME as date) >= date '2015-06-20'  AND cast(DAT_TME as date) <= date '2015-06-30'
//cast(DAT_TME as date) >= date '2015-07-01'  AND cast(DAT_TME as date) <= date '2015-07-10'
//cast(DAT_TME as date) >= date '2015-07-11'  AND cast(DAT_TME as date) <= date //'2015-07-20'
//cast(DAT_TME as date) >= date '2015-07-21'  AND cast(DAT_TME as date) <= date '2015-07-31'


sqlctx.read.jdbc(
  url = "<URL>",
  table = "<TABLE>",
  predicates = predicates,
  connectionProperties = new Java.util.Properties()
)

DataFrameが生成され、各パーティションには、異なる述語に関連付けられた各サブクエリのレコードが含まれます。

DataFrameReader.scala でソースコードを確認してください

15

シリアル化されていないテーブルは40GBに収まりますか?ディスク上でスワッピングを開始すると、パフォーマンスが大幅に低下します。

とにかく、ansi SQL構文で標準のJDBCを使用する場合は、DBエンジンを利用するため、teradata(teradataはわかりません)がテーブルに関する統計を保持している場合、従来の「テーブルからのカウント(*)の選択」は非常に高速です。代わりに、sparkは、「select * from table」のようなものを使用して、1億行をメモリにロードし、RDD行のカウントを実行します。それはかなり異なるワークロードです。

5

他と異なる解決策の1つは、Oracleテーブルのデータをhadoopに保存されたavroファイル(多くのファイルに分割されている)に保存することです。このようにsparkでこれらのavroファイルを読み取ることは、dbをもう呼び出さないので、簡単です。

0
Vasile Surdu