본문 바로가기
Q&A해결2026년 7월 20일1분 읽기

deadlock detected가 가끔 터지는데 재현이 안 돼서 미치겠어요

l
lock_hyerim
조회 755댓글 4

정산 배치가 계정 잔액을 여러 건 한 트랜잭션 안에서 UPDATE 하는데, 평소엔 멀쩡하다가 트래픽 몰리는 시간대에만 가끔 아래 에러가 납니다.

ERROR:  deadlock detected
DETAIL:  Process 18442 waits for ShareLock on transaction 99123;
         blocked by process 18455.
         Process 18455 waits for ShareLock on transaction 99120;
         blocked by process 18442.
HINT:  See server log for query hints.

문제는 로컬에서 아무리 돌려도 재현이 안 된다는 거예요. 배치가 여러 개 동시에 돌 때만 터지는 것 같은데, 각 배치는 잘못된 것도 없어 보이고 각자 커밋도 잘 됩니다. 데드락이면 뭔가 서로 물고 있는 건 알겠는데 어디서부터 봐야 할지 감이 안 잡힙니다.

댓글 4

김김영삼3개월 전
로그에 힌트가 다 있어요. 두 트랜잭션이 서로 상대가 잡은 행을 기다리다가 순환 대기가 생긴 겁니다. 이게 전형적인 lock ordering 데드락이에요.

원인은 거의 항상 '업데이트 순서'입니다. 배치 A가 계정 100 -> 200 순으로 잠그고, 배치 B가 200 -> 100 순으로 잠그면, A는 100 잡고 200 기다리고 B는 200 잡고 100 기다리면서 서로 영원히 못 풀립니다. 그래서 로컬(동시 실행 안 됨)에선 재현이 안 되는 거예요.

해결은 한 트랜잭션 안에서 여러 행을 건드릴 때 항상 같은 순서로 잠그는 겁니다. 대상 id를 정렬해서 순서대로 UPDATE 하거나, 미리 SELECT ... FOR UPDATE ... ORDER BY id 로 일관된 순서로 락을 잡고 시작하면 순환이 생길 수가 없어요. 어느 배치든 낮은 id부터 잡으니까요.

추가로 트랜잭션은 최대한 짧게 가져가세요. 한 트랜잭션이 오래 여러 행을 붙들고 있을수록 충돌 창이 커집니다.
llock_hyerim(guest)3개월 전
아 짚어주신 대로 코드 보니까, 업데이트할 계정 id 배열을 받아온 순서 그대로 루프 돌면서 updateMany를 치고 있었어요. 그 배열이 정렬이 안 돼 있었네요. 그럼 id로 sort만 해주면 되는 건가요? 그리고 혹시 몰라 데드락 나면 재시도하게 해두는 것도 같이 하는 게 맞을까요?
김김영삼3개월 전
네, 업데이트 전에 id 배열을 오름차순 정렬하는 것만으로 이 순환은 사라집니다. 모든 배치가 같은 방향으로 잠그게 되니까요. 그게 근본 해결이에요.

재시도도 같이 두는 걸 권합니다. 동시성이 높으면 데드락은 '버그'라기보다 가끔 일어날 수 있는 정상 상황에 가까워서, 40P01(deadlock_detected) 에러코드를 잡아서 짧게 백오프 후 한두 번 재시도하는 안전망은 실무에서 흔히 씁니다. 다만 재시도는 어디까지나 보조고, 순서 정렬로 발생 자체를 없애는 게 먼저예요. 재시도만 믿고 순서를 안 맞추면 부하 오를 때 재시도 폭풍이 됩니다.
llock_hyerim(guest)3개월 전
정렬 넣고 이틀째 돌리는데 데드락 로그가 한 건도 안 뜹니다. 재시도도 안전망으로 같이 넣어뒀어요. 재현이 안 돼서 한참 헤맸는데 순서 문제였다니... 정확히 그 문제였습니다. 감사합니다!
Ctrl+Enter로 등록