s3で、大量のファイル(10万とか)をコピーしたい時、どうされていますか?
*ディレクトリ名をちょっと変えたいとかで、大量のディレクトリとファイルがある場合
boto3でプログラム組んで、コピーしようとしたら、ゾッとする遅さでした。
管理コンソール上で数秒で終わるコピーが、何分もかかるとは、思わなかった・・・
今回の方法は、やや制約(gzipになる、HIVE形式でディレクトリ出力される)は発生しますが、同じ溝にはまった人はご参考までに。
s3で大量コピー:boto3(python)の場合
boto3 pythonで実行したところ、100ファイルごとに30秒前後かかっていて、震えました。。。
目の前の25万ファイルを目にして。
30秒×25万ファイル=2083時間 = 87日 もかかる・・・
Athenaでやってみた
どうしようか、、、と考えて、思いついた方法
「AthenaのINSERT INTOを使おう!」
という話です。
[手順]
(1) COPY元のテーブルを作成
(ディレクトリをパーティションに指定)
- コツ: テーブルカラムはstringに統一した方が面倒がないかも
(2) COPY元のパーティションを追加してください。
MSCK REPAIR TABLEは超遅いから、 ALTER TABLEでやりましょう。
例えば、日付("dt", YYYYMMDD形式)をパーティションキーにする場合のコードを載せておきます。
from datetime import datetime , timedelta
start_day = datetime.strptime('2020-10-15','%Y-%m-%d') #例えば2020/10/15 ~
last_day = datetime.strptime('2021-05-10','%Y-%m-%d') # 2021/5/10までのデータをコピーする時
q = 'ALTER TABLE old_table ADD IF NOT EXISTS' # q に ALTER TABLEが入る
part = " PARTITION (dt='{dt}') LOCATION 's3://bucket/path/to/{dt}/'"
dts = [] # ここには、100パーティションの "dt in ({parttion})" に指定するリストが入る
key = []
ctr = 0
while start_day.timestamp() <= last_day.timestamp():
ctr +=1
d = start_day.strftime('%Y%m%d')
q += part.format(dt=d)
key.append("'{}'".format(d))
start_day += timedelta(days=1)
if ctr >=100:
dts.append(",".join(key))
key = []
ctr = 0
if ctr >0:
dts.append(",".join(key))
(3) COPY先のテーブルを作成
(ディレクトリをパーティションに指定)
* [制約]HIVE形式( /dt=xxxx/ になる)
* [制約]出力はgzip形式になる
(4) パーティション数が100以上になる場合は、100単位で分割
Athenaは一気に100パーティション以上扱えないらしい。
だから、パーティション100個分ずつ、実行する必要がある。
(5) INSERT INTOを実行
・一応重複がないように、ファイル名が一致したら取り込まないようにする
[追記/訂正] こちら、new."$path"としてますが、ファイル名変わるのでカラムとして取り込まないとダメでした。すいません。
INSERT INTO new_table (select old.* from old_table as old left join new_table as new on egexp_extract(old."\$path", '[^/]+$') = regexp_extract(new."\$path", '[^/]+$') where new."$path" is null and dt in ('20201015','20201016' )
[結果]
-> ざっくりですが、250ディレクトリ、 25万件ほどのファイルコピーが実行3回(合計、15分くらい)で終わった。
よかった。。。
Athenaって便利ですね。
他にも方法があるのかもしれませんが。