자원 경합과 연쇄장애

김키핑·2026년 8월 19일
post-thumbnail

자원경합

둘 이상의 프로세스, 스레드, 또는 작업이 한정된 동일 자원을 동시에 사용하려고 다투는 상태


공유자원

공유 자원(Shared Resource)이란 동시에 실행되는 프로세스들이 공동으로 접근하여 사용하는 자원을 의미한다. 전역 변수(예: '잔액', '총합')와 같은 데이터뿐만 아니라 파일, 입출력장치, 보조기억장치 등의 하드웨어 및 시스템 자원까지 포함한다.

여러 프로세스가 이러한 공유 자원에 동시에 접근해 읽고 쓸 때 접근 순서에 따라 결과가 달라지는 동시성 문제가 발생할 수 있다. 따라서 공유 자원에 접근하는 코드 영역 중 동시에 실행해서는 안 되는 영역을 임계 구역(Critical Section)으로 설정하고, 상호 배제(Mutual Exclusion)를 통해 보호해야 한다.


Lock 경합

Lock의 메커니즘 비교

Lock 종류작동 방식주요 특징 및 적합한 상황
Mutex단일 스레드 전용 점유소유권 개념이 존재하며, 락을 획득한 스레드만 해제 가능하다.
Semaphore카운터 기반 N개 접근 허용설정한 수만큼 동시 접근을 허용한다. Mutex는 Binary Semaphore의 특수한 형태다.
Spinlock대기 중 지속적 CPU 점유 (Busy-wait)Context Switching 비용보다 대기 시간이 짧을 때 유리하다. 대기가 길어지면 CPU 낭비가 극대화된다.
Read-Write Lock읽기 공유 / 쓰기 단독 배타 접근읽기 작업이 많고 쓰기 작업이 적은 워크로드에서 경합을 줄이는 데 효과적이다.

Lock 경합 심화 시 문제점

⚠️ 락 경합이 심화될 때 나타나는 시스템 병목 현상

  • Context Switching 비용 증가: 락을 기다리는 스레드가 대기 상태로 전환되고, 락 해제 시 다시 스케줄링되는 과정에서 상당한 CPU 오버헤드가 발생한다.
  • 대기 큐 적체: 락을 기다리는 스레드나 트랜잭션이 큐에 쌓이면서 처리 지연이 누적되고 전체 시스템의 처리량(Throughput) 저하로 이어진다.

Lock 범위 (Granularity)

구분보호 범위장점단점
Coarse-grained Lock넓은 범위 (예: 테이블 전체)구현이 단순하다.동시성이 떨어지고 경합 가능성이 높다.
Fine-grained Lock좁은 범위 (예: Row, Page)경합이 줄어들고 동시성이 향상된다.락 관리 오버헤드와 구현 복잡도가 증가한다.

락을 얼마나 잘게 쪼개느냐가 동시성(Concurrency)과 오버헤드 사이의 트레이드오프를 결정하는 핵심 설계 지점이다.


Deadlock과 Starvation

  • Deadlock (교착 상태): 둘 이상의 트랜잭션이나 스레드가 서로가 점유한 자원을 상호 대기하며 무한정 멈춰 있는 상태다.
  • Starvation (기아 상태): 특정 트랜잭션이 우선순위나 타이밍 문제로 인해 지속적으로 락을 획득하지 못하고 뒤로 밀리는 현상이다.

자원고갈

시스템이 가진 메모리, CPU, 디스크 공간, 네트워크 대역폭 등의 제한된 자원이 과도하게 소모되거나 반환되지 않아 더 이상 정상적인 작업을 수행할 수 없는 상태다.


자원고갈의 주요 현상 및 원인

구분주요 현상원인 및 메커니즘
메모리 고갈 (OOM)Heap 공간 부족으로 프로그램이 강제 종료된다.자원 누수(Resource Leak), 무한 루프 또는 과도한 재귀 호출로 인해 메모리가 계속 점유된다.
File Descriptor 고갈OS의 최대 파일 오픈 개수를 초과하여 새 파일이나 소켓을 열지 못한다.사용이 끝난 파일 핸들이나 소켓을 시스템에 정상적으로 반환하지 않는 코드 결함이 원인이다.
Port 고갈네트워크 연결에 필요한 Ephemeral Port가 모자라 통신 불능 상태가 된다.대량의 연결 요청이 몰리거나(DoS/DDoS), Connection Pool 미사용 및 CLOSE_WAIT/TIME_WAIT 상태 적체로 발생한다.

연쇄장애

상호 연결된 분산 시스템이나 구조에서 하나의 구성 요소가 고장 났을 때, 그 여파가 인접 서비스로 파급되어 전체 시스템이 차례대로 마비되는 현상이다.


핵심 요인

  • Head-of-Line Blocking: 앞선 느린 작업이 대기열을 점유하여 뒤따르는 작업 전체를 지연시킨다.
  • Thread Pool Exhaustion: 느린 하위 의존성 하나로 인해 상위 서비스의 스레드 풀 전체가 대기 상태로 잠식된다.
  • Retry Storm / Thundering Herd: 타임아웃 발생 후 다량의 재시도 요청이 한꺼번에 몰리며 하류 시스템의 부하를 폭발적으로 증폭시킨다.

연쇄장애 전파 경로

지연 증가 ──> 점유시간 증가 ──> Thread Pool 고갈 ──> 대기 적체 ──> 타임아웃/재시도 ──> 부하 증폭 ──> 상위 서비스 전파

작은 지연이 자원 점유 시간을 늘리고, 이는 Thread Pool 고갈과 대기 적체로 이어진다. 결과적으로 타임아웃에 따른 재시도 폭주(Retry Storm) 현상이 발생하여 부하가 증폭되고 상위 서비스로 장애가 전파된다.


씨스터디 답변

여러 독립적인 작업이 하나의 공유 자원이나 상태를 사용하고 있습니다. 특정 작업 하나에서 부하가 발생하자 관계없는 다른 작업들까지 함께 느려졌습니다. 왜 이런 현상이 발생할 수 있고, 영향 범위를 줄이기 위해 어떻게 설계하시겠습니까? 각자의 도메인에 맞춰 설명해주세요.

특정 작업의 부하가 공유 자원을 둘러싼 자원 경합과 자원 고갈을 유발하고, 이것이 대기열을 타고 전파되면서 시스템 전체의 연쇄 장애로 이어집니다.

// 저는 요즘 DB 유지보수에 관심이 있고, 그중에서도 엔진 레벨에서 자원과 락을 직접 제어할 수 있는 MS SQL Server 기반 환경에 마음이 갑니다.

MS SQL Server 환경에서 대량의 데이터를 일괄 처리할 때 행 단위 잠금이 테이블 잠금으로 자동 승격되면, 작업과 무관한 트랜잭션까지 대기 상태에 빠지며 선두 차단 현상이 발생합니다.

선두 차단 현상은 앞선 요청이 잠금으로 인해 처리되지 못하면서, 그 뒤의 요청들까지 연쇄적으로 진행하지 못하고 대기하게 되는 현상입니다.
이 현상이 장기화되면 데이터베이스 연결 자원이 고갈되고 스레드 풀 고갈과 재시도 폭주로 이어져 결국 상위 서비스 전체에 연쇄 장애를 일으키게 됩니다.

이를 해결하기 위해 리소스 관리자로 배치 작업의 CPU와 메모리 사용량을 제한하여 시스템 영향을 최소화하고, 읽기 커밋된 스냅숏 격리(RCSI)를 적용해 읽기 작업이 잠금 대기 없이 tempdb에 저장된 행 버전 정보를 읽도록 하겠습니다.

DB를 잘 치는 것은 아니지만, 도메인 및 답변 중복을 간만에 없애기 위해 DBA 관점의 답변을 준비하였다.

더 알아보기

대량의 데이터를 일괄 처리할 때 행 단위 잠금을 많이 잡으면 SQL 서버가 이를 테이블 잠금으로 자동 승격시킬 수 있습니다. 많은 행에 각각 잠금을 유지하는 것보다 하나의 넓은 범위로 관리하는 것이 잠금 관리 비용을 줄일 수 있기 때문입니다. 하지만 이 경우 개별 행이 아닌 테이블 전체에 가까운 범위가 잠기면서, 배치 작업과 관계없는 다른 트랜잭션(작업범위)까지 잠금을 기다리게 됩니다.

profile
양치기소녀

0개의 댓글