DB 요청이 몰려 실행 대기 시간이 길어집니다. 동시에 실행할 작업 수와 기다리게 할 요청 수를 어떻게 제한하나요?
잠깐, 답변을 보기 전에
먼저 내 말로 설명해 볼까요?
답변과 점검 포인트 펼치기답변 접기
구두 답변
정의를 외우기보다, 원리와 조건을 연결해 설명해 보세요.
먼저 하위 시스템이 안정적으로 처리하는 양과 요청이 기다릴 수 있는 시간을 보겠습니다. 동시 실행 수는 자원을 얼마나 함께 쓰게 할지이고, 대기열 크기는 넘치는 작업을 얼마나 보관할지입니다. 둘을 크게 잡는다고 처리 능력이 늘지는 않습니다. 오히려 실패할 요청을 오래 붙잡을 수 있습니다.
실행 제한이 20이면 최대 20개가 DB 작업을 진행하고, 대기열 상한이 100이면 추가 요청 100개까지 보관하는 식입니다. 이 숫자는 예시일 뿐 권장 설정이 아닙니다. 대기열을 1,000개로 늘려도 DB가 빨라지지는 않고, 요청이 보유한 메모리와 기다린 시간만 늘 수 있습니다.
DB 요청을 예로 들면 부하를 올리면서 쿼리 시간과 연결 대기 시간을 나눠 측정하겠습니다. 처리량은 더 늘지 않는데 대기와 p99만 커지는 구간보다 낮게 실행 수를 잡는 것이 출발점입니다. DB 연결 수가 상한의 단서일 수는 있어도, 다른 서비스가 같은 DB를 쓰고 있으므로 연결 수 그대로를 최적값으로 보지는 않겠습니다. 긴 작업과 짧은 작업이 섞이면 긴 작업이 모두 차지하지 못하도록 풀을 분리하는 것도 방법입니다.
대기열은 요청의 남은 데드라인과 메모리 예산 안에서 정하겠습니다. 이미 기다리는 작업만으로도 제한 시간을 소진할 것 같다면 새 요청을 즉시 거절하는 편이 유효 처리량에 낫습니다. 재시도 가능한 오류와 적절한 간격을 안내하고, 생산자가 멈출 수 있는 내부 파이프라인은 상위로 대기를 전달하겠습니다. 마지막으로 평상시뿐 아니라 하위 장애와 회복을 테스트해, 큐가 비워지는 시간과 성공 요청 수를 확인하겠습니다.
받아들일 양과 버릴 조건을 분리합니다
실행 중인 작업 수를 세마포어나 제한된 실행기로 통제하는 것은 동시성 제한(concurrency limiting)입니다. 요청을 시스템 안으로 받아도 되는지 판단하는 것을 진입 제어(admission control)라고 부릅니다. 과부하에서 일부 요청을 거절하거나 오래된 작업을 버려 나머지의 응답 목표를 지키는 것은 부하 차단(load shedding)입니다. 생산자에게 대기나 감속 신호를 전달하는 백프레셔와 목적은 연결되지만 동작은 다릅니다. 큐만 제한하면 생산자가 즉시 재시도해 바깥에 더 큰 큐를 만들 수 있으므로, 거절 이후의 행동까지 포함해야 실제 유입을 줄일 수 있습니다.
예를 들어 안정 상태에서 초당 200개를 완료하고 요청 하나가 시스템 안에 평균 0.1초 머문다면, 평균 진행 중 요청은 약 20개입니다. 안정적인 흐름에서 평균 개수는 처리율과 평균 체류 시간의 곱이라는 관계를 리틀의 법칙(Little’s law)이라고 합니다. 다만 여기서 진행 중 개수는 대기와 실행을 합한 값입니다. 이를 곧바로 워커 20개나 큐 20개라는 권장 설정으로 옮기면 안 됩니다. 평균 관계는 p99를 보장하지 않고, 유입이 처리량을 계속 넘어서 적체가 커지는 상태에는 정상 상태의 계산을 그대로 적용할 수 없습니다. 실제 작업 길이와 순간 유입 분포를 함께 측정하는 이유입니다.
대기 상한은 시간과 개수 양쪽으로 둡니다. 대기 중인 요청이 100개이고 안정적인 완료율이 초당 200개라면, 단순 계산으로도 뒤 요청은 약 0.5초를 기다릴 수 있습니다. 서비스 시간 편차가 크면 더 오래 걸릴 수 있으므로 이 값은 보장이 아니라 거절 판단의 단서입니다. 이미 남은 시간이 0.2초인 요청을 그 큐 뒤에 넣는 것은 성공 가능성이 낮은 작업에 메모리를 쓰는 일입니다. 요청 크기가 다르면 개수 상한만으로 부족하므로 바이트 예산이나 작업 비용별 가중치도 필요합니다. 제한을 통과하려고 기다리는 스레드나 코루틴 자체도 무한히 만들지 않아야 합니다.
병목이 옮겨가는 곳까지 봅니다
한 인스턴스의 실행 상한을 20으로 정해도 인스턴스가 열 개면 DB에는 최대 200개의 작업이 들어갈 수 있습니다. 자동 확장까지 있다면 인스턴스별 제한과 서비스 전체의 DB 예산을 함께 설계해야 합니다. 요청률 제한도 별개입니다. 초당 요청 수가 같아도 각 쿼리가 열 배 느려지면 동시 점유는 커지므로 속도 제한 하나로 연결 수를 보호할 수 없습니다. 반대로 동시성만 제한하면 아주 짧은 요청이 과도하게 반복돼 호출당 과금이나 외부 API의 속도 한도를 넘을 수 있습니다.
부하를 올릴 때는 입력률, 받아들인 비율, 실제 성공 처리율, 거절률, 큐의 가장 오래된 요청 나이를 같은 시간축에서 보겠습니다. 즉시 실패한 요청이 늘어 전체 평균 지연이 좋아지는 것은 성능 개선이 아닙니다. 재시도에는 전체 시간·횟수 예산과 무작위 지연을 두고, 클라이언트가 안내를 지키지 않아도 서버 내부 상한은 유지해야 합니다. 부하가 내려간 뒤 큐가 얼마나 빨리 비워지는지도 시험해, 장애가 끝났는데 오래된 요청 때문에 회복이 늦어지는 상태를 막겠습니다.
득점 포인트
이해가 드러나는 지점을 확인해 보세요.
- 실행 상한과 저장 상한을 처리량·p99로 구분한다.
- 데드라인·메모리 예산으로 대기와 거절을 선택한다.
- 긴 작업 분리와 장애 회복을 검증한다.
감점 포인트
놓치기 쉬운 조건과 오해를 짚어 보세요.
- 큐가 크면 처리량이 늘어난다고 말한다.
- DB 연결 수가 곧 최적 동시성이라고 단정한다.
- 과부하에서 거절이 항상 나쁘다고 말한다.
더 파고들 거리
하나 더 질문하며 이해의 범위를 넓혀 보세요.
득점·감점 포인트는 학습용 자기 점검 기준이며, 특정 회사의 실제 채점표가 아닙니다.
원리부터 공부하고 싶다면
답변을 보충하는 개념·예제·슈도코드를 읽어 보세요.