본문 바로가기
CS/NoSQL

[Redis] Multiplexing & Event Loop

by persi0815 2026. 1. 22.

0. 개요

Redis의 내부 작동 원리에 대해 학습하다 multiplexer와 event loop 개념이 나와서 이에 대해 정리하고자 정리한다.

 

1. Multiplexing이란? 

멀티플렉싱의 어원은 '다중화'로,

I/O 관점에서는 '단 하나의 스레드가 수천개의 소켓(file descripter)를 동시에 감시하는 기술'을 의미한다. -> 시스템 자원 효율화

ex) epoll, kqueue

HTTP 관점에서는 '하나의 통신로를 활용해 여러개의 독립된 신호를 전송하는 기술'을 의미한다. -> 지연시간 단축

ex) http 1.1 keep alive, http 2.0 stream

 

I/O Multiplexing의 주요 매커니즘은 다음과 같다. 

방식 특징 알고리즘 복잡도
select 감시 소켓 수 제한(보통 1024개)이 있고, 이벤트 발생한 FD에만 '체크 표시'하고 전체를 앱에 돌려줌. 앱은 매번 전체 리스트를 훑으며 체크 표시를 찾음 O(N)
poll 구조체 배열을 사용해 select의 개수 제한 문제를 해결했지만, 여전히 전체 탐색 O(N)
epoll / kqueue 현대적 방식. 이벤트가 발생한 소켓들만 따로 Ready List를 만들어 전달하면, 앱은 해당 리스트만 확인하며 처리. O(M). O(1)라고 하기도

 

더 적은 자원 (메모리와 CPU)으로 더 많은 client를 처리할 수 있어 대규모 트래픽을 처리해야 하는 서버 구현시 사용한다.

 

 

2. Event Loop란? 

먼저 Event란 변화가 발생한 파일과 관련된 "상태 변화"이다. 

Event Loop은 말 그대로 loop을 이용해 적은 스레드로 반복적으로 이벤트를 처리하는 방식이다. 

 

Event Loop의 동작 방식은 다음과 같다. 

 

1. 이벤트 등록 (Interest List)

: event와 사건 발생 시 실행할 코드(callback/handler)를 연결해 OS에게 등록 요청을 epoll_ctl()을 통해 보내면,

OS는 Interest List을 메모리에 생성하고 I/O 멀티플렉서(epoll, kqueue 등)에게 전달한다. -> 감시 시작

- Interest List는 내부적으로 Red-Black Tree로 관리되어 삽입/삭제가 O(log N)으로 빠릅

 

2. 이벤트 대기 (OS Notification)

: Loop는 (Task Queue에 처리할 event가 없으면) epoll_wait() 시스템 콜을 호출해 잠시 sleep(=wait)하며 운영체제의 신호를 대기한다.

 

3. 이벤트 발생 및 큐잉 (실제 이벤트 발생)

: OS는 event를 감지해 모아두었다가 Loop이 epoll_wait() 시스템 콜을 호출할 때 모아둔 Ready List를 전달하고, Loop는 전달받은 event들(=Ready List에 모아둔 event)을 즉시 Event Queue(=Task Queue)에 넣는다. 

 

4. 이벤트 처리 (execution)

: 하나의 이벤트라도 Queue에 들어오면 즉시 Loop가 Queue에서 일감을 꺼내어 적절한 handler를 찾아 실행한다.

 

Loop는 커널(OS)에 Interest List를 등록하고 즉시 제어권을 돌려받아 신호가 올때까지 cpu를 점유하며 대기하지 않고, Task Queue에 쌓여있는 작업들을 처리하거나 작업 없을 시 sleep한다.=> Non-Blocking

이처럼 논블로킹 방식으로 작동해 Single Thread로도 cpu를 효율적으로 사용하며 많은 작업들을 처리할 수 있게 되었다.

 

event loop가 없다면, OS는 사용자 코드(handler)를 직접 실행시킬 수 없기에 스레드를 만들어서 handler에게 붙여야 한다. 그렇게 되면 Thread-per-connection처럼 동작해 사용자 증가시 빈번한 context switching으로 cpu 오버헤드와 메모리 부족을 겪을 수 있고, 작업 실행보다 스레드 생성/삭제에 많은 자원을 쓰게 된다. 그래서 사용자 레벨의 Single Thread로 동작하는 Event Loop을 만들어 두고 Loop에게 이벤트를 전달하는 것이다. 

 

 

3. Redis에서의 작동 방식

4. Redis Streams 작동 예시

1. 메시지 대기(XREAD BLOCK)

1-1) Client가 네트워크 통해 데이터를 보냄

1-2) 루프는 epoll_wait()을 통해 Read 신호를 감지하고 Task Queue에 넣음

1-3) Dispatcher를 통해 Handler를 찾고 Event Processor가 해당 Client가 Blocked 상태임을 표시해두고, 해당 Client의 소켓 정보를 Waiting List에 보관

1-4) Loop는 XREAD 명령어를 처리하는 Handler를 즉시 종료하고 제어권을 회수해 다음 작업 큐에서 꺼내어 실행

 

2. 데이터 유입(XADD)

2-1) Client가 네트워크 통해 데이터를 보냄

2-2) 루프는 epoll_wait()을 통해 Read 신호를 감지하고 Task Queue에 넣음

2-3) Dispatcher를 통해 Handler를 찾고 Event Processor가 해당 stream key에 데이터를 저장, Waiting List 체크 후 해당 stream 대기 중인 consumer 소켓 찾아서 Write 이벤트 생성해 Task Queue에 넣음

2-4) 루프가 다음 바퀴 돌때, 큐에서 해당 이벤트 꺼내 Consumer에게 메시지 전송