はじめに
この記事は 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のテーブルも上手く分割され、並列に転送されるようになりました。
他にも上手く転送する方法がありましたら、教えていただけると幸いです。