🗿

EC2に700万件insertする

2021/11/27に公開

したい事

  • 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回でできる

Discussion