SparkのJdbcRDDを使っていてハマったので、メモを残します.
結論を最初にまとめると、以下2点に注意する必要が有ります.
結論1: >,<(超過、未満)を利用すると想定外の件数が取得される.バグの元となる.
結論2: =>,<=(以上、以下) を利用していても、lowerBound, upperBoundを正しく設定できていないとパーティション毎の件数にばらつきがでる.機能障害にはならなくても、性能問題となる可能性あり.
0.前提
JdbcRDDのドキュメントを読むと、以下のように記載がある.(パラメータの説明のみ和訳をつけている)
Instance Constructors
newJdbcRDD(sc: SparkContext, getConnection: () ⇒ Connection, sql: String, lowerBound:Long, upperBound: Long, numPartitions: Int, mapRow: (ResultSet) ⇒ T = …)(implicitarg0: ClassTag[T])
- getConnection
- a function that returns an open Connection. The RDD takes care of closing the connection.コネクションを開く関数.コネクションのクローズはRDDが行う.
- sql
- the text of the query. The query must contain two ? placeholders for parameters used to partition the results. E.g. “select title, author from books where ? <= id and id <= ?”テキスト形式のクエリ.クエリは取得結果をパーティション分割する際に利用する二つの?(クエスチョンマーク)プレイスホルダーを含まなければ成らない.
- lowerBound
- the minimum value of the first placeholder最初のプレイスホルダーに当てはまる最小値
- upperBound
- the maximum value of the second placeholder The lower and upper bounds are inclusive.2番目のプレイスホルダーに当てはまる最大値.最小値と最大値はその値を包括する(つまり、以上・以下を意味する).
- numPartitions
- the number of partitions. Given a lowerBound of 1, an upperBound of 20, and a numPartitions of 2, the query would be executed twice, once with (1, 10) and once with (11, 20)パーティション数.例えば、最小値が1、最大値が20、パーティション数が2の場合、クエリーは2回投げられる.1回目は1−10の値を、2回目は11−20の値を取得する.
- mapRow
- a function from a ResultSet to a single row of the desired result type(s). This should only call getInt, getString, etc; the RDD takes care of calling next. The default maps a ResultSet to an array of Object.ResultSetをRDDの1レコードの型に変換する関数.この関数はgetIntやgetString(いずれもResultSetのメソッド)のみを呼び出すべきである.nextを呼ぶのはRDDの役割である.デフォルトではResultSetをObjectの配列に変換する.
1.問題点(ハマったポイント)
5,000件のレコードが入っているテーブルから値を取得しようとした際、結果のレコード数が4,982行しか取得できなかった.
IDが1から5000までふられていたが、すべてのレコードを取得したいと思い、lowerBound, upperBoundにはそれぞれ−1、5001を設定していた.(広めにしておけば全量取得できると考えていた.)
mapRowとして渡している関数の呼び出し回数をカウントしても4,982回しか呼ばれていないことが確認出来たため、このコンストラクタに対して渡している値が不正だとあたりをつけた.
2.解決策
lowerBoundとupperBoundはドキュメントに記載のあるとおり、inclusive(包括的)に設定しなければ成らない.
つまり、sqlのwhere句では>,<(超過、未満)を利用してはならず、必ず =>,<=(以上、以下)を利用しなければならない.
>,<(超過、未満)を利用し、 id > 0 and id < 5001としたところ、取得件数は4982件となった.
→結論1: >,<(超過、未満)を利用すると想定外の件数が取得される.バグの元となる.
=>,<=(以上、以下)を利用し、id >= 0 and id < 5001 としたところ、取得件数は5000件となった.但し、パーティションのうち、1つ目が499件、4つ目が501件、残りが500件ずつとバラバラの値となった.
→結論2: =>,<=(以上、以下) を利用していても、lowerBound, upperBoundを正しく設定できていないとパーティション毎の件数にばらつきがでる.機能障害にはならなくても、性能問題となる可能性あり.
ドキュメントはちゃんと読んで、規定された使い方をしましょうという教訓でした.