Rails에서 1000건의 데이터 삭제를 99% 빠르게 하려면(초보자용)

5695 단어 SQLRailsActiveRecord

대량의 데이터 삭제 정보



Rails 공부중 아저씨입니다.

문득 1000건 정도의 데이터 삭제는 얼마나 시간이 걸릴까라고 생각해 보면,
너무 늦었고, "에, Rails는 그런데...?"라고 생각했습니다만, 조금
바꾸는 것만으로 99% 빨라지므로, 각서. Rails 초보자용입니다.
sqlite3을 사용하고 있습니다.

테스트용 앱



테스트용 앱을 scaffold로 살짝 만듭니다.
모델은 유저와 마이크로 포스트만.
$ rails new lots_of_delete_test
$ cd lots_of_delete_test
$ bin/rails g scaffold User name:string
$ bin/rails g model Micropost content:string user:references

사용자는 많은 마이크로 포스트를 가지고 있다고 가정합니다.
dependent: :destroy로 사용자를 지우면 관련
마이크로 포스트도 지워주도록 해 둡니다.
(이 destroy가 느린. 후술)

app/models/user.rb
class User < ApplicationRecord
  has_many :microposts, dependent: :destroy
end

테스트 데이터



테스트 데이터도 살짝 만들어 넣습니다.
유저를 1명, 거기에 묶는 마이크로 포스트를 1000건.

db/seeds.rb
user = User.create(name: "john")
1000.times do |i|
  Micropost.create(content: "test#{i}", user: user)
end
$ bin/rails db:migrate
$ bin/rails db:seed

삭제 테스트



서버를 시작하고 테스트해 봅니다.
$ bin/rails s

http://localhost:3000/users로 이동하여 Destroy를 클릭하여 사용자를 삭제합니다.


로그를 보면 마이크로 포스트에 대해 1000 건분의 DELETE 문이 발행되고,
전체적으로 2614ms 걸립니다. 느린.



SQL로 생각하면, 아래의 2문으로 끝나는 이야기입니다만, 그렇게 되어 있지 않습니다.
DELETE FROM microposts WHERE user_id = 1;
DELETE FROM users WHERE id = 1;

사용자를 삭제하면 관련 마이크로 포스트가 자동으로 삭제됩니다.
이것은 위의 User 모델에서 설정한 dependent: :destroy 때문입니다.
실제의 삭제 처리는 하기 부분.

app/controllers/users_controller.rb
  def destroy
    @user.destroy # <= ココ

    respond_to do |format|
      format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

개선법



User 모델의 :destroy를 :delete_all로 변경하면됩니다.

app/models/user.rb
class User < ApplicationRecord
  has_many :microposts, dependent: :delete_all # <= ココ
end

데이터를 재설정하고 다시 삭제해 봅니다.
$ bin/rails db:reset

그러면 ...


전체적으로 32ms가 되었습니다. DELETE문도 2회만이군요.
아까 정도는 2614ms였습니다. 약 99 % 감소 !!!

주의점



destroy에서 delete_all로 변경하면 콜백 등을 건너 뛰고
갑자기 관련 테이블에 DELETE 문을 발행합니다.
delete_all이 빠르기 때문에 이유만으로 사용하면 before_destroy와 같은
콜백을 빠져 버리기 때문에, 사용에는 주의가 필요합니다.

좋은 웹페이지 즐겨찾기