MySQL 미드레벨 시스템 설계 기술면접
새 면접Q. 사용자 수가 1억 명을 넘어가는 글로벌 서비스에서 MySQL 데이터베이스를 수평 샤딩(Horizontal Sharding)해야 하는 상황입니다. 사용자 ID 기반 해시 샤딩, 범위 기반 샤딩, 지역 기반 샤딩의 장단점을 비교하고, 사용자 간 친구 관계를 조회하는 소셜 기능이 있을 때 어떤 샤딩 전략이 적합한지 크로스 샤드 쿼리와 데이터 재분배 관점에서 설명해주세요.
해시 샤딩은 데이터를 균등하게 분산시키지만 범위 검색이 어렵고 샤드 추가 시 대규모 재분배가 필요합니다. 범위 기반 샤딩은 특정 범위 조회가 효율적이지만 핫스팟이 발생할 수 있습니다. 지역 기반 샤딩은 지역 내 쿼리는 빠르지만 글로벌 쿼리가 느립니다. 친구 관계 조회의 경우 사용자 ID 기반 해시 샤딩을 기본으로 하되, 친구 관계 테이블은 양방향으로 중복 저장하거나 별도의 그래프 데이터베이스를 활용하는 하이브리드 접근이 적합합니다. Consistent Hashing을 사용하면 샤드 추가 시 재분배 범위를 최소화할 수 있습니다.
- • 해시 샤딩: 균등 분산, 범위 검색 불리, 재분배 비용 높음
- • 범위/지역 샤딩: 특정 패턴 쿼리 유리, 핫스팟 위험
- • 친구 관계는 크로스 샤드 쿼리 발생 - 중복 저장 또는 별도 DB 고려
- • Consistent Hashing으로 재분배 최소화
샤드 간 트랜잭션이 필요한 경우(예: 사용자 A에서 B로 포인트 이체) 어떻게 처리하시겠습니까? 2PC(Two-Phase Commit)와 Saga 패턴의 차이를 설명해주세요.
Q. 이커머스 시스템에서 상품의 다양한 옵션(색상, 사이즈 등)과 각 옵션 조합별 재고 및 가격을 관리해야 합니다. EAV(Entity-Attribute-Value) 모델과 전통적인 정규화된 테이블 구조(Product, ProductOption, ProductSKU) 중 어떤 방식이 적합한지 쿼리 복잡도, 성능, 확장성, 데이터 무결성 관점에서 비교 설명해주세요.
EAV 모델은 유연한 속성 추가가 가능하지만 JOIN이 많아지고 타입 안정성이 떨어지며 인덱스 활용이 어렵습니다. 정규화된 구조(Product-ProductOption-ProductSKU)는 쿼리가 단순하고 외래키로 무결성을 보장하며 인덱스 최적화가 용이합니다. 이커머스의 경우 옵션 구조가 비교적 정형화되어 있고 재고/가격 조회 성능이 중요하므로 정규화된 구조가 적합합니다. ProductSKU 테이블에 option_value_ids를 JSON으로 저장하고 Generated Column으로 인덱싱하면 유연성과 성능을 모두 확보할 수 있습니다.
- • EAV: 유연하지만 JOIN 복잡, 타입 안정성 낮음, 인덱스 비효율
- • 정규화 구조: 쿼리 단순, 무결성 보장, 인덱스 최적화 용이
- • 이커머스는 정형화된 옵션 구조 - 정규화 모델 적합
- • JSON + Generated Column으로 유연성과 성능 절충
상품 검색 기능에서 여러 옵션 필터를 동시에 적용할 때(예: 빨간색 + L사이즈) 효율적인 쿼리 전략과 인덱스 설계 방법을 설명해주세요.
Q. 게시글 목록 API에서 각 게시글마다 작성자 정보, 댓글 수, 좋아요 수를 함께 반환해야 합니다. N+1 쿼리 문제를 해결하기 위한 방법들(JOIN, Subquery, Batch Loading)을 비교하고, 댓글 수와 좋아요 수를 실시간으로 집계하는 방식과 미리 계산해서 저장하는 방식의 트레이드오프를 설명해주세요.
N+1 문제는 LEFT JOIN으로 한 번에 조회하거나, 서브쿼리로 집계하거나, ID 목록을 모아 IN 절로 배치 조회하는 방법이 있습니다. JOIN은 쿼리 한 번이지만 결과 행이 증가하고, 서브쿼리는 가독성이 좋지만 성능이 떨어질 수 있으며, 배치 로딩은 애플리케이션 레벨에서 두 번의 쿼리로 처리합니다. 댓글 수와 좋아요 수는 실시간 집계 시 COUNT 쿼리 부하가 크므로, 게시글 테이블에 카운터 컬럼을 두고 INSERT/DELETE 시 UPDATE하는 방식이 조회 성능에 유리합니다. 정확성이 중요하면 트랜잭션 내에서 업데이트하고, 약간의 지연을 허용하면 비동기 이벤트로 처리할 수 있습니다.
- • N+1 해결: JOIN(한 번, 결과 증가), 서브쿼리(가독성), 배치 로딩(두 번)
- • 실시간 집계는 COUNT 부하 큼, 카운터 컬럼이 조회 성능 유리
- • 카운터 업데이트: 트랜잭션(정확성) vs 비동기(성능)
- • 트레이드오프: 저장 공간 vs 조회 성능, 일관성 vs 지연시간
카운터 컬럼 방식에서 동시에 여러 사용자가 좋아요를 누를 때 발생할 수 있는 Lost Update 문제를 어떻게 해결하시겠습니까?
Q. 금융 거래 시스템에서 계좌 잔액을 관리할 때, 현재 잔액만 저장하는 State 기반 방식과 모든 거래 이력을 저장하고 집계하는 Event Sourcing 방식의 장단점을 비교하고, MySQL에서 Event Sourcing을 구현할 때 스냅샷(Snapshot) 테이블을 활용하는 이유와 설계 방법을 설명해주세요.
State 기반 방식은 조회가 빠르고 단순하지만 이력 추적이 어렵고 감사(Audit)가 불가능합니다. Event Sourcing은 완전한 이력 보존과 시점별 재구성이 가능하지만 현재 상태 조회 시 모든 이벤트를 집계해야 해서 느립니다. 금융 시스템은 감사 추적이 필수이므로 Event Sourcing이 적합합니다. 성능 문제 해결을 위해 주기적으로 스냅샷을 생성하여 특정 시점의 잔액을 저장하고, 이후 이벤트만 집계하면 조회 속도를 크게 개선할 수 있습니다. 스냅샷 테이블은 account_id, snapshot_date, balance를 저장하고 일별 또는 월별로 생성합니다.
- • State 기반: 조회 빠름, 이력 추적 어려움
- • Event Sourcing: 완전한 이력, 감사 가능, 조회 느림
- • 금융 시스템은 감사 필수 - Event Sourcing 적합
- • 스냅샷으로 성능 개선: 특정 시점 잔액 + 이후 이벤트만 집계
Event Sourcing에서 과거 이벤트에 오류가 발견되었을 때 보상 트랜잭션(Compensating Transaction)을 어떻게 설계하고 적용하시겠습니까?
Q. 공개 API 서비스에서 사용자별로 분당 요청 수를 제한하는 Rate Limiting을 구현해야 합니다. MySQL만 사용하는 방식과 Redis를 함께 사용하는 방식을 비교하고, Fixed Window, Sliding Window, Token Bucket 알고리즘 중 어떤 방식이 적합한지 정확성과 구현 복잡도 관점에서 설명해주세요.
MySQL만 사용하면 api_request_log 테이블에 타임스탬프를 저장하고 COUNT 쿼리로 확인하는데, 동시 요청 처리가 어렵고 DB 부하가 큽니다. Redis의 INCR과 EXPIRE를 사용하면 원자적 연산으로 빠르고 정확하게 처리할 수 있어 Rate Limiting에 적합합니다. Fixed Window는 구현이 단순하지만 경계에서 버스트가 발생하고, Sliding Window는 정확하지만 복잡하며, Token Bucket은 버스트를 허용하면서도 평균 속도를 제어할 수 있어 실무에서 많이 사용됩니다. Redis의 Sorted Set으로 Sliding Window를 구현하거나, 단순히 카운터로 Token Bucket을 구현할 수 있습니다.
- • MySQL: COUNT 쿼리 부하 큼, 동시성 처리 어려움
- • Redis: INCR/EXPIRE 원자적 연산, 빠르고 정확
- • Fixed Window: 단순, 경계 버스트
- • Token Bucket: 버스트 허용 + 평균 제어, 실무 적합
분산 환경에서 여러 API 서버가 있을 때 Rate Limiting의 정확성을 보장하기 위한 방법과, Redis 장애 시 Fallback 전략을 설명해주세요.
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!