# Thundering Herd: 10,000개 스레드가 동시에 깨어날 때 서버가 멈추는 이유
Table of Contents
잠자는 사자의 코털을 동시에 건드린다면?
서버 개발자라면 **“컨텍스트 스위칭(Context Switch) 비용이 비싸다”**는 말을 한 번쯤 들어보셨을 겁니다. Thundering Herd(천둥처럼 달려드는 짐승 떼) 문제는 바로 이 비용을 극대화하여 멀쩡하던 서버를 순식간에 마비시키는 최악의 패턴 중 하나입니다.
상황을 한 번 상상해 봅시다.
- 하나의 먹이(이벤트)를 기다리는 하이에나(스레드/프로세스) 10,000마리가 잠자고 있습니다. (
wait상태) - 먹이 하나가 바닥에 툭 떨어집니다. (이벤트 발생)
- 10,000마리가 그 소리를 듣고 동시에 눈을 뜨며(Wake up) 먹이를 향해 전력 질주합니다.
- 가장 빠른 한 마리가 먹이를 채갑니다. (이벤트 처리)
- 나머지 9,999마리는? “아, 늦었네” 하고 허탈해하며 다시 잠을 자러 갑니다. (다시
wait)
이 짧은 순간 발생한 10,000번의 기상과 9,999번의 헛걸음. 이것이 바로 CPU를 낭비하고 시스템 전체 성능을 급락시키는 Thundering Herd 현상입니다.
어디서 주로 발생할까요?
1. Accept Queue (고전적인 문제)
이는 웹 서버(Nginx, Apache) 초창기 시절의 고질병이었습니다.
여러 워커 프로세스가 하나의 소켓(listen fd)을 공유하고 accept()를 호출하며 대기합니다. 새로운 연결 요청이 하나 들어오면, 커널이 대기 중이던 모든 워커를 다 깨워버리는 방식이었죠.
다행히 현대의 리눅스 커널(2.6 이상)은 EPOLLEXCLUSIVE 플래그 등을 도입하여 “이벤트가 발생해도 하나의 프로세스만 깨우는” 기능을 제공함으로써 이 문제를 OS 레벨에서 해결했습니다.
2. Cache Stampede (현대적인 문제)
오늘날 MSA(Microservices Architecture) 환경에서 더 자주 겪게 되는 문제입니다. 인기 있는 게시글의 캐시가 만료되는 순간(TTL Expire), 그 찰나에 수천 개의 요청이 동시에 들어옵니다. 모든 요청이 “어? 캐시가 없네? DB에서 읽어와야지”라고 판단합니다. 결국 수천 개의 스레드가 동시에 데이터베이스로 돌진합니다. 결과는? DB 서버의 CPU가 100%를 치고 사망합니다.
해결책: “줄을 서시오”
이 문제를 해결하는 핵심은 **“동시성(Concurrency)을 제어”**하여 불필요한 경쟁을 없애는 것입니다.
1. 지터(Jitter) 추가하기
모두가 똑같은 시간에 깨어나거나 똑같은 시간에 캐시가 만료되지 않도록, 시간에 랜덤 값을 섞는 것입니다.
예를 들어, 캐시 만료 시간을 TTL + Random(0~10s)로 설정하면, 수천 개의 캐시 키가 동시에 만료되어 모든 서버가 동시에 DB를 타격하는 상황을 막을 수 있습니다.
2. 한 놈만 보낸다 (Mutex Lock / Promise)
캐시가 없음을 감지했을 때, 가장 먼저 도착한 요청 하나만 “내가 DB 갔다 올게, 너희는 기다려”라고 깃발(Lock)을 듭니다. 나머지 후속 요청들은 그 깃발을 보고 “아, 누가 가지러 갔구나” 하고 잠시 대기하거나(Spin Lock), 그 요청이 가져온 결과(Promise/Future)를 구독하고 기다립니다.
3. 커널 레벨의 지원 (epoll)
리눅스의 epoll 시스템 콜을 사용할 때 EPOLLEXCLUSIVE 플래그를 사용하면, 이벤트가 발생했을 때 대기 중인 프로세스 중 하나만 깨우도록 커널에 지시할 수 있습니다. Nginx 같은 고성능 서버들은 이미 이 기능을 적극적으로 활용하여 불필요한 컨텍스트 스위칭을 최소화하고 있습니다.
마치며
Thundering Herd는 **“과유불급”**을 시스템적으로 보여주는 대표적인 사례입니다. 이벤트에 빠르게 반응하는 건 중요하지만, 1개의 이벤트를 처리하기 위해 10,000개의 프로세스가 일어나는 건 명백한 자원 낭비입니다.
여러분의 시스템이 이유 없이 CPU 사용량이 튀거나, 특정 캐시 만료 시점마다 DB가 휘청거린다면 의심해 보세요. 혹시 잠자던 짐승 떼가 천둥소리를 내며 의미 없는 달리기를 하고 있지는 않은지 말입니다.