SQL 튜닝 주니어 기술면접
새 면접Q. 사용자 테이블에서 WHERE 조건으로 email을 자주 조회하는 쿼리가 느립니다. 인덱스를 생성하려고 할 때 고려해야 할 사항과 인덱스 생성 후 성능 개선의 원리를 설명해주세요.
인덱스의 자료구조와 검색 시 발생하는 I/O 횟수 변화를 생각해보세요.
email 컬럼에 인덱스를 생성하면 B-Tree 구조로 정렬된 별도의 자료구조가 만들어집니다. 인덱스가 없으면 Full Table Scan으로 모든 행을 순차 검색하지만, 인덱스가 있으면 O(log N) 시간복잡도로 빠르게 검색할 수 있습니다. 다만 email 컬럼의 카디널리티가 높아야 효과적이며, 중복값이 많으면 인덱스 효율이 떨어집니다. 또한 INSERT, UPDATE, DELETE 시 인덱스도 함께 갱신되므로 쓰기 성능에 영향을 줄 수 있습니다. 따라서 조회가 많고 쓰기가 적은 컬럼에 인덱스를 생성하는 것이 효과적입니다.
- • B-Tree 자료구조 이해
- • Full Table Scan vs Index Scan 비교
- • 카디널리티 개념
- • 쓰기 성능 트레이드오프
회원 로그인 시 email로 사용자를 조회하는 쿼리의 성능을 개선할 때 사용합니다.
복합 인덱스(Composite Index)를 생성할 때 컬럼 순서가 중요한 이유는 무엇인가요?
Q. EXPLAIN 명령어를 사용해 쿼리 실행 계획을 분석할 때, 어떤 항목들을 중점적으로 확인해야 하며 각각이 성능에 미치는 영향을 설명해주세요.
type, rows, Extra 컬럼에서 성능 문제를 파악할 수 있는 정보들이 있습니다.
실행 계획에서 가장 중요한 것은 type 컬럼으로, ALL은 Full Table Scan을 의미하여 성능이 가장 나쁘고, index, range, ref, eq_ref, const 순으로 성능이 개선됩니다. rows 컬럼은 검토할 예상 행 수로, 이 값이 크면 많은 데이터를 스캔한다는 의미입니다. Extra 컬럼에서 Using filesort나 Using temporary가 나타나면 추가적인 정렬이나 임시 테이블 생성으로 성능 저하가 발생합니다. key 컬럼을 확인해 실제로 어떤 인덱스가 사용되는지 파악하고, NULL이면 인덱스를 타지 않는다는 의미입니다. 이러한 정보들을 종합해 쿼리 개선 방향을 결정할 수 있습니다.
- • type 컬럼의 접근 방식 이해
- • rows를 통한 스캔 범위 파악
- • Extra의 성능 경고 신호
- • 인덱스 사용 여부 확인
프로덕션 환경에서 느린 쿼리를 발견했을 때 원인을 분석하고 개선 방향을 수립할 때 사용합니다.
Using where와 Using index의 차이점은 무엇이며, 어느 것이 더 효율적인가요?
Q. 주문 테이블과 상품 테이블을 JOIN하는 쿼리에서 성능을 최적화하기 위한 접근 방법을 설명하고, Nested Loop Join과 Hash Join의 시간복잡도 차이를 비교해주세요.
JOIN 컬럼에 인덱스가 있는지 여부와 테이블 크기에 따라 최적의 JOIN 알고리즘이 달라집니다.
JOIN 성능 최적화를 위해서는 먼저 JOIN 조건 컬럼에 인덱스를 생성해야 합니다. Nested Loop Join은 외부 테이블의 각 행마다 내부 테이블을 반복 검색하므로 O(N*M) 시간복잡도를 가지지만, 내부 테이블에 인덱스가 있으면 O(N*log M)으로 개선됩니다. Hash Join은 작은 테이블을 해시 테이블로 만들고 큰 테이블을 스캔하므로 O(N+M) 시간복잡도를 가져 대용량 데이터에 유리합니다. 소량의 데이터나 인덱스가 잘 구성된 경우 Nested Loop가, 대량 데이터에서는 Hash Join이 효율적입니다. 또한 WHERE 조건으로 먼저 데이터를 필터링한 후 JOIN하면 처리할 행 수가 줄어들어 성능이 개선됩니다.
- • JOIN 컬럼 인덱스의 중요성
- • Nested Loop Join의 시간복잡도
- • Hash Join의 작동 원리
- • 데이터 크기에 따른 JOIN 전략
주문 내역 조회 시 주문, 상품, 사용자 테이블을 JOIN하는 복잡한 쿼리의 성능을 개선할 때 사용합니다.
LEFT JOIN과 INNER JOIN 중 어느 것이 일반적으로 더 빠르며, 그 이유는 무엇인가요?
Q. SELECT 절이나 WHERE 절에 서브쿼리를 사용할 때 발생할 수 있는 성능 문제와, 이를 JOIN이나 EXISTS로 변환했을 때의 성능 개선 원리를 설명해주세요.
서브쿼리가 메인 쿼리의 각 행마다 반복 실행되는지, 한 번만 실행되는지를 구분해보세요.
WHERE 절의 상관 서브쿼리(Correlated Subquery)는 외부 쿼리의 각 행마다 반복 실행되어 O(N*M) 시간복잡도를 가지므로 성능이 매우 나쁩니다. 이를 JOIN으로 변환하면 한 번의 조인 연산으로 처리되어 O(N+M) 또는 인덱스 사용 시 O(N*log M)으로 개선됩니다. EXISTS는 조건을 만족하는 첫 번째 행을 찾으면 즉시 중단하므로 IN 서브쿼리보다 효율적입니다. SELECT 절의 스칼라 서브쿼리도 각 행마다 실행되므로 LEFT JOIN으로 변환하는 것이 좋습니다. 비상관 서브쿼리는 한 번만 실행되므로 성능 문제가 적지만, 가능하면 WITH 절(CTE)로 명시적으로 작성하면 가독성과 재사용성이 높아집니다.
- • 상관 서브쿼리의 성능 문제
- • JOIN 변환을 통한 복잡도 개선
- • EXISTS의 조기 종료 특성
- • 스칼라 서브쿼리 최적화
특정 조건을 만족하는 사용자의 최근 주문 금액을 조회하는 복잡한 쿼리를 최적화할 때 사용합니다.
IN 연산자와 EXISTS 연산자의 성능 차이는 어떤 상황에서 발생하나요?
Q. WHERE 조건에 함수나 연산을 사용하면 인덱스를 타지 못하는 경우가 있습니다. 이런 상황이 발생하는 원리와 해결 방법을 시간복잡도 관점에서 설명해주세요.
인덱스는 원본 컬럼 값으로 정렬되어 있는데, 함수를 적용하면 어떻게 되는지 생각해보세요.
인덱스는 컬럼의 원본 값을 기준으로 B-Tree에 정렬 저장되어 있어 O(log N) 검색이 가능합니다. 하지만 WHERE YEAR(created_at) = 2024 같이 컬럼에 함수를 적용하면 인덱스의 정렬 순서를 활용할 수 없어 Full Table Scan으로 O(N) 복잡도가 됩니다. 이를 해결하려면 WHERE created_at BETWEEN '2024-01-01' AND '2024-12-31' 처럼 컬럼을 가공하지 않고 비교값을 변환해야 합니다. SUBSTRING(email, 1, 3) = 'abc' 같은 경우도 인덱스를 못 타므로 email LIKE 'abc%'로 변환하면 인덱스 Range Scan이 가능합니다. 함수 기반 인덱스(Function-based Index)를 생성하는 방법도 있지만, 쿼리를 재작성하는 것이 더 범용적인 해결책입니다.
- • 인덱스 정렬 구조와 검색 원리
- • 함수 사용 시 Full Table Scan 발생
- • 컬럼 가공 회피 기법
- • 시간복잡도 O(log N)에서 O(N)으로 악화
날짜 범위 조회나 문자열 검색 쿼리가 느릴 때 WHERE 조건을 재작성하여 인덱스를 활용하도록 개선합니다.
LIKE 연산자에서 '%abc'와 'abc%'의 인덱스 활용 차이를 설명해주세요.
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!