したい事
- EC2のmysql(RDS)にレコードを700万件入れたい
- できるだけ短時間で入れたい
- 1回で入れたい
環境
- docker-compose
- rails(6.1)
- localマシン: mbp 16GB
- EC2プラン: t3.small(メモリ2GB)、DBはRDS
local 下準備
docker-compose.yml内容変更↓
version: '3'
services:
db:
image: mysql:5.7.34
command: --max_allowed_packet=536870912
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
ports:
- "4306:3306"
web:
build: .
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
environment:
TZ: Asia/Tokyo
volumes:
- .:/test
ports:
- "3000:3000"
depends_on:
- db
- ポイント: 上から5行目でmysqlのpacketを512MBに増やしている
dockerのsettingを変更↓
- ポイント: resourceのmemoryを8GBに増やしている
localで実行
-
まずlocalで数百万のinsertをできるかを確認
-
seeds.rb内容変更↓
-
(例はcsvのデータをinsertする場合)
# frozen_string_literal: true
require "csv"
def insert_all_test
array = []
CSV.foreach("db/test1.csv", headers: true) do |row|
array << {
name: row["name"],
created_at: Time.current,
updated_at: Time.current
}
end
User.insert_all array
array = []
CSV.foreach("db/test2.csv", headers: true) do |row|
array << {
name: row["name"],
created_at: Time.current,
updated_at: Time.current
}
end
User.insert_all array
end
insert_all_test
- ポイント: エラーにならない程度の量のデータを1つのループで配列に入れ、一度
insert_all
でDBに入れる - それを繰り返し
- そのdefを、最終行の
insert_all_test
で実行($ rails db:seed
)する - これで1回でinsertできる。localでは700万件insertが約15分で完了
- (insert_allメソッドはcreated_atとupdate_atを自動で入れないので上記のように明記する必要)
EC2 下準備
- mysqlの環境を変更する
DBがRDSの場合
-
パラメータグループで
max_allowed_packet
を変更する(今回は512MBとした) -
ポイント: 新規でパラメータグループを作成した後は、そのグループをEC2インスタンスに適用する
EC2で実行
$ rails db:seed RAILS_ENV=production
プラン: t3.smallの場合
- 1つのcsvは約50万件
- 700万件のinsertに約50分掛かった
プラン: t2.microの場合
- 1つのcsvは約30万件(50万件ではエラー。broken pipeする)
プランがmicroでも、時間を掛ければ1回でできる