티끌모아 로키산맥 🏔
search
![](https://t1.daumcdn.net/tistory_admin/static/manage/images/r3/default_L.png)
로ᄏl
배움에 끝은 없다... 개발 또한 그러하다.
Today
Yesterday
우아한테크코스 Lv4 - MYSQL 최적화 미션 (학습자료 정리)
Lv4 미션으로 쿼리 튜닝해보는 미션이 주어졌다.
먼저 본인의 쿼리 작성 능력을 테스트해볼 겸 실습 사이트에서 아래의 쿼리를 직접 작성해보자!
위 튜토리얼(?)에 대한 나의 쿼리는 아래와 같다. (주의 성능은 1도 생각안하고 결과만 제대로 나오는 쿼리다!)
1번 쿼리
SELECT p.ProductId '상품아이디', p.ProductName '상품이름', SUM(od.Quantity) '총수량'
FROM Products p
JOIN OrderDetails od
ON od.ProductId = p.ProductId
JOIN Orders o
ON o.OrderId = od.OrderId
GROUP BY p.ProductId
HAVING SUM(od.Quantity) >= 200
ORDER BY SUM(od.Quantity) DESC
2번 쿼리
SELECT c.CustomerID 'ID', c.CustomerName '고객이름', SUM(od.Quantity) '주문량'
FROM OrderDetails od
JOIN Orders o ON od.OrderId = o.OrderId
LEFT JOIN Customers c ON c.CustomerID = o.CustomerID
GROUP BY c.CustomerId
ORDER BY SUM(od.Quantity) DESC
주의 해당 쿼리 결과는 틀렸다! 총 91건의 데이터가 나와야한다.
총 주문량이 0건인 고객도 있기 때문에 고객을 기준으로 JOIN을 해줘야한다. (고객 테이블이 왼쪽에 있도록해서 LEFT JOIN
시켰다.)
수정 후 (총 91건의 데이터)
SELECT c.CustomerID 'ID', c.CustomerName '고객이름', SUM(od.Quantity) '주문량'
FROM Customers c
LEFT JOIN Orders o ON c.CustomerID = o.CustomerID
LEFT JOIN OrderDetails od ON od.OrderId = o.OrderId
GROUP BY c.CustomerId
ORDER BY SUM(od.Quantity) DESC
3번 쿼리
SELECT c.CustomerID 'ID', c.CustomerName '고객이름', SUM(od.Quantity) '총수량', SUM(od.Quantity * p.Price) '지출금액'
FROM Customers c
LEFT OUTER JOIN Orders o ON c.CustomerID = o.CustomerID
LEFT OUTER JOIN OrderDetails od ON od.OrderId = o.OrderId
LEFT OUTER JOIN Products p ON p.ProductID = od.ProductId
GROUP BY c.CustomerId
ORDER BY SUM(od.Quantity * p.Price) DESC
기본 내용 중에서 잘 모르는 내용만 기록
SELECT * FROM Orders
JOIN OrderDetails ON Orders.OrderID = OrderDetails.OrderID
WHERE Orders.OrderID IN (1, 30)
드라이빙 테이블
, 검색 결과를 통해 뒤늦게 검색하는 테이블을 드리븐 테이블
이라고 한다.DB에서 최적화를 할 요소는 총 세가지가 있는데,
Client
,Database Engine
,Filesystem
이다.
JDBC Statement
는 쿼리 문장 분석, 컴파일, 실행의 단계를 캐싱한다.
PreparedStatement
는 처음 한 번만 세 단계를 거친 후 캐시에 담아서 재사용한다. (따라서 가급적이면 PreparedStatement를 쓰는게 좋다)읽는 블록 수를 줄여주는
을 의미한다.참고해볼 자료: 쿼리 최적화: 빠른 쿼리를 위한 7가지 체크리스트
* 쿼리 분석: Where 절의 검색 조건인지, Join 조건인지 판단
* 인덱스 선택: 각 테이블에 사용된 조건과 인덱스 통계 정보를 이용해 사용할 인덱스를 결정
* 조인 처리: 여러 테이블의 조인이 있는 경우 어떤 순서로 테이블을 읽을지 결정
Sequential Access vs Random Access
DB 테이블에서 데이터를 찾는 방법은 두 가지가 있다.
위 두 특징을 통해서 알 수 있는 사실은 읽을 데이터가 일정량을 넘으면 Index 보다 Table Full Scan이 유리하다
는 것이다.
- Index를 사용할 경우 Random I/O 횟수를 줄이는 것이다.
인덱스 탐색과정은 스캔 시작지점을 찾는 수직적 탐색
과 데이터를 찾는 수평적 탐색
, 두 가지로 나눌 수 있다. (cf. MySQL InnoDB의 경우 B-Tree 인덱스 구조가 기본이다)
(이미지 참조: https://simpledb.tistory.com/22)
인덱스 키
와 노드 정보
로 구성된 페이지 단위
이다.ROWID
가 필요하다.
데이터블록 주소 + 로우 번호(블록내 순번)
으로 구성된다.버퍼풀에서 먼저 찾아보고
못찾을 때만 디스크에서 블록을 읽는다. (읽은 후에는 버퍼풀에 적재한다)
JPA의 영속성 컨텍스트
와 유사해보인다. 아니 사실은 JPA 영속성 컨텍스트가 DB에 유사하게 구현됐다는 표현이 맞는 듯 하다.인덱스 튜닝은 크게 2가지로 인덱스 스캔 효율화
와 랜덤 엑세스 초기화
이 있다.
(랜덤 엑세스 초기화는 인덱스 스캔 후 테이블 레코드를 액세스할 때, 랜덤 I/O 횟수를 줄이는 것을 의미한다.)
랜덤액세스 최소화 튜닝이 더 중요하다.
1건을 조회하든, 1000만건을 다 조회하든 Table Full Scan의 성능은 일정하게 유지된다.
하지만 인덱스를 이용해 테이블을 액세스할 경우에는 랜덤 액세스 때문에 점점 느려진다.
Table Full Scan은 Sequantial Acess인 반면, 인덱스 ROWID를 이용한 테이블 액세스는 랜덤 액세스 방식이기 때문이다.
(테이블 데이터가 10~100만건 이내의 경우 5 ~ 20% 가량에서 손익분기점이 형성된다고 함)
조회건수가 늘수록 데이터를 버퍼캐시에서 찾을 가능성이 낮아진다.
우아한테크코스 Lv4 - 조회 성능 개선하기 수업자료
[파일구조]- 레코드 접근과 성능
MySQL 다시 공부하기(1)