N + 1 쿼리에 대한 또 다른 게시물
N + 1
쿼리 문제는 ActiveRecord와 같은 ORM (Object-Relational Mapping) 도구를 사용하여 작성된 쿼리에서 발견되는 일반적인 성능 병목 현상입니다.모든 관련 레코드를 가져오기 위해 만들어진 첫 번째 쿼리 외에도 각 연결에 대해 N개의 쿼리를 실행해야 할 때 문제가 발생합니다.
예를 들어
User
및 Appointment
모델 간에 다음과 같은 관계가 있는 경우# app/models/user.rb
class User < ApplicationRecord
has_many :appointments
end
# app/models/appointment.rb
class Appointment < ApplicationRecord
belongs_to :user
end
코드에서 아래 쿼리를 실행했습니다.
Appointment.all.limit(5).each do |appointment|
puts "#{appointment.user.name} has an appointment at #{appointment.date}"
end
이것이 레일 콘솔에서 볼 수 있는 것입니다.
Appointment Load (0.5ms) SELECT "appointments".* FROM "appointments" LIMIT ? [["LIMIT", 5]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 5], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 7], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 9], ["LIMIT", 1]]
위의 쿼리는 무해한 것처럼 보이지만 1,000개의 약속을 로드해야 한다고 상상해 보십시오! 데이터베이스에 1번(모든 약속을 잡기 위해) 더하기 1,000번(각 약속의 사용자를 잡기 위해), 총 1,001개의 쿼리가 필요합니다. 기술적인 측면에서 결과는 느리게 로드되고 있습니다. 즉,
appointment.user.name
에서 요청한 대로 "N"개의 연속 쿼리가 수행됩니다.독립 데이터베이스 쿼리 수를 줄이는 방법은 무엇입니까?
이에 대한 가능한 솔루션은
.includes
를 사용하는 것입니다. 그러면 쿼리 결과를 즉시 로드합니다. 즉, 관련 연결(상위 및 하위)이 단 몇 개의 쿼리를 통해 한 번에 모두 로드됩니다.예를 들어 콘솔에서 다음 코드를 실행하면
Appointment.includes(:user).limit(5).each do |appointment|
puts "#{appointment.user.name} has an appointment at #{appointment.date}"
end
이것은 우리가 돌려받을 것입니다:
Appointment Load (0.2ms) SELECT "appointments".* FROM "appointments" LIMIT ? [["LIMIT", 5]]
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [["id", 1], ["id", 3], ["id", 5], ["id", 7], ["id", 9]]
위의 결과에서 볼 수 있듯이 결과가 느리게 로드될 때 6개 대신 데이터베이스에 대해 2개의 쿼리만 실행됩니다.
데이터로 백업!
이 시점에서 "알겠습니다. 충분히 이야기했습니다!
.includes
를 사용하면 실제로 쿼리 성능이 향상된다는 것을 보여주는 데이터는 어디에 있습니까?"라고 궁금해할 수 있습니다.100명의 사용자 약속을 지연 로드하려고 시도하면 밀리초 단위로 아래와 같은 결과를 얻습니다.
user system total real
0.101984 0.096377 0.198361 ( 0.761556)
반면에 동일한 100개의 레코드를 즉시 로드하려고 하면 실행 시간이 빨라집니다.
user system total real
0.026250 0.010747 0.036997 ( 0.078877)
코드를 실행하는 데 소요된 시간(사용자)과 커널(시스템)에서 소요된 시간을 더한 총 시간을 고려하면 레코드를 즉시 로드하는 속도가 5.36배 빨라집니다!
Ruby의 벤치마크 모듈here에 대해 자세히 알아볼 수 있습니다.
애플리케이션에서 N + 1 쿼리를 식별하는 방법
애플리케이션에서 속도가 느려지는 모든 N + 1 쿼리를 찾기 위해 커뮤니티에서는 Bullet gem 을 사용할 것을 권장합니다.
참조
Reference
이 문제에 관하여(N + 1 쿼리에 대한 또 다른 게시물), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/yagosansz/yet-another-post-about-n-1-queries-41ae텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)