JPA와 트랜잭션 환경에서 자주 발생하는 데드락 사례 분석
운영 환경에서 가끔 이런 로그를 보게 됩니다.
Detail: Process 123 waits for ShareLock on transaction 456
또는 Spring 로그에서는 다음처럼 나타납니다.
이 에러는 DB가 동시에 실행되는 트랜잭션 사이에서 교착 상태(Deadlock)를 감지했을 때 발생합니다.
1. Deadlock이란 무엇인가
Deadlock은 두 개 이상의 트랜잭션이 서로의 Lock을 기다리며 멈춰있는 상태입니다.
예
lock row1
Transaction B
lock row2
이후
Transaction B → row1 필요
구조
B → row1 기다림
결과
DB는 이를 감지하고 한 트랜잭션을 강제로 종료합니다.
2. Deadlock이 발생하는 대표적인 사례
실무에서 가장 흔한 경우입니다.
사례 1. Update 순서가 다른 경우
테이블
데이터
id = 2
트랜잭션 A
UPDATE account SET balance = balance + 100 WHERE id = 2;
트랜잭션 B
UPDATE account SET balance = balance + 100 WHERE id = 1;
결과
B → row2 lock
이후
B → row1 요청
Deadlock 발생.
사례 2. JPA Lazy Loading + Update
JPA에서 이런 코드가 있을 수 있습니다
public void updateOrder(Long id) {
Order order = orderRepository.findById(id);
order.getItems().size();
order.setStatus("COMPLETE");
}
문제
때문에 추가 쿼리가 실행되면서 Lock 순서가 꼬일 수 있습니다.
사례 3. Batch Update
대량 업데이트 시에도 자주 발생합니다.
WHERE status = 'READY'
동시에 여러 서버가 실행하면
이 발생합니다.
사례 4. FK 관계
예
order_items
트랜잭션
B → order_items update
FK 검사 과정에서 lock 순서가 꼬일 수 있습니다.
3. Deadlock이 발생하면 DB는 어떻게 할까
DB는 Deadlock을 감지하면
합니다.
그래서 Spring에서는 다음 예외가 발생합니다.
4. Deadlock을 해결하는 방법
Deadlock은 완전히 없앨 수는 없지만 발생 확률을 줄일 수 있습니다.
방법 1. Lock 순서를 동일하게 유지
가장 중요한 방법입니다.
예
UPDATE account SET ... WHERE id = 2;
모든 트랜잭션이 같은 순서를 사용해야 합니다.
방법 2. 트랜잭션을 짧게 유지
트랜잭션이 길어질수록 lock이 오래 유지됩니다.
예
public void process() {
externalApiCall(); // 문제
updateDB();
}
이 경우 API 호출 동안 DB lock이 유지됩니다.
방법 3. SELECT FOR UPDATE 사용
명시적으로 lock을 잡을 수 있습니다.
WHERE id = 1
FOR UPDATE
이 방법은 lock 순서를 제어할 수 있습니다.
방법 4. Retry 처리
Deadlock은 일시적인 경우가 많습니다.
그래서 보통 재시도 로직을 넣습니다.
Spring에서는
value = DeadlockLoserDataAccessException.class,
maxAttempts = 3
)
같은 방식으로 처리합니다.
방법 5. 인덱스 최적화
인덱스가 없으면
이 발생합니다.
그러면
이 생깁니다.
따라서 update 조건에는 반드시 index가 필요합니다.
5. Deadlock 모니터링
PostgreSQL에서는 다음 쿼리로 확인할 수 있습니다.
또는
이 정보를 보면
waiting query
를 확인할 수 있습니다.
6. 실무에서 가장 중요한 원칙
Deadlock을 줄이려면 다음 원칙을 지켜야 합니다.
트랜잭션 최소화
인덱스 사용
대량 업데이트 주의
정리
Deadlock은 대규모 트래픽 시스템에서 언젠가는 반드시 발생하는 문제입니다.
하지만 설계 단계에서 다음을 고려하면 발생 확률을 크게 줄일 수 있습니다.
짧은 트랜잭션
Retry 로직
적절한 인덱스
✔ 핵심 한 줄
'Spring & Backend' 카테고리의 다른 글
| Node.js + React vs Java Spring Boot 메모리 사용 비교 (0) | 2026.03.12 |
|---|---|
| Gradle에 대해 설명해 주세요. (1) | 2026.03.12 |
| Spring Boot 서버 선택: Tomcat vs Undertow 성능·메모리·CPU 비교 분석 (0) | 2026.03.11 |
| 결합도와 응집도에 대해 설명해주세요. (0) | 2026.03.11 |
| JPA Fetch Join과 페이징을 함께 사용할 때 주의점을 설명해 주세요. (0) | 2026.03.11 |
