Redis 미드레벨 CS 기초 기술면접
새 면접Q. Redis는 TCP 기반의 클라이언트-서버 모델을 사용합니다. TCP의 3-way handshake와 4-way handshake 과정을 설명하고, Redis 클라이언트가 커넥션 풀(Connection Pool)을 사용하는 이유와 적절한 풀 크기를 결정하는 기준을 설명해주세요.
TCP 3-way handshake는 SYN -> SYN-ACK -> ACK 순서로 연결을 수립하고, 4-way handshake는 FIN -> ACK -> FIN -> ACK 순서로 연결을 종료합니다. Redis 클라이언트가 커넥션 풀을 사용하는 이유는 매번 새로운 TCP 연결을 생성하면 handshake 오버헤드가 발생하고 TIME_WAIT 상태의 소켓이 누적되어 리소스를 낭비하기 때문입니다. 적절한 풀 크기는 애플리케이션의 동시 요청 수, Redis 서버의 maxclients 설정, 네트워크 레이턴시를 고려하여 결정하며, 일반적으로 애플리케이션 스레드 수의 1~2배 정도로 설정합니다. 과도하게 큰 풀은 Redis 서버의 파일 디스크립터를 고갈시킬 수 있으므로 주의해야 합니다.
- • TCP handshake 과정 이해
- • 커넥션 풀을 통한 handshake 오버헤드 감소
- • TIME_WAIT 상태 관리
- • 풀 크기 결정 요소(동시 요청 수, maxclients, 레이턴시)
Redis 서버에서 maxclients 제한에 도달했을 때 발생하는 현상과, 이를 모니터링하고 대응하는 방법은 무엇인가요?
Q. Redis의 List 자료구조는 내부적으로 quicklist라는 구조를 사용합니다. quicklist가 무엇이며, 이전에 사용되던 ziplist와 linkedlist의 단점을 어떻게 보완했는지 설명해주세요. 또한 list-max-ziplist-size 설정의 의미와 영향을 설명해주세요.
quicklist는 ziplist 노드들을 이중 연결 리스트로 연결한 하이브리드 자료구조입니다. 순수 linkedlist는 각 노드마다 포인터 오버헤드가 크고 메모리 단편화가 발생하며, 순수 ziplist는 연속된 메모리를 사용해 메모리 효율적이지만 크기가 커지면 realloc 비용이 증가하는 단점이 있었습니다. quicklist는 작은 크기의 ziplist들을 linkedlist로 연결하여 메모리 효율성과 삽입/삭제 성능의 균형을 맞췄습니다. list-max-ziplist-size는 각 quicklist 노드의 최대 크기를 제어하며, 양수일 때는 엔트리 개수, 음수일 때는 바이트 크기를 의미합니다. 이 값이 작으면 메모리는 더 사용하지만 삽입/삭제가 빠르고, 크면 메모리는 절약되지만 realloc 비용이 증가합니다.
- • quicklist는 ziplist + linkedlist 하이브리드
- • 메모리 효율성과 성능의 트레이드오프
- • list-max-ziplist-size 설정의 영향
- • realloc 비용과 포인터 오버헤드 균형
LPUSH와 RPUSH의 시간 복잡도가 O(1)인 이유를 quicklist 구조와 연관지어 설명해주세요.
Q. Redis는 I/O 멀티플렉싱을 위해 epoll(Linux), kqueue(BSD), select 등을 사용합니다. epoll의 동작 원리와 select 대비 장점을 설명하고, Redis가 epoll을 사용할 때 Level-Triggered와 Edge-Triggered 모드 중 어떤 것을 선택하며 그 이유는 무엇인지 설명해주세요.
epoll은 커널 공간에 관심 파일 디스크립터 목록을 유지하여 매번 전체 fd set을 복사하는 select의 오버헤드를 제거합니다. select는 O(n) 시간에 모든 fd를 순회하지만, epoll은 이벤트가 발생한 fd만 반환하여 O(1)에 가까운 성능을 제공합니다. Redis는 Level-Triggered 모드를 사용하는데, 이는 읽을 데이터가 남아있으면 계속 이벤트가 발생하여 프로그래밍이 간단하고 안전하기 때문입니다. Edge-Triggered는 상태 변화 시점에만 이벤트가 발생하여 더 효율적이지만, 모든 데이터를 한 번에 읽지 않으면 이벤트를 놓칠 수 있어 non-blocking I/O와 복잡한 버퍼 관리가 필요합니다. Redis의 단일 스레드 이벤트 루프 모델에서는 Level-Triggered가 더 적합합니다.
- • epoll의 커널 공간 fd 관리와 O(1) 성능
- • select 대비 확장성 우위
- • Level-Triggered의 안전성과 프로그래밍 단순성
- • Redis 이벤트 루프와의 적합성
Redis 6.0에서 도입된 I/O 스레드는 어떤 문제를 해결하기 위한 것이며, 기존 단일 스레드 모델과 어떻게 조화를 이루나요?
Q. Redis의 SCAN 명령어는 커서 기반 반복자를 사용하여 키를 순회합니다. SCAN이 내부적으로 사용하는 reverse binary iteration 알고리즘의 원리를 설명하고, rehashing 중에도 키를 누락하거나 중복 반환하지 않는 메커니즘을 설명해주세요.
reverse binary iteration은 해시 테이블의 인덱스를 이진수로 표현했을 때, 최상위 비트부터 증가시키는 방식으로 순회합니다. 일반적인 순차 증가와 달리, 이 방식은 해시 테이블이 확장되거나 축소될 때 이미 방문한 버킷과 아직 방문하지 않은 버킷을 명확히 구분할 수 있습니다. Redis는 두 개의 해시 테이블(ht[0], ht[1])을 유지하며 rehashing 중에는 두 테이블 모두를 검사합니다. 커서의 비트 패턴을 통해 작은 테이블의 위치와 큰 테이블의 대응 위치를 동시에 추적하므로, 데이터가 이동 중이어도 누락이나 중복 없이 순회할 수 있습니다. 다만 rehashing 중에는 동일한 키가 두 번 반환될 수 있으므로 클라이언트에서 중복 제거가 필요합니다.
- • reverse binary iteration의 최상위 비트 우선 순회
- • rehashing 중 두 해시 테이블 동시 검사
- • 커서 비트 패턴을 통한 위치 추적
- • 클라이언트 측 중복 제거 필요성
SCAN의 COUNT 옵션이 정확한 개수를 보장하지 않는 이유와, 실제로 반환되는 키의 개수가 어떻게 결정되는지 설명해주세요.
Q. Redis는 ACID 특성 중 일부만 지원합니다. Redis의 MULTI/EXEC 트랜잭션이 전통적인 RDBMS의 트랜잭션과 다른 점을 ACID 관점에서 분석하고, Redis 트랜잭션에서 롤백(Rollback)이 지원되지 않는 이유와 이에 따른 실무적 대응 방안을 설명해주세요.
Redis의 MULTI/EXEC는 Atomicity(원자성)와 Isolation(격리성)은 제공하지만 Durability(지속성)는 제한적이고 Consistency(일관성)는 개발자가 보장해야 합니다. Redis 트랜잭션은 명령어들을 큐에 저장했다가 EXEC 시점에 순차적으로 실행하며, 중간에 다른 클라이언트의 명령어가 끼어들지 않습니다. 하지만 실행 중 에러가 발생해도 롤백되지 않고 나머지 명령어는 계속 실행됩니다. Redis가 롤백을 지원하지 않는 이유는 명령어 실패가 대부분 프로그래밍 에러(잘못된 타입 연산 등)이며, 롤백 구현은 성능 오버헤드와 복잡성을 증가시키기 때문입니다. 실무에서는 Lua 스크립트를 사용하여 조건 검증 후 실행하거나, WATCH를 통한 낙관적 락으로 충돌을 감지하고, 애플리케이션 레벨에서 보상 트랜잭션을 구현하는 방식으로 대응합니다.
- • ACID 중 A와 I는 제공, D와 C는 제한적
- • 명령어 큐잉 후 순차 실행
- • 롤백 미지원의 성능과 단순성 트레이드오프
- • Lua 스크립트와 보상 트랜잭션을 통한 대응
Redis 트랜잭션에서 WATCH를 사용할 때, 감시 중인 키가 변경되면 어떤 일이 발생하며, 이를 처리하는 재시도 로직을 어떻게 설계해야 하나요?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!