DB 락(Database Lock) 완벽 이해하기 - 개발자라면 꼭 알아야 할 개념
“은행 계좌에서 돈을 빼는데, 둘이 동시에 빼면 어떻게 될까?”
개발을 하다 보면 동시에 같은 데이터를 읽고, 수정하고, 삭제하는 상황을 피할 수 없습니다. 예를 들어, A와 B가 같은 은행 계좌에서 돈을 동시에 인출하려 한다고 해봅시다.
- A가 5만 원을 빼려는 순간 잔액: 10만 원
- B도 같은 순간 5만 원을 빼려는 요청을 보냄
- A와 B 모두 잔액 10만 원을 보고 인출 → 최종 잔액이 0원이 아닌 5만 원이 되어버림!
데이터 무결성(Data Integrity) 이 깨집니다.
이 문제를 해결하는 핵심 개념이 바로 DB 락(Database Lock) 입니다.
DB 락이란?
“여러 명이 동시에 같은 데이터를 건드릴 때, 꼬이지 않도록 잠시 잠가두는 안전장치”
데이터베이스는 다중 사용자가 접근하는 시스템이기 때문에, 한 사용자가 데이터를 읽거나 수정하는 동안 다른 사용자의 작업을 잠시 막는 방법이 필요합니다.
그게 바로 락(Lock) 이죠.
왜 락이 필요할까? (은행 계좌 예시)
- A: “내 계좌에서 5만 원 인출할게”
- B: “나도 5만 원 인출할래”
DB가 락을 걸지 않으면, A와 B가 동시에 잔액을 조회(10만 원) → 각자 5만 원 빼서 저장 → 최종 잔액이 5만 원이 남아버리는 문제가 생깁니다.
하지만 DB가 ‘잠깐! A 먼저 끝낼 때까지 기다려!’ 하고 락을 건다면? ➡ A의 작업이 끝나고 나서야 B가 처리되므로 정확한 결과(잔액 0원) 가 유지됩니다.
DB 락의 두 가지 철학
DB 락에는 두 가지 방식이 있습니다.
1. 낙관적 락 (Optimistic Lock)
“동시에 접근해도 괜찮겠지? 근데 마지막에 확인은 해볼게.”
- 데이터를 읽을 때 락을 걸지 않음
- 대신 버전(version) 필드를 두고, 커밋(저장)할 때 변경 여부 확인
- 누군가 먼저 수정했다면 에러(충돌) 발생 → 다시 시도해야 함
예시
- 게시글 좋아요 수 같은 기능 → 여러 명이 동시에 눌러도 조금 늦게 업데이트되면 괜찮음
비유
- “책을 빌릴 때 자물쇠 안 걸고 그냥 빌려줌. 나중에 반납할 때 ‘책이 다른 사람에 의해 수정됐나?’ 확인 후 충돌 시 다시 처리”
장점: 락을 거의 안 걸기 때문에 성능이 좋음
단점: 저장할 때 충돌 나면 재시도 로직 필요
2. 비관적 락 (Pessimistic Lock)
“혹시 모르니 내가 끝날 때까지 아무도 건들지 마!”
- 데이터를 읽는 순간부터 DB가 잠금 걸기
- 다른 트랜잭션은 읽기/쓰기 모두 대기 상태 (잠금 해제될 때까지 기다림)
- SQL에서는
SELECT FOR UPDATE
로 구현
예시
- 은행 계좌, 항공권 좌석, 콘서트 티켓 → 절대 꼬이면 안 되는 데이터
비유
- “내가 책 빌리면 사서가 ‘이 책은 지금 대출 중이니 건들지 마세요’라고 딱지 붙임”
장점: 충돌이 거의 없음 (안전성 최고)
단점: 락이 오래 걸리면 다른 사용자가 기다려야 함 → 성능 저하 가능
DB 락의 동작 방식
DB 락은 트랜잭션(Transaction) 단위로 동작합니다.
- 트랜잭션 시작
- 데이터를 읽으면서 락을 걸기 (비관적 락의 경우)
- 수정 후 커밋
- 커밋이 완료되면 락 해제
그래서 락을 쓸 때는 트랜잭션을 짧게 유지하는 게 중요합니다.
언제 어떤 락을 써야 할까?
낙관적 락 (Optimistic Lock)
- 동시성이 많지만 충돌 가능성은 적을 때
- 예: 게시글 좋아요 수, 상품 조회수, 포인트 적립
비관적 락 (Pessimistic Lock)
- 꼬이면 절대 안 되는 핵심 로직
- 예: 은행 계좌 잔액 변경, 티켓 좌석 예매, 창고 재고 처리
Spring에서 DB 락 쓰는 법
Spring Data JPA에서는 @Lock 어노테이션으로 쉽게 DB 락을 사용할 수 있습니다.
public interface AccountRepository extends JpaRepository<Account, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE) // 비관적 락
@Query("SELECT a FROM Account a WHERE a.id = :id")
Optional<Account> findByIdForUpdate(@Param("id") Long id);
}
마무리
DB 락은 동시성 문제를 해결하는 가장 기본적인 도구입니다.
- 낙관적 락(Optimistic) -> “충돌 안 나겠지? 근데 저장할 때 체크는 할게”
- 비관적 락(Pessimistic) -> “내가 끝날 때까지 아무도 건들지 마!”
그리고 락을 쓸 땐 트랜잭션 길이를 최대한 짧게 유지하는 게 핵심!
락을 오래 잡아두면 다른 작업이 모두 기다리기 때문에 시스템 전체 성능이 떨어질 수 있습니다.
Enjoy Reading This Article?
Here are some more articles you might like to read next: