썸네일을 이미지 테이블의 첫번째 값으로 설정하는 방식으로 인한 상품 검색 API 성능 문제
SELECT
i.item_uuid,
i.item_name,
i.price,
MIN(img.ImageUrl),
b.name
FROM Item AS i
JOIN
Brand b ON i.brand_id = b.brand_id
LEFT JOIN
ImageFile img ON img.item_id = i.item_id
WHERE
i.deleted_date IS NULL
AND b.name LIKE '%유한%'
GROUP BY
i.item_uuid
LIMIT 0, 20
;
ImageFile 조인 시 다수 이미지 때문에 결과 행이 크게 늘어나고, GROUP BY로 인한 집계 비용이 커짐
Brand 테이블 name 컬럼 인덱스 설정되어있긴하나 %(와일드카드)가 좌측에 있는 경우는 인덱스가 적용되지 않음.
SELECT
i.item_uuid,
i.item_name,
i.price,
img.min_url,
b.name
FROM Item AS i
JOIN Brand b ON i.brand_id = b.brand_id
LEFT JOIN (
SELECT item_id, MIN(ImageUrl) AS min_url
FROM ImageFile
GROUP BY item_id
) img ON img.item_id = i.item_id
WHERE
i.deleted_date IS NULL
AND b.name LIKE '%유한%'
LIMIT 0, 20;
Brand 테이블에서 name LIKE '%유한%'
조건이 큰 비용 발생 원인
%
가 앞에 붙어 인덱스가 완벽하게 활용되지 않아 26,553
건 스캔 후 2,950
건만 필터링됨Item 테이블은 brand_id와 deleted_date 조건을 인덱스로 잘 활용하고 있음
ImageFile 테이블은 서브쿼리에서 임시 테이블 사용하며 item_id 인덱스를 통해 428,412건을 스캔함
최종적으로 img
조인에서 114,989
건 결과가 나오면서 비용 상승
임시 테이블(using_temporary_table: true
)이 사용되고 있어 그룹핑 관련 작업에 비용이 발생함
SELECT
i.item_uuid,
i.item_name,
i.price,
i.thumbnail,
b.name
FROM Item AS i
JOIN Brand b ON i.brand_id = b.brand_id
WHERE
i.deleted_date IS NULL
AND b.name LIKE '%유한%'
LIMIT 0, 20;
다대다 혹은 일대다 관계에서 다수의 데이터를 집계하는 쿼리는 반드시 비용이 크기 때문에 반정규화 개선하는 방법에 대해 학습
임시 테이블과 그룹핑은 쿼리 비용 상승의 주요 원인임을 항상 염두에 두어야 하며, 가능한 미리 집계된 데이터를 활용하는 설계가 중요함