1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Qiita100万記事感謝祭!記事投稿キャンペーン開催のお知らせ

【Rails】180万件のCSVを「postgres-copy」を使って、爆速でデータ移行をした話

Last updated at Posted at 2025-01-14

はじめに

マスタデータ180万件を移行したいという依頼がありました。
大量のデータをインポートするRuby on RailsのGemとしてactiverecord-importが有名ですが、100万とかなると実行に時間がかかりそうでした。
その際にpostgres-copyというGemで実行時間がかからず移行できたので、そのときの手順を記載します。

※S3はマスタデータファイルの保存先として使用します。初めはGitに含めてパッチを当てようとしましたが、Gitに大きいファイルを含めてしまうとリGitのパフォーマンスが低下して重くなってしまうため、Gitに含めずデータ移行をします。

前提

  • Ruby on Rails
  • PostgreSQL
  • AWS S3

postgres-copy、aws-sdk-s3の導入

Gemfileにpostgres-copy、aws-sdk-s3を追加します。

gem 'postgres-copy'
gem 'aws-sdk-s3'

その後bundle installをします(Dockerを使っています)

docker-compose build
docker-compose up -d

インポートするファイルを準備する

インポートするファイル(CSV)を用意します。
180万件入っているCSVファイルはそのままインポートできませんので、ファイルを分割します。
ファイルサイズが大きすぎますと後ほどS3からCSVファイルを読み取って登録する際に読み取る時間がかかりますので、ファイルサイズは調整をして下さい。

下記はsample.csvをsample-<連番>.csvに分割するコマンドです。

split -l 10000 -d -a 3 sample.csv sample-

上記コマンドで1万行にCSVファイルを分割することができました。
ただし、拡張子が付いていないので、拡張子をつけます。

分割したファイルをS3に保存します。

データ移行のパッチを作成する

rails cで実行するパッチのコマンドを用意します。
下記のようなコマンドでS3に保存したファイルを取得して、データを登録します。

client = Aws::S3::Client.new(
  region: ENV['AWS_REGION'],
  access_key_id: ENV['AWS_ACCESS_KEY_ID'],
  secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
)
rows = []

(0..184).each do |i| # ここの184を分割したファイルの連番の一番大きい数に変更する。
  formatted_number = format("%03d", i)
  puts "処理中: #{formatted_number}"
  file = client.get_object(bucket: 'sample-import-bucket', key: "sample-#{formatted_number}.csv")
  csv = CSV.parse(file.body.read)
  csv.each do |row|
    rows <<  {
      id: row[0],
      title: row[1],
      body: row[2],
    }
  end
end

Benchmark.realtime do
  Post.insert_all!(rows)
end

実行結果

約4分ぐらいで実行することができました。

Benchmark.realtime do
  Post.insert_all!(rows)
end
(省略)
=> 230.73561293902458

注意点

insert_allではなくinsert_all!を使いましょう

insert_allは重複データがあった場合に無視します。insert_all!の場合、レコードの重複があったら、ActiveRecord::RecordNotUniqueの例外がraiseされますので、なるべくinsert_all!を使いましょう。

不正なデータがないことを事前に確認する

insert_allの場合はRailsのvalidationを設定していてもスキップされます。
取り込む前に、データが正しいことを確認しましょう。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?