데이터베이스:django ORM에서 N+1 쿼리를 처리하는 방법

데이터베이스 N+1 조회는 흔히 볼 수 있는 문제로 장면을 간단하게 다음과 같이 설명한다.
기본 장면
class Category(models.Model):
    name = models.CharField(max_length=30)

class Article(models.Model):
    title = models.CharField(max_length=30)
    body = models.TextField()
    category = models.ForeignKey(Category)
    time    = models.DateTimeField()
    
#----     
{% for a in Article.objects.all %}
    {{ a.title }}
    {{ a.category.name }}
{% endfor %}

목록 페이지를 생성할 때 먼저 한 번 실행합니다
select * from article limited 0,N

그리고 캐티gory를 하나씩 가져오세요.name, N번 더 실행
select name from category where id = category_id

그래서 N+1문제는 사실 1+N문제라고 해야 하는데 이것은 데이터베이스 디자인 모델의 문제일 뿐이다.그러나 데이터베이스에 큰 압력을 가져올 수 있다. 간단한 목록 페이지에 수백 번의 데이터베이스 조회가 있을 수 있다
N+1문제는 ORM만의 문제가 아니라 orm을 사용할 때 데이터베이스 테이블의 줄이 하나의 대상이 되기 때문에 자연스레 위의 방법으로 조회하기 쉽고 orm을 사용하지 않고 프로그래밍을 하는 경우 하위 조회나 inner join을 직접 사용한다.
select a.*,c.name from article a,category b where a.category_id = b.id

하위 조회나 inner join은 데이터베이스에 있어서도 자원을 많이 쓰는 작업이다. 왜냐하면 시계를 잠가야 하기 때문에 높은 병발 상태에서는 쉽게 잠겨 죽기 쉽다.
1+N 문제를 해결하려면 보통 3가지 방법이 있어요.
  • 데이터베이스 반범식 디자인은 직설적으로 말하면 시계를 합쳐서 불필요한 시계로 설계하는 것이다. 이것은 두 가지 문제를 가져올 수 있다.
  • 표에 대량의 중복 데이터 항목이 존재한다
  • 표에 대량의 빈 항목이 나타나고 전체 표는 희소 행렬(sparse matrix)
  • 으로 변한다.
    그래서 이런 방안은 분명히 저장 효율이 높지 않지만 이 두 가지 상황을 최적화하면 좋은 해결 방법이라고 할 수 있다. MongoDB는 이렇게 한 것이다
  • 캐시를 추가하여 전체 목록 페이지에 캐시를 추가합니다.이렇게 하면 1+N회 조회를 계속 집행하든지, inner join 1회 조회로 끝내든지 상관없다.이런 방법의 단점은
  • 캐시 업데이트에 필요한 비용과 코드 복잡성 증가
  • 일부 장면은 데이터의 실시간성을 요구하고 캐시를 사용할 수 없음
  • N+1회 조회를 2차 조회로 바꾸기 간단하게
    select *,category_id from article limited 0,N
    를 실행한 다음 결과 목록을 훑어보고 모든categoryid, 중복 항목을 제거하고 다시 실행
    select name from category where id in (category id list)
  • 성능 최적화
    하위 조회/join 조회를 두 번으로 나누는 것은 높은 병발 사이트 데이터베이스 조정에서 매우 효과적인 흔한 방법이다. 비록 더 많은 cpu 시간을 들일 수 있지만 시스템의 잠금을 피하고 병발 응답 능력을 향상시켰다.
    데이터베이스 자체는 높은 합병을 처리할 수 없다. 왜냐하면 우리는 단일 데이터 항목의 조작이 원자라는 것을 보장할 수 있을 뿐이고 데이터베이스의 조회는 목록을 기본 단원으로 하기 때문에 이것은 천연적인 모순이고 해석이 없다.
    데이터베이스 디자인 모델은 웹 프레임워크 능력 범위 내에 있지 않기 때문에django의 ORM은 뒤의 두 가지 방법만 지원한다
  • Article.ojbects.select_related () 이것이 inner join
  • 입니다.
  • Article.objects.prefetch_related ('category ') 2번 검색
  • 이 문서의 주소:http://lutaf.com/156.htm루타프 오리지널 문장, 전재를 환영합니다. 원문 링크를 첨부하십시오

    좋은 웹페이지 즐겨찾기