Slide 1

Slide 1 text

Bulk Insertって速いの? Railsを使って検証してみた @hasehiro25 駆け出しエンジニアのための、初めてのLT会 (2020/02/15)

Slide 2

Slide 2 text

はせがわ(@hasehiro25) twitter @hasehiro25 github @hasehiro25 Fjord歴:5ヶ月目ぐらい、最近アジャイル開発に参加 初めてのLTでテンパってます

Slide 3

Slide 3 text

Bulk Insert(バルクインサート)を検証!

Slide 4

Slide 4 text

INSERT文とは?(よく使うやつ) データをデータベースに挿入するためのクエリ Railsでレコードを作るとよくみるやつ INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["name", "fjord"], ["email", "[email protected]"], ["created_at", "2020-02-14 13:03:21.219745"], ["updated_at", "2020-02-14 13:03:21.219745"]]

Slide 5

Slide 5 text

Bulk Insertとは? SQL1回の発行で複数のレコードを挿入することができる機能。 INSERT INTO "users"("name","email","created_at","updated_at") VALUES ('hoge', '[email protected]', '2020-02-14 09:26:57.549471', '2020-02-14 09:26:57.549472'), ('fuga', '[email protected]', '2020-02-14 09:26:57.549473', '2020-02-14 09:26:57.549474') ※データベースによっては BulkInsert用の書き方がある(sqlserverなど)

Slide 6

Slide 6 text

Rails6からデフォルトで使えます!! Rails5までは・・・  activerecord-import というgemを使ってた Rails6からは・・・  次のメソッドが追加されました insert_all(value) valueにある配列をまとめてInsertする insert_all!(value) insert_allと一緒だが、失敗時にエラーを返す upsert_all(value) 新しいレコードは新規で作り、既存のものは更新する

Slide 7

Slide 7 text

使う場合の注意点 ・直接SQLを組み立てるので、Railsのvalidationやcallbackが呼ばれない ・同様にcreated_atとupdated_atが自動で入らないので、必要な場合は考慮する 必要がある

Slide 8

Slide 8 text

検証してみた

Slide 9

Slide 9 text

検証環境 環境 - OS: macOS Mojave 10.14.6 - CPU: 2.7 GHz i7 - memory 16GB - ruby: 2.7.0 - rails 6.0.2.1 - db: postgresql seed.rbにコードを書いて時間を計測

Slide 10

Slide 10 text

検証① Createで100万件入れるのにかかる時間を測定 require 'benchmark' Benchmark.bm 15 do |r| r.report "Create users" do 1_000_000.times do |i| User.create( name: "Fjord No.#{i}", email: "hogehoge#{i}@example.com" ) end end

Slide 11

Slide 11 text

その結果・・・

Slide 12

Slide 12 text

user  system total real 2058.856938 76.251603 2135.108541 (2522.862995) 約35分かかる!!

Slide 13

Slide 13 text

遅い要因 Rails(Activerecord)側で毎回SQLを作ってからデータベースで実行するので時間がかかるのと、 コールバックやバリデーションの処理が走るのでそのぶん遅い? (それでも毎分約28,000レコード作ってくれてる) 「このデータいれておいてね」 
 「いれておいたよ!」 
 insert x 100万回 1件とか楽勝

Slide 14

Slide 14 text

コールバックを呼ばないとどうなる??

Slide 15

Slide 15 text

検証その② 生SQLで100万件入れるのにかかる時間を測定 Benchmark.bm 15 do |r| r.report "Create users" do con = ActiveRecord::Base.connection 1_000_000.times do |i| time = Time.now con.execute("INSERT INTO users (name, email, created_at, updated_at) VALUES ('Fjord#{i}', 'hogehoge#{i}@example.com', '#{time}', '#{time}')") end end end

Slide 16

Slide 16 text

user system total real 400.546058 23.915804 424.461862 (603.570310) 約7分!! 速い! 結果・・・

Slide 17

Slide 17 text

Bulk Insert なら・・・!

Slide 18

Slide 18 text

検証その③ Bulk Insertで100万件入れるのにかかる時間を測定 を Benchmark.bm 15 do |r| r.report "Create users" do users = [] 1_000_000.times do |i| now = Time.now users << { name: "FJORD#{i}", email: "hogehoge#{i}@example.com", created_at: now, updated_at: now } end User.insert_all(users) end end

Slide 19

Slide 19 text

user system total real 44.650481 1.137704 45.788185 ( 64.265599) 約46秒!! めっちゃ速い! 結果・・・

Slide 20

Slide 20 text

大量のデータだとSQLの発行回数がボトルネック まとめて1回のSQLで済ませた方が圧倒的に速い コンテキストチェンジの回数が少なく済むのも速い要因 「このデータいれておいてね」 
 「い、いれておいたよ・・・」 
 insert x 1回 なんだこの量は!?

Slide 21

Slide 21 text

records/insert by create by raw SQL Bulk Insert 1,000 2.078143 0.433111 0.047257 10,000 21.482302 4.251136 0.384161 100,000 227.163341(≒4 min) 40.733448 3.569448 1,000,000 2135.108541(≒32 min) 424.461862(≒7min) 45.788185 処理時間まとめ ・単位は秒 ・Benchmarkのtotalで比較

Slide 22

Slide 22 text

まとめ ・Rails6からBulk Insert デフォルトで使えるよ ・大量のデータを入れるならBulk Insertを検討す る 価値があるかも ・ただし、大量のデータでなければあまり気にする必 要ないし、むしろ実装コストがあがる

Slide 23

Slide 23 text

ご静聴ありがとうございました!!