MySQL 주니어 데이터베이스 면접
새 면접Q. users 테이블에 (name, email, created_at) 컬럼이 있고, WHERE email = ? AND created_at > ? 조건으로 자주 조회합니다. 복합 인덱스를 생성한다면 어떤 순서로 컬럼을 구성해야 하며, 그 이유는 무엇인가요?
인덱스는 첫 번째 컬럼부터 순차적으로 사용되며, 등호 조건과 범위 조건의 특성을 고려해야 합니다.
복합 인덱스는 INDEX(email, created_at) 순서로 생성해야 합니다. 인덱스는 왼쪽부터 순차적으로 사용되므로, 등호(=) 조건인 email을 먼저 배치하여 정확한 값으로 범위를 좁힌 후, 범위(>) 조건인 created_at을 뒤에 배치하는 것이 효율적입니다. 만약 순서를 바꾸면 created_at으로 먼저 범위를 좁힌 후 email을 검색해야 하므로 스캔 범위가 넓어집니다. 카디널리티가 높은 컬럼(email)을 앞에 두는 것도 선택도를 높이는 데 유리합니다. 실무에서는 WHERE 절의 조건 순서와 조건 타입을 고려하여 인덱스를 설계해야 합니다.
- • 등호 조건 컬럼을 앞에, 범위 조건 컬럼을 뒤에 배치
- • 인덱스는 왼쪽부터 순차적으로 사용됨
- • 카디널리티가 높은 컬럼을 앞에 배치하는 것이 유리
검색 조건이 여러 개인 API에서 복합 인덱스 설계는 쿼리 성능에 직접적인 영향을 미칩니다.
만약 WHERE name LIKE '%kim%' 조건이 추가된다면 인덱스 설계를 어떻게 수정하시겠습니까?
Q. MySQL에서 트랜잭션을 시작하고 종료하는 기본 명령어는 무엇이며, COMMIT과 ROLLBACK은 각각 언제 사용하나요?
트랜잭션은 START 또는 BEGIN으로 시작하며, 성공과 실패 케이스를 구분해야 합니다.
트랜잭션은 START TRANSACTION 또는 BEGIN 명령어로 시작합니다. COMMIT은 트랜잭션 내의 모든 변경사항을 데이터베이스에 영구적으로 반영할 때 사용하며, 모든 작업이 성공적으로 완료되었을 때 실행합니다. ROLLBACK은 트랜잭션 내의 모든 변경사항을 취소하고 트랜잭션 시작 전 상태로 되돌릴 때 사용하며, 오류가 발생하거나 비즈니스 로직상 작업을 취소해야 할 때 실행합니다. 예를 들어 송금 시스템에서 출금은 성공했지만 입금이 실패하면 ROLLBACK으로 전체 작업을 취소해야 합니다.
- • START TRANSACTION 또는 BEGIN으로 트랜잭션 시작
- • COMMIT은 변경사항을 영구 반영
- • ROLLBACK은 변경사항을 취소하고 이전 상태로 복구
결제, 포인트 적립, 재고 차감 등 여러 테이블을 동시에 수정하는 작업에서 트랜잭션 관리는 필수입니다.
AUTO COMMIT 설정이 활성화되어 있을 때와 비활성화되어 있을 때의 차이점은 무엇인가요?
Q. SELECT * FROM orders WHERE YEAR(created_at) = 2024 쿼리가 느립니다. 인덱스가 created_at 컬럼에 생성되어 있는데도 왜 느리며, 어떻게 개선할 수 있나요?
WHERE 절에서 컬럼에 함수를 사용하면 인덱스 활용에 제약이 생깁니다.
WHERE 절에서 컬럼에 함수(YEAR)를 적용하면 인덱스를 사용할 수 없어 풀 테이블 스캔이 발생합니다. MySQL은 함수가 적용된 값과 인덱스를 직접 비교할 수 없기 때문입니다. 개선 방법은 WHERE created_at BETWEEN '2024-01-01' AND '2024-12-31 23:59:59' 또는 WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01' 형태로 쿼리를 변경하는 것입니다. 이렇게 하면 created_at 컬럼의 인덱스를 정상적으로 활용할 수 있습니다. 실무에서는 WHERE 절에 컬럼을 변형하지 않고 원본 그대로 사용하는 것이 인덱스 활용의 기본 원칙입니다.
- • WHERE 절의 컬럼에 함수를 사용하면 인덱스를 사용할 수 없음
- • 범위 조건으로 변경하여 인덱스 활용 가능
- • 컬럼을 변형하지 않고 원본 그대로 사용해야 함
날짜 범위 검색, 문자열 변환 검색 등에서 함수 사용은 성능 저하의 주요 원인입니다.
MySQL 8.0에서 지원하는 함수 기반 인덱스(Function-Based Index)를 사용하면 어떤 이점이 있나요?
Q. 게시판 시스템에서 posts 테이블과 comments 테이블을 설계합니다. comments 테이블에 post_id 외래키를 설정할 때 ON DELETE CASCADE와 ON DELETE SET NULL의 차이는 무엇이며, 각각 어떤 상황에서 사용해야 하나요?
부모 레코드가 삭제될 때 자식 레코드를 어떻게 처리할지 정책을 결정하는 옵션입니다.
ON DELETE CASCADE는 부모 레코드(게시글)가 삭제되면 연결된 모든 자식 레코드(댓글)도 자동으로 삭제됩니다. 게시글이 삭제되면 해당 게시글의 댓글도 함께 삭제되어야 하는 경우에 적합합니다. ON DELETE SET NULL은 부모 레코드가 삭제되면 자식 레코드의 외래키 값을 NULL로 설정합니다. 댓글을 보존하되 원본 게시글과의 연결만 끊고 싶을 때 사용하지만, 댓글 시스템에서는 일반적으로 적합하지 않습니다. 게시판에서는 게시글 삭제 시 댓글도 함께 삭제되는 것이 자연스러우므로 CASCADE를 사용하는 것이 일반적입니다. 데이터 정합성과 비즈니스 로직을 고려하여 적절한 옵션을 선택해야 합니다.
- • CASCADE는 부모 삭제 시 자식도 함께 삭제
- • SET NULL은 부모 삭제 시 자식의 외래키를 NULL로 설정
- • 비즈니스 로직에 따라 적절한 옵션 선택 필요
게시판, 주문-상품, 회원-구매이력 등 부모-자식 관계 테이블 설계 시 삭제 정책을 명확히 해야 합니다.
ON DELETE RESTRICT와 ON DELETE NO ACTION의 차이점은 무엇인가요?
Q. MySQL에서 인덱스를 너무 많이 생성하면 어떤 문제가 발생할 수 있나요? 조회 성능과 쓰기 성능 측면에서 설명해주세요.
인덱스는 조회 성능을 향상시키지만, 데이터 변경 시에는 추가 작업이 필요합니다.
인덱스가 많으면 SELECT 쿼리는 빨라질 수 있지만, INSERT, UPDATE, DELETE 작업 시 성능이 저하됩니다. 데이터를 추가하거나 수정할 때마다 모든 인덱스를 함께 업데이트해야 하기 때문입니다. 예를 들어 인덱스가 5개인 테이블에 데이터를 INSERT하면 테이블 본체와 5개의 인덱스 구조를 모두 갱신해야 합니다. 또한 인덱스는 디스크 공간을 추가로 차지하므로 저장 공간 비용이 증가합니다. 따라서 실제로 자주 사용되는 조회 조건에만 인덱스를 생성하고, 사용되지 않는 인덱스는 주기적으로 제거하는 것이 좋습니다.
- • 인덱스가 많으면 INSERT, UPDATE, DELETE 성능 저하
- • 데이터 변경 시 모든 인덱스를 함께 업데이트해야 함
- • 디스크 공간을 추가로 차지함
쓰기 작업이 많은 로그 테이블이나 대량 데이터 입력 배치 작업에서 인덱스 개수는 성능에 큰 영향을 미칩니다.
실무에서 불필요한 인덱스를 찾아내는 방법은 무엇인가요?
Q. SELECT ... FOR UPDATE 구문은 언제 사용하며, 일반 SELECT 쿼리와 어떤 차이가 있나요? 실무 예시를 들어 설명해주세요.
데이터를 조회한 후 수정할 계획이 있을 때, 다른 트랜잭션의 간섭을 방지하기 위해 사용합니다.
SELECT ... FOR UPDATE는 조회한 행에 배타적 락을 걸어 다른 트랜잭션이 해당 행을 수정하거나 삭제하지 못하도록 합니다. 일반 SELECT는 락을 걸지 않아 다른 트랜잭션이 동시에 같은 데이터를 수정할 수 있습니다. 예를 들어 쿠폰 발급 시스템에서 남은 쿠폰 수량을 조회한 후 차감하는 경우, SELECT ... FOR UPDATE로 조회하면 현재 트랜잭션이 끝날 때까지 다른 트랜잭션이 해당 쿠폰 데이터를 수정할 수 없어 동시성 문제를 방지할 수 있습니다. 트랜잭션 내에서 조회 후 수정이 필요한 경우 필수적으로 사용해야 하며, COMMIT 또는 ROLLBACK 시 락이 해제됩니다.
- • 조회한 행에 배타적 락을 걸어 수정/삭제 방지
- • 트랜잭션 내에서 조회 후 수정 시 동시성 문제 방지
- • 트랜잭션 종료 시 락 해제
한정 수량 상품 구매, 좌석 예약, 포인트 차감 등 동시 요청이 많은 상황에서 데이터 정합성을 보장해야 할 때 사용합니다.
FOR UPDATE NOWAIT와 FOR UPDATE SKIP LOCKED의 차이는 무엇인가요?
Q. COUNT(*), COUNT(1), COUNT(column_name)의 차이점은 무엇이며, 성능상 어떤 것을 사용하는 것이 좋은가요?
NULL 값 처리 방식과 InnoDB 엔진의 최적화 방식을 고려해야 합니다.
COUNT(*)와 COUNT(1)은 동일하게 동작하며 NULL을 포함한 모든 행의 개수를 셉니다. MySQL 옵티마이저가 동일하게 최적화하므로 성능 차이는 없습니다. COUNT(column_name)은 해당 컬럼이 NULL이 아닌 행만 셉니다. 특정 컬럼의 NULL 여부를 확인해야 하는 경우가 아니라면 COUNT(*)를 사용하는 것이 명확하고 표준적입니다. InnoDB 엔진에서는 WHERE 조건이 없는 COUNT(*)도 전체 테이블을 스캔해야 하므로 대용량 테이블에서는 느릴 수 있습니다. 실무에서는 전체 행 개수가 필요하면 COUNT(*)를, 특정 컬럼의 값이 있는 행만 세려면 COUNT(column_name)을 사용합니다.
- • COUNT(*)와 COUNT(1)은 동일하게 동작하며 성능 차이 없음
- • COUNT(column_name)은 NULL이 아닌 행만 카운트
- • InnoDB에서 COUNT(*)는 전체 스캔 필요
게시판 전체 글 개수, 회원 수 집계, 대시보드 통계 등에서 COUNT 함수의 올바른 사용이 중요합니다.
대용량 테이블에서 전체 행 개수를 빠르게 조회하는 방법은 무엇이 있나요?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!