Dotfriends의 페이징 기능 성능 테스트를 하던 중 데이터가 100만개를 넘어서면 호출 시간이 3초나 걸리는 문제가 생겼다.
쿼리문 한개에 4초의 시간이 걸린다.
이런 문제를 자각하고 페이징 기능을 쿼리 성능 튜닝에 대해서 알아보았다.
해결 방법은 크게 3가지가 있었다.
1. NO OFFSET으로 페이징 기능 변경
1-1. NO OFFSET 기능으로 변경 후 총 데이터 카운트 쿼리 삭제
2. 서브 쿼리로 변경
NO OFFSET 기능은 상용하기가 어려운게 dotfriends사이트 특성상 원하는 지점으로 빠르게 이동하는 방식이 유저에게 더 편할 것 같기 때문에 NO OFFSET기능으로 변경하기에는 무리가 있었다.
데이터가 1억건이 넘아가게 되면 카운트 쿼리에서도 성능이 저하 되므로 카운트 쿼리를 안사용하는 방식을 사용해야겠지만
우선 100만건이라는 가정하에서는 카운트 쿼리가 큰 시간을 잡아먹지 않기 때문에 서브 쿼리를 사용하시로 했다.
난관
장고 ORM쿼리가 이미 너무 복잡한 상태에서 서브 쿼리문 문법을 사용하기가 어려웠고
로우 쿼리로 해야겠단 생각하고 쿼리문을 계속 짜봤지만
로우 쿼리문이 익숙하지 않아서 SQL공부를 따로 했다.
해결
기존에 ORM코드를 보면
products = Product.objects.filter(**q).prefetch_related('image_set')\
.annotate(avg_rate=Avg('comment__rate'),popular=Count("userproductlike", distinct=True),review_count=Count('comment',distinct=True))\
.order_by(order)[offset:offset+limit]
쿼리를 조회한 후에 offset:limit 이 붙게 된다.
그래서 조회하는 쿼리가 offset 값이 커질수록 호출 시간이 늘어나게 된다.
이걸 로우 쿼리로 바꾸면
products = Product.objects.raw('SELECT
p.id, p.name, p.price, p.discount_percent, p.is_new, p.category_id, p.created_at, p.updated_at,
AVG(comments.rate) AS avg_rate, COUNT(DISTINCT userproductlikes.id)
AS popular, COUNT(DISTINCT comments.id) AS review_count from products as p
LEFT OUTER JOIN comments
ON
(p.id = comments.product_id)
LEFT OUTER JOIN userproductlikes
ON
(p.id=userproductlikes.product_id)
JOIN(SELECT id
FROM products
ORDER BY {order}
LIMIT {offset}, {limit}) as temp
ON temp.id = p.id
GROUP BY p.id;'.format(offset= offset, limit= limit, order=order))
상당히 복잡한 쿼리문이 되었다.
그래도 성능은 상당히 좋아졌다.
기존 4초(4000ms)에서 101ms로 40배나 빨라졌다.
'DotFriends' 카테고리의 다른 글
🚀 상품 리스트 조회 API분기 줄이기 (0) | 2021.09.25 |
---|---|
📌 쿼리 파라미터 입력값에 따른 500에러 막기 (0) | 2021.09.24 |
⚫️ DotFriends 프로젝트 후기 (0) | 2021.09.12 |
제품 상세페이지 구현과 고민 01 (0) | 2021.09.05 |
prefetch_related로 성능 향상하기 (0) | 2021.09.05 |