레디스가 싱글스레드라서 BullMQ에서 job data가 커지면 Redis가 터진다고 단순화해서 말하는 경우가 많음
하지만 실제로는 Redis가 큰 payload 처리 때문에 막히고, 그 지연이 메모리와 네트워크 문제로 이어지며 타임아웃이나 OOM 같은 연쇄 문제로 번지는 쪽이 더 큼
job data가 커질 때 Redis에서 바로 벌어지는 일
1) 이벤트루프가 큰 값 때문에 오래 점유
BullMQ는 job을 만들 때 job data를 보통 Redis에 문자열이나 JSON 형태로 저장함
job data가 커지면 쓰기와 조회 모두에서 처리해야 하는 바이트가 늘어남 SET이나 HSET 같은 쓰기, HGET 같은 조회가 한 명령당 처리해야 하는 바이트를 키움
Redis는 각 명령을 이벤트루프에서 처리하므로 큰 payload를 처리하는 동안 다른 클라이언트 요청이 대기함 그 결과 latency spike가 나고 타임아웃이 늘며 큐 정체가 생김
2) 네트워크 병목이 Redis ↔ Worker 구간에서 커짐
큰 job data는 결국 워커로 전송돼야 함
예를 들어 job 하나가 5MB이고 워커가 동시에 많이 가져가면, 그 순간 Redis NIC와 스위치, 서버 소켓 처리량을 크게 쓰게 됨 이때 Redis CPU보다 네트워크 처리, 커널 버퍼, 소켓 대기가 병목이 될 수 있음
이 영향은 같은 Redis 인스턴스를 쓰는 다른 명령에도 전파돼 전반 지연이 악화될 수 있음
3) 메모리가 생각보다 빨리 찬다
Redis는 인메모리라 큰 payload는 곧바로 메모리 압박으로 연결됨
여기에 키나 해시, 리스트 같은 자료구조가 가지는 오버헤드가 더해짐 또 완료나 실패 같은 상태 기록도 함께 커질 수 있음
복제나 영속화가 켜져 있으면 2차 충격이 더 커짐 replica 복제를 쓰면 복제본에도 동일 데이터가 전파돼 네트워크와 메모리 부담이 늘어남 AOF를 쓰면 큰 write가 디스크에 누적되고 설정에 따라 fsync 비용이 더 커질 수 있음 RDB 스냅샷을 만들면 fork와 copy-on-write 때문에 순간 메모리 증가가 발생할 수 있음
4) big key 성격의 데이터가 삭제나 만료에서 튀는 케이스
하나의 job key가 커지는 것 자체는 UNLINK 같은 비동기 삭제로 완화될 여지가 있음
다만 구조가 복잡해지거나 큰 컬렉션 형태로 만료가 몰리면, 일시적으로 삭제나 만료 처리 지연이 튀는 상황이 생길 수 있음 이 부분은 BullMQ 설정과 Redis 버전, 내부 자료구조 구성에 따라 체감이 달라짐
5) 최악은 OOM 이후 eviction, 그리고 큐 무결성 이슈
메모리 한계를 넘으면 OOM으로 인해 enqueue가 실패할 수 있음 또 maxmemory 설정이 eviction 방식이면 큐 데이터가 지워질 수 있어서 운영상 리스크가 커짐 설계에 따라 치명적일 수 있으니 특히 주의해야 함
싱글스레드라서 정확히 무엇이 문제인가
핵심은 이거임
Redis는 보통 하나의 이벤트루프가 명령 처리를 담당함 큰 payload 한 번을 처리하는 동안 이벤트루프가 더 오래 점유됨 그래서 CPU 멀티코어를 못 써서 느려진다는 관점보다, 큰 데이터 때문에 이벤트루프 점유 시간이 늘며 전체 지연이 커진다는 관점이 더 정확함
운영에서 흔히 보이는 증상
Redis 명령 지연이 평소 12ms에서 갑자기 50500ms로 튀는 형태로 나타날 수 있음
BullMQ worker에서 getNextJob 같은 동작이 타임아웃이나 재시도로 이어지기도 함
used_memory가 급증하고 Redis로 나가는 네트워크 트래픽도 같이 늘어나는 패턴이 관찰될 수 있음 AOF나 RDB가 켜져 있으면 디스크 IO가 함께 튀면서 latency가 더 악화될 수 있음
대응 패턴
1순위는 job data를 Redis에 큰 본문으로 넣지 않는 것임
대부분은 본문을 S3나 GCS 같은 오브젝트 스토리지, 또는 DB나 Blob storage에 저장하고 job에는 objectKey나 id 같은 참조만 넣는 방식이 안전함 예를 들어 payloadRef 같은 형태로 외부 위치나 키만 기록
BullMQ 설정으로 완료와 실패 같은 job 보관 수나 보관 시간을 줄이는 것도 중요함 상태 기록이 쌓이면 위험이 더 커질 수 있음
Redis는 가능하면 큐 전용으로 분리하는 것을 권장함 캐시나 세션 같은 다른 성격의 데이터가 같이 있으면 같이 영향을 받는 구성이 되기 쉬움
큰 payload를 피할 수 없으면 압축 같은 우회가 선택지가 될 수 있음 다만 압축은 CPU 비용과 지연 트레이드오프가 생김 또 워커 동시성이나 프리패치 같은 항목을 줄여 한 번에 가져가는 양을 제한하는 접근도 함께 고려하면 됨