複合主キーのテーブルをSqoopで並列転送する


はじめに

この記事は MicroAd Advent Calendar 2020 22日目の記事です。  
業務でApache Sqoopを使って複合主キーのテーブルを転送する際に工夫したことを記事にします。

状況設定

やりたかったことは以下の通りです。

  • レコード数が多いMySQLのテーブルをCDHに転送する
  • レコード数が多いため、レコードを分割して並列に転送しないとメモリエラーで落ちてしまう
  • MySQLのテーブルは複合主キー

MySQLの主キーが一つだったら非常に話は簡単です。 --split-byオプションに主キーを設定し、並列したい数を --num-mappersオプションに設定すれば均等に分割されて転送されます。

一方で複合主キーだった場合には --split-byオプションに複数カラムを設定することができないため、工夫が必要となります。複合主キーとは別のカラムを設定することもできますが、均等に分割されないことが予想されます。うまく転送できないかもしれません。

なお使用しているSqoopのバージョンは1.4.6です。
参考: Sqoop User Guide (v1.4.6)

解決方法

複合主キーを結合後にmd5で変換し、桁数を減らして10進数に戻したものを--split-byオプションに設定しました。
具体的には複合主キーをkey1、key2として以下のように変換された値を使用します。

CAST(CONV(LEFT(MD5(CONCAT(key1, key2)), 7), 16, 10) AS UNSIGNED)

これだけだと分かりにくいので、内側から順に確認します。

第一工程

CONCAT(key1, key2)

複合主キーを結合した文字列を作成します。

第二工程

MD5(CONCAT(key1, key2))

結合した文字列をmd5でハッシュ化します。
ハッシュ値は概ねランダムで与えられます(少なくとも検証した範囲ではほぼランダムでした)。

第三工程

LEFT(MD5(CONCAT(key1, key2)), 7)

ハッシュ値の上から7桁を取得します。
md5で変換された32桁の16進数をそのまま10進数に戻してMySQLで扱った場合、桁数が大きすぎてうまく扱えません。

第四工程

CONV(LEFT(MD5(CONCAT(key1, key2)), 7), 16, 10)

16進数を10進数に変換します。

第五工程

CAST(CONV(LEFT(MD5(CONCAT(key1, key2)), 7), 16, 10) AS UNSIGNED)

UNSIGNEDのINT型にCASTします。

最後に

この方法だと主キーがどの型か、いくつあるか等を気にする必要がありません。パフォーマンスも良好で、レコードが数千万件に上るMySQLのテーブルも上手く分割され、並列に転送されるようになりました。
他にも上手く転送する方法がありましたら、教えていただけると幸いです。