Python 시니어 CS 기초 기술면접
새 면접Q. B-Tree와 B+Tree의 구조적 차이와 각각의 특성을 설명하고, 데이터베이스 인덱스로 B+Tree가 선호되는 이유를 설명해주세요. 또한 Python으로 대용량 데이터를 처리할 때 메모리 기반 자료구조 대신 디스크 기반 B+Tree를 고려해야 하는 시나리오와, 이를 구현하거나 활용할 수 있는 라이브러리를 제시해주세요.
B-Tree는 모든 노드에 키와 데이터를 저장하지만, B+Tree는 리프 노드에만 데이터를 저장하고 내부 노드는 인덱스 역할만 합니다. B+Tree는 리프 노드가 연결 리스트로 연결되어 범위 검색에 유리하며, 내부 노드가 더 많은 키를 저장할 수 있어 트리 높이가 낮아집니다. 데이터베이스는 순차 스캔과 범위 쿼리가 빈번하고, 디스크 I/O를 최소화해야 하므로 B+Tree를 선호합니다. Python에서는 수억 건의 정렬된 데이터를 메모리에 올릴 수 없을 때 LMDB, BerkeleyDB 같은 라이브러리를 사용하거나, SQLite를 임베디드 인덱스로 활용할 수 있습니다.
- • B+Tree는 리프 노드에만 데이터 저장, 내부 노드는 인덱스만 보유
- • 리프 노드 간 연결 리스트로 범위 검색 최적화
- • 디스크 I/O 최소화와 캐시 효율성이 데이터베이스 인덱스 선택 이유
- • Python에서 LMDB, BerkeleyDB, SQLite 등으로 디스크 기반 인덱싱 구현 가능
LSM Tree(Log-Structured Merge Tree) 기반 인덱스 구조와 B+Tree의 차이점을 비교하고, 쓰기 집약적인 워크로드에서 어떤 구조가 유리한지 설명해주세요.
Q. TCP의 흐름 제어(Flow Control)와 혼잡 제어(Congestion Control)의 차이를 설명하고, 혼잡 제어 알고리즘인 Slow Start, Congestion Avoidance, Fast Retransmit, Fast Recovery의 동작 원리를 설명해주세요. Python으로 대용량 파일을 전송하는 서버를 구현할 때 TCP 소켓 옵션(SO_SNDBUF, TCP_NODELAY, TCP_CORK 등)을 어떻게 튜닝하여 성능을 최적화할 수 있는지 설명해주세요.
흐름 제어는 수신자의 처리 능력에 맞춰 송신 속도를 조절하는 것이고(Sliding Window), 혼잡 제어는 네트워크 전체의 혼잡도에 따라 송신 속도를 조절합니다. Slow Start는 cwnd를 지수적으로 증가시키고, Congestion Avoidance는 선형 증가로 전환하며, Fast Retransmit은 3개의 중복 ACK로 즉시 재전송하고, Fast Recovery는 혼잡 윈도우를 절반으로 줄인 후 선형 증가합니다. Python에서 대용량 파일 전송 시 SO_SNDBUF를 크게 설정하여 버퍼링을 늘리고, TCP_NODELAY는 Nagle 알고리즘을 비활성화하여 지연을 줄이며, TCP_CORK는 여러 작은 패킷을 모아 전송하여 효율성을 높입니다. sendfile 시스템 콜을 사용하면 커널 공간에서 직접 파일을 전송하여 컨텍스트 스위칭을 줄일 수 있습니다.
- • 흐름 제어는 수신자 기준, 혼잡 제어는 네트워크 전체 기준
- • Slow Start(지수 증가) → Congestion Avoidance(선형 증가) → 패킷 손실 시 Fast Retransmit/Recovery
- • SO_SNDBUF로 송신 버퍼 크기 조정, TCP_NODELAY로 즉시 전송, TCP_CORK로 패킷 병합
- • sendfile 시스템 콜로 제로 카피 최적화 가능
HTTP/2의 멀티플렉싱과 TCP의 Head-of-Line Blocking 문제를 설명하고, 이를 해결하기 위한 HTTP/3와 QUIC 프로토콜의 접근 방식을 비교해주세요.
Q. 가상 메모리의 페이징 기법에서 페이지 교체 알고리즘(FIFO, LRU, LFU, Clock)의 동작 원리와 각각의 장단점을 설명하고, Python 애플리케이션에서 메모리 사용 패턴이 페이지 폴트에 미치는 영향을 설명해주세요. 또한 대용량 데이터 처리 시 메모리 맵 파일(mmap)을 사용하는 것이 일반 파일 I/O보다 유리한 이유와 주의사항을 설명해주세요.
FIFO는 가장 오래된 페이지를 교체하지만 Belady's Anomaly가 발생할 수 있고, LRU는 최근 사용 시간 기반으로 최적에 가깝지만 구현 비용이 높습니다. LFU는 참조 횟수 기반이지만 초기 참조가 많은 페이지가 계속 남고, Clock은 LRU의 근사 알고리즘으로 참조 비트를 순환 검사합니다. Python에서 큰 리스트나 딕셔너리를 순차 접근하면 locality가 좋아 페이지 폴트가 적지만, 무작위 접근은 thrashing을 유발할 수 있습니다. mmap은 파일을 가상 메모리에 매핑하여 read/write 시스템 콜 없이 메모리 접근처럼 사용할 수 있고, OS의 페이지 캐시를 활용하여 성능이 향상되지만, 파일 크기가 가용 메모리보다 크면 페이지 폴트가 빈번해질 수 있습니다.
- • LRU가 이론적으로 최적에 가깝지만 구현 비용 높음, Clock은 실용적 대안
- • Python의 메모리 접근 패턴(순차 vs 무작위)이 locality와 페이지 폴트에 영향
- • mmap은 시스템 콜 오버헤드 제거, OS 페이지 캐시 활용으로 성능 향상
- • 대용량 파일 mmap 시 페이지 폴트 증가 가능성 고려 필요
Copy-on-Write(COW) 메커니즘의 동작 원리를 설명하고, Python의 multiprocessing에서 fork 방식이 COW를 활용하는 방법과 메모리 효율성에 대해 설명해주세요.
Q. 데이터베이스 트랜잭션의 격리 수준(Isolation Level) 4단계(Read Uncommitted, Read Committed, Repeatable Read, Serializable)를 설명하고, 각 수준에서 발생할 수 있는 문제(Dirty Read, Non-Repeatable Read, Phantom Read)를 비교해주세요. Python 애플리케이션에서 PostgreSQL과 MySQL의 기본 격리 수준 차이로 인해 발생할 수 있는 실무 문제와 해결 방법을 설명해주세요.
Read Uncommitted는 커밋되지 않은 데이터를 읽을 수 있어 Dirty Read가 발생하고, Read Committed는 커밋된 데이터만 읽지만 Non-Repeatable Read가 발생합니다. Repeatable Read는 트랜잭션 시작 시점의 스냅샷을 보장하지만 Phantom Read가 발생할 수 있고, Serializable은 모든 문제를 방지하지만 성능이 저하됩니다. PostgreSQL은 기본이 Read Committed이고 MVCC로 Repeatable Read에서도 Phantom Read를 방지하지만, MySQL(InnoDB)은 Repeatable Read가 기본이며 Next-Key Lock으로 Phantom Read를 방지합니다. Python 애플리케이션에서 두 DB를 혼용할 때 동일한 코드가 다른 동작을 할 수 있으므로, 명시적으로 격리 수준을 설정하거나 애플리케이션 레벨에서 락을 관리해야 합니다.
- • 격리 수준이 높을수록 동시성 문제는 줄지만 성능과 동시성은 저하
- • PostgreSQL은 Read Committed 기본, MySQL은 Repeatable Read 기본
- • MVCC와 락 메커니즘 차이로 동일 코드가 다른 동작 가능
- • 명시적 격리 수준 설정 또는 애플리케이션 레벨 락으로 일관성 보장
낙관적 락(Optimistic Lock)과 비관적 락(Pessimistic Lock)의 차이를 설명하고, Django ORM이나 SQLAlchemy에서 각각을 구현하는 방법과 적합한 사용 시나리오를 설명해주세요.
Q. 분산 시스템에서 일관된 해싱(Consistent Hashing)의 개념과 동작 원리를 설명하고, 기존 해시 함수 대비 노드 추가/삭제 시 어떤 이점이 있는지 설명해주세요. Python으로 캐시 서버 클러스터나 샤딩 시스템을 구현할 때 일관된 해싱을 적용하는 방법과, Virtual Node를 사용하여 부하 분산을 개선하는 전략을 설명해주세요.
일관된 해싱은 해시 공간을 원형 링으로 구성하고 서버와 키를 동일한 해시 함수로 매핑하여, 키는 시계 방향으로 가장 가까운 서버에 할당됩니다. 기존 해시 함수는 노드 변경 시 대부분의 키가 재배치되지만(O(N)), 일관된 해싱은 평균 K/N개의 키만 이동합니다(K는 전체 키 수, N은 노드 수). Python에서 bisect 모듈로 정렬된 서버 목록에서 이진 탐색으로 O(log N)에 서버를 찾을 수 있습니다. Virtual Node는 각 물리 서버를 여러 개의 가상 노드로 분할하여 링에 배치함으로써 부하를 더 균등하게 분산시키고, 서버 성능 차이를 반영할 수 있습니다. Redis Cluster, Cassandra 등이 이 방식을 사용합니다.
- • 해시 링 구조로 노드 변경 시 최소한의 키만 재배치(K/N)
- • bisect 모듈로 O(log N) 시간에 담당 서버 탐색
- • Virtual Node로 부하 균등 분산 및 서버 이질성 처리
- • Redis Cluster, Cassandra 등 실제 분산 시스템에서 활용
Rendezvous Hashing(Highest Random Weight)과 Consistent Hashing의 차이점을 설명하고, 각각의 트레이드오프와 적합한 사용 사례를 비교해주세요.
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!