こんにちわ、swamp09です。
先日Railsプロジェクトで遭遇した大規模データのインポートについてお話しします。
ある外部システムから大規模なデータの連携をファイルで行うことになり、ファイルからの大規模データのインポートについていくつか方法を試しました。
大規模データは、月次で連携され毎回データをインポートします。
例として、その大規模データはユーザーデータで CSV 形式で受け取るとして、users.csv
を毎月ファイルで受け取りインポートすると思ってください。
最初にやったのは、取り込む CSV ファイルを分割し並列で読み込む方法です。CSVファイルのサイズが1GB以上あり、データ数が100万件を超えていたので、ファイルを一度に読み込みデータをインポートするのはメモリへの負荷が大きすぎました。
ファイルの分割はsplit
コマンドを使用し、10万件ずつのファイルに分割しました。
gem の Parallel を使用し、だいたい下記のような形になりました。
Parallel.map
を使用するとデフォルトでCPUコア数から並列数を決定してくれます。https://github.com/grosser/parallel/blob/v1.20.1/lib/parallel/processor_count.rb#L8
def import(file_path) file_split(file_path) split_files = Dir.glob(SPLITTED_FILES_PATH + '*') Parallel.map(split_files) do |csv_file| csv = CSV.read(csv_file) User.insert_all(csv) File.delete(csv_file) end end def file_split(file_path) system("split -l 100000 -d --additional-suffix=.csv #{file_path} #{SPLITTED_FILES_PATH}splitted_", exception: true) end
手元の開発環境で試して、約30分でインポートが終わりました。 こちらを試そうとしたのですが、実行する予定のサーバーのマシンパワーに余力がない懸念があり、もう少し良い方法がないか検討することにしました。
そんな時、チームメンバーから MySQL だとファイルの一括インポートできるツールがあるけど、それと似たようなツールあるだろうか、といった話があがりました。プロジェクトで使っていた RDBMS が Oracle だったのですが、調べてみると SQL*Loader というファイルからの一括インポートのツールがあったので試してみることにしました。 SQL*Loader では、制御ファイルを作ってインポートを行います。
users.csv
のデータの例としてこのようなデータが入っているとします。
user_id | name | prefecture | birth_day | last_sign_in_at |
---|---|---|---|---|
1 | 田中 太郎 | 東京都 | 2000/06/01 | 2020/01/30 |
2 | 山田 次郎 | 神奈川県 | 2000/10/01 | 2020/03/30 |
上記のデータから、必要なものが user_id
と name
と address
だけであるとして、3つだけ抽出します。
制御ファイルを下記のように作成します。
LOAD DATA CHARACTERSET JA16SJIS INFILE 'users.csv' INTO TABLE users APPEND FIELDS TERMINATED BY ',' ( user_id INTEGER EXTERNAL, name CHAR, prefecture CHAR, birth_day FILLER, last_sign_in_at FILLER )
SQL*Loader で処理すると、ファイルの分割処理も必要なくなり、処理時間も格段に短くなりました。 スクリプトを自作していた時は約30分ぐらいかかっていた処理時間が SQL*Loader では約3分で終わったので、圧倒的スピードで終わるようになりました。
まとめ
Active Record を使って良い感じに速くデータをインポートする方法を考えなければ、と思っていたところRDBMSの付属のツールを使えば一気に解決しました。 他の RDBMS では、MySQL は mysqlimport があり、PostgreSQL では COPY があるようです。
大量のデータをファイルからインポートするケースはあまり多くないかもしれませんが、なにかの参考になれば幸いです。