Redis는 단일 스레드로 동작합니다. 처음 들으면 느릴 것 같지만, 실제로는 초당 수십만 건의 요청을 처리합니다. 그 비결은 이벤트 루프와 I/O 다중화에 있습니다.
Redis의 메인 커맨드 처리 루프는 단일 스레드로 동작합니다. 모든 읽기/쓰기 명령이 하나의 스레드에서 순서대로 실행됩니다.
클라이언트 A: SET user:1 "철수" ─┐
클라이언트 B: GET user:1 ─┤→ [이벤트 큐] → 순서대로 처리
클라이언트 C: INCR counter ─┘
하나의 명령이 실행 중일 때 다른 명령은 대기합니다. 덕분에 Race Condition이나 Deadlock이 발생하지 않습니다.
Race Condition: 두 스레드가 공유 데이터에 동시에 접근해 결과가 실행 순서에 따라 달라지는 문제. Redis는 단일 스레드이므로 이 문제가 없습니다.
Deadlock: 두 스레드가 서로 상대방이 보유한 자원을 기다리며 무한 대기하는 상태. Lock을 사용하지 않으므로 발생하지 않습니다.
단일 스레드임에도 빠른 핵심 이유는 두 가지입니다.
첫째, 데이터가 메모리에 있습니다. 디스크 I/O가 없으므로 명령 하나의 처리 시간이 마이크로초 단위입니다.
둘째, I/O 다중화를 사용합니다. 하나의 스레드가 수천 개의 클라이언트 소켓을 동시에 감시합니다. OS 커널의 epoll(Linux) 또는 kqueue(macOS)를 사용해 데이터가 도착한 소켓만 골라 처리합니다.
단일 스레드 OS 커널
──────────── ──────────────────────────────
이벤트 루프 ──epoll──▶ 소켓 1 (대기)
소켓 2 (데이터 있음) ◀─ 처리
소켓 3 (대기)
소켓 4 (데이터 있음) ◀─ 처리
...
epoll: Linux 커널이 제공하는 I/O 이벤트 감시 메커니즘. 수천 개의 파일 디스크립터 중 이벤트가 발생한 것만 알려주므로 불필요한 폴링 없이 효율적으로 동작합니다.
Redis의 이벤트 루프는 다음 순서로 반복됩니다.
1. epoll_wait 호출 → 이벤트 도착 대기
2. 이벤트 있음 → 처리할 소켓 목록 확보
3. 각 소켓에서 명령 읽기
4. 명령 실행 (메모리 연산)
5. 결과 쓰기 버퍼에 추가
6. 다음 루프
CPU가 실제로 연산하는 시간은 매우 짧습니다. 대부분의 시간은 epoll_wait에서 다음 이벤트를 기다리는 상태입니다.
Redis 6.0부터 네트워크 I/O 부분에 멀티 스레드를 도입했습니다. 단, 핵심인 명령 실행 자체는 여전히 단일 스레드입니다.
IO 스레드 1: 소켓 읽기 → 파싱 ──┐
IO 스레드 2: 소켓 읽기 → 파싱 ──┤→ 메인 스레드: 명령 실행
IO 스레드 3: 소켓 읽기 → 파싱 ──┘
명령 실행의 원자성은 유지하면서, 대용량 데이터 전송 시 발생하는 I/O 병목을 줄였습니다.
Redis는 모든 Key-Value 쌍을 메모리에 올려둡니다. 내부적으로 해시 테이블로 Key를 관리하며, O(1)에 가까운 속도로 조회합니다.
전역 해시 테이블
┌──────────────────────────────┐
│ "user:1000" → 해시 객체 │
│ "session:abc" → 문자열 객체 │
│ "ranking" → Sorted Set 객체 │
└──────────────────────────────┘
메모리 사용량이 설정한 maxmemory에 도달하면 설정한 Eviction 정책에 따라 키를 삭제합니다.
Eviction: 메모리 한계에 도달했을 때 기존 데이터를 삭제하는 정책. allkeys-lru(전체 키 중 최근에 안 쓴 것), volatile-lru(만료 시간 있는 키 중 최근에 안 쓴 것) 등을 설정할 수 있습니다.
단일 스레드라는 특성상 실행 시간이 긴 명령은 전체 Redis를 블로킹합니다.
KEYS * — 전체 키 스캔, 키가 수백만 개면 수 초가 걸림SMEMBERS (수백만 원소 Set) — 대량 데이터 반환SORT — 대용량 데이터 정렬이런 명령을 운영 환경에서 쓰면 그동안 다른 모든 클라이언트가 대기합니다. KEYS 대신 SCAN을 사용하는 이유입니다.
SCAN: 커서 기반으로 키를 조금씩 나눠 스캔하는 명령. 한 번 호출에 처리 키 수를 제한할 수 있어 블로킹을 피할 수 있습니다.