
DB Lock 이란?
📌 DB Lock은 여러 트랜잭션이 동시에 같은 자원에 접근할 때 데이터 무결성(정합성)을 보장하기 위해 사용되는 메커니즘
- 쉽게 말해, 어떤 사용자가 데이터를 사용하고 있는 동안에는 다른 사용자가 그 데이터를 동시에 수정하면 안 되기 때문에, ‘잠금’을 걸어서 충돌(Conflict)을 방지
- 여러 사용자가 동시에 DB를 사용하는 경우 같은 데이터를 동시에 수정하려고 시도하는 경우 충돌 제어 및 데이터의 일관성을 보장하기 위해 사용한다.

언제 Lock을 사용하는가?
- 동시에 읽기/쓰기가 빈번하게 일어나는 중요한 테이블에 대해, 데이터 무결성을 엄격히 보장해야 할 때.
- 은행 이체, 재고 관리, 주문 처리와 같이 동시에 발생하면 안 되는 시나리오를 제어할 때.

- 불필요한 락은 성능 저하를 유발할 수 있으므로, 적절한 수준에서 사용하는 것이 중요
- DeadLock발생 여부를 꼭 확인해야함!

Lock의 생명주기
1 트랜잭션 시작!
- DB와의 트랜잭션을 시작
- 아직 특정 자원에 대한 락이 획득된 상태는 🙅
2 Lock 획득!
- 쿼리 요청
- Lock 체크
- 대기 혹은 Lock 획득
3 트랜잭션 종료!
- 트랜잭션 Commit 혹은 RollBack
- 락 해제
Lock 종류
1. 낙관적 락 (충돌이 발생하지 않을 것이라고 가정)
- 데이터베이스의 락을 사용하는 것이 아닌 application 레벨에서 버전 관리
- 특정 작업을 수행하기 전에 별도의 락(lock)을 걸지 않고, 작업 완료 시점에서 데이터의 변경 여부를 확인하여 충돌 여부를 판단 (@Version 활용)
- 데이터 충돌이 거의 없을것이라고 가정한 경우 사용
- LockModeType.OPTIMISTIC로 적용
- 충돌 시 ObjectOptimisticLockingFailureException 발생

2. 비관적 락 (충돌이 자주 발생할 것이라고 가정)
2.1 공유락(Shared Lock, S Lock)
- 여러 트랜잭션이 동시에 데이터를 읽기할 수 있지만, 쓰기 하려면 공유락을 해제하고 배타락으로 변경
- 다른 트랜잭션이 쓰기하려 하면 대기 상태
- LockModeType.PESSIMISTIC_READ로 적용
2.2 배타락(Exclusive Lock, X Lock)
- 오직 한 트랜잭션만 해당 데이터를 읽거나 쓸 수 있음
- 다른 트랜잭션이 접근하려 하면 대기 상태
- LockModeType.PESSIMISTIC_WRITE로 적용

3. 분산 락
- 여러 인스턴스나 분산 환경에서 락을 설정
- 데이터베이스에 직접 Lock을 걸지 않고, 외부에서 권한을 받아 처리
- Redis, **Zookeeper 등... 을 활용하여 적용**

낙관적 락 (Optimistic Lock)
1. 낙관적 락이란?
- 데이터의 충돌이 드물다고 가정하고, 충돌이 발생했을 때만 문제를 해결하는 방식.
- 일반적으로 버전 번호(version)를 활용하여 데이터를 갱신할 때 충돌을 감지.
2. 특징
- 비교적 낮은 충돌 가능성 전제:
- 동시에 데이터를 갱신하는 상황이 드물거나, 충돌 가능성이 낮은 환경에서 효과적.
- 병렬성 보장:
- 별도의 잠금(lock) 없이 데이터를 읽고, 이후 업데이트 시점에서만 충돌 여부를 확인.
- 성능 우선:
- 자원을 차단하지 않기 때문에, 읽기 작업과 병렬 처리에서 높은 성능을 보임.
3. 작동 방식
- 데이터 조회:
- 데이터와 함께 버전 번호를 가져옴.
- 수정 및 업데이트 요청:
- 데이터를 수정한 후, 저장 요청 시 버전 번호를 포함.
- 버전 검증:
- 데이터베이스에서 현재 데이터의 버전 번호와 요청에 포함된 버전 번호를 비교.
- 성공/실패 여부:
- 동일하면 업데이트 성공 → 버전 번호 증가.
- 다르면 충돌 발생으로 간주하고 예외 처리.
4. 예제
4.1 데이터베이스 테이블 예제
CREATE TABLE example_table (
id BIGINT PRIMARY KEY,
data VARCHAR(255),
version INT NOT NULL
);
4.2 JPA와 낙관적 락
import jakarta.persistence.*;
@Entity
public class ExampleEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String data;
@Version // 버전 관리용 필드
private Integer version;
// getters and setters
}
4.3 낙관적 락 업데이트 코드
import org.springframework.stereotype.Service;
import jakarta.persistence.EntityManager;
import jakarta.persistence.OptimisticLockException;
@Service
public class ExampleService {
private final EntityManager entityManager;
public ExampleService(EntityManager entityManager) {
this.entityManager = entityManager;
}
public void updateData(Long id, String newData) {
try {
ExampleEntity entity = entityManager.find(ExampleEntity.class, id);
entity.setData(newData);
entityManager.merge(entity);
} catch (OptimisticLockException e) {
System.out.println("낙관적 락 충돌 발생!");
// 충돌 처리 로직 추가
}
}
}
5. 장단점
장점 | 단점 |
병렬성 보장: 별도의 잠금 없이 병렬 작업 가능. | 충돌이 빈번하면 롤백 비용 증가. |
성능 최적화: 리소스를 차단하지 않으므로 성능 향상. | 충돌 발생 시 복구 로직 필요. |
충돌이 적은 환경에서 매우 적합. | 충돌 발생 시 사용자가 직접 재시도하거나 해결해야 함. |
데이터 읽기와 병렬 처리 시 높은 효율성. | 데이터 변경이 많은 환경에서는 비효율적일 수 있음. |
6. 낙관적 락 vs 비관적 락
낙관적 락 (Optimistic Lock) | 비관적 락 (Pessimistic Lock) | |
충돌 가능성 | 낮은 환경에서 적합. | 높은 환경에서 적합. |
잠금 방식 | 잠금 사용 안 함 (버전 번호로 충돌 감지). | 데이터 읽기 시점에서 데이터에 잠금 적용. |
성능 | 충돌 가능성이 낮으면 성능 우수. | 잠금으로 인해 동시 처리량 감소. |
복구 비용 | 충돌 발생 시 롤백과 재시도 비용 발생. | 잠금으로 인해 충돌 방지 가능하나 대기 시간 증가. |
사용 예시 | 읽기 작업이 많고 쓰기 작업이 적은 환경. | 동시에 여러 사용자가 쓰기 작업을 자주 수행하는 환경. |
7. 사용 사례
- 읽기 작업이 많은 환경:
- 예: 상품 상세 페이지의 재고 확인.
- 충돌 가능성이 낮은 환경:
- 예: 사용자 개인 설정 업데이트.
- 트랜잭션이 짧은 작업:
- 예: 간단한 상태 변경 작업.
8. 학습하면 좋은 추가 주제
- JPA의 @Version 어노테이션 활용:
- 실무에서의 구체적 활용 방법과 사례.
- 비관적 락(Pessimistic Lock):
- 낙관적 락과 비교되는 개념 학습.
- 분산 시스템에서의 락 구현:
- Redis나 Zookeeper를 활용한 분산 락 구현.
- 데드락과 충돌 처리 전략:
- 충돌 및 데드락 방지를 위한 설계 패턴.
비관적 락 (Pessimistic Lock)
1. 비관적 락이란?
- 데이터 충돌이 자주 발생할 것으로 예상되는 환경에서, 데이터를 수정하거나 읽기 전에 잠금을 걸어 충돌을 방지하는 방법.
- 트랜잭션 동안 다른 트랜잭션이 데이터에 접근하지 못하도록 차단.
- 주로 데이터베이스에서 제공하는 락 메커니즘(예: SELECT ... FOR UPDATE)을 사용.
2. 특징
- 잠금을 통해 충돌 방지:
- 데이터 수정 전 잠금을 걸어 다른 트랜잭션의 접근을 차단.
- 강력한 일관성 보장:
- 동시에 같은 데이터를 수정하려는 작업 간 충돌을 완전히 방지.
- 성능 비용 증가:
- 트랜잭션이 오래 지속되면 잠금으로 인해 다른 트랜잭션이 대기 상태가 됨.
3. 작동 방식
- 데이터 조회 시 잠금:
- 데이터를 읽으면서 **배타적 잠금(Exclusive Lock)**을 설정.
- 잠금 해제:
- 트랜잭션이 종료될 때 잠금 해제.
- 다른 트랜잭션 처리:
- 잠금이 해제될 때까지 다른 트랜잭션은 대기 상태.
4. 비관적 락 구현 방법
4.1 데이터베이스에서 직접 사용
- SQL 예제:
-- 데이터 수정 전 잠금 설정
SELECT * FROM example_table
WHERE id = 1
FOR UPDATE;
4.2 JPA에서 비관적 락
- JPA 예제:
import jakarta.persistence.LockModeType;
import jakarta.persistence.EntityManager;
public ExampleEntity findByIdWithLock(Long id, EntityManager entityManager) {
return entityManager.find(ExampleEntity.class, id, LockModeType.PESSIMISTIC_WRITE);
}
- Spring Data JPA 사용:
@Repository
public interface ExampleRepository extends JpaRepository<ExampleEntity, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT e FROM ExampleEntity e WHERE e.id = :id")
ExampleEntity findByIdWithLock(@Param("id") Long id);
}
4.3 LockModeType 종류
LockModeType | 설명 |
PESSIMISTIC_READ | 데이터를 읽는 동안 다른 트랜잭션에서 수정하지 못하도록 잠금. |
PESSIMISTIC_WRITE | 데이터를 수정하거나 읽는 동안 다른 트랜잭션에서 수정/읽기 모두 차단. |
PESSIMISTIC_FORCE_INCREMENT | 데이터 수정 여부와 관계없이 버전 번호를 강제로 증가시키는 잠금. |
5. 장단점
장점 | 단점 |
일관성 보장: 데이터 충돌을 확실히 방지. | 성능 저하: 트랜잭션 대기 시간이 늘어나 성능 저하 가능. |
단순한 구현: 잠금 메커니즘을 사용하여 충돌 방지 로직 간소화. | 데드락 위험: 여러 트랜잭션이 서로 대기하면서 데드락 발생 가능. |
변경이 빈번한 환경에 적합. | 병렬 처리 저하: 동시 처리 성능 감소. |
6. 낙관적 락 vs 비관적 락 비교
낙관적 락 (Optimistic Lock) | 비관적 락 (Pessimistic Lock) | |
충돌 가능성 | 충돌이 낮은 환경에 적합. | 충돌이 잦은 환경에 적합. |
잠금 방식 | 잠금을 사용하지 않음, 버전 번호로 충돌 감지. | 데이터 접근 시점에 잠금을 사용해 다른 트랜잭션 차단. |
성능 | 낮은 충돌 환경에서는 성능 우수. | 잠금으로 인해 동시 처리 성능 저하. |
충돌 처리 비용 | 충돌 발생 시 롤백 비용 발생. | 충돌 발생이 없으므로 롤백 비용이 없음. |
데드락 가능성 | 없음. | 있음 (잠금으로 인해 데드락 발생 가능). |
적용 사례 | 읽기 작업이 많고 쓰기 작업이 적은 환경. | 쓰기 작업이 잦고 데이터 충돌 가능성이 높은 환경. |
7. 사용 사례
- 은행 시스템:
- 계좌 잔액 수정처럼 데이터의 정확성이 중요한 작업.
- 재고 관리:
- 재고 수량을 줄이거나 늘릴 때 다른 트랜잭션이 동일 데이터를 수정하지 못하도록 차단.
- 예약 시스템:
- 좌석 예약, 호텔 방 예약 등에서 동일 리소스를 중복 예약하지 않도록 잠금.
8. 학습하면 좋은 추가 주제
- 트랜잭션 격리 수준:
- READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE의 차이.
- 데드락 방지 전략:
- 트랜잭션 순서 지정, 타임아웃 설정 등.
- 낙관적 락과 비관적 락의 적절한 사용 전략:
- 각 락의 장단점을 고려한 실무 적용 사례 분석.
- 분산 환경에서의 락 관리:
- Redis, ZooKeeper 등 분산 락 시스템 학습.
분산 락 (Distributed Lock)
1. 분산 락이란?
- 분산 시스템 환경에서 공유 자원에 대한 동시 접근을 제어하기 위해 사용하는 락 메커니즘.
- 동시에 여러 노드(서버)가 공유 자원에 접근하려는 상황에서 데이터의 무결성과 일관성을 보장.
2. 특징
- 멀티 노드 환경:
- 여러 노드가 동일한 자원에 접근 가능.
- 분산 락을 통해 자원을 독점적으로 사용할 노드를 제어.
- 데이터 일관성 보장:
- 데이터의 충돌 방지 및 무결성 유지.
- 중앙 관리:
- 락 상태를 중앙에서 관리하거나 분산 환경에서 합의 프로토콜을 사용.
3. 동작 원리
- 락 생성:
- 특정 자원에 대해 락 생성 요청.
- 락이 성공하면 해당 노드는 자원을 독점적으로 사용 가능.
- 락 유지:
- 락을 일정 시간 동안 유지.
- 필요 시 갱신(Renewal) 가능.
- 락 해제:
- 자원 사용이 끝나면 락을 해제.
- 타임아웃으로 자동 해제될 수도 있음.
4. 분산 락 구현 방식
4.1 데이터베이스 기반
- 방법: 데이터베이스 테이블에 락 상태를 저장.
- SQL 예제:
-- 락 생성
INSERT INTO distributed_lock (resource, lock_time) VALUES ('RESOURCE_ID', NOW());
-- 락 확인
SELECT * FROM distributed_lock WHERE resource = 'RESOURCE_ID';
-- 락 해제
DELETE FROM distributed_lock WHERE resource = 'RESOURCE_ID';
- 장점:
- 별도 도구 없이 쉽게 구현 가능.
- 트랜잭션과 연동하여 락 관리.
- 단점:
- 성능 저하(데이터베이스 부하 증가).
- 대규모 시스템에서는 비효율적.
4.2 Redis 기반
- 방법: Redis의 SETNX 명령어를 사용하여 락 생성.
- 예제:
# 락 생성
SET resource:lock "LOCKED" NX PX 10000
# 락 확인
GET resource:lock
# 락 해제
DEL resource:lock
- 장점:
- 빠른 속도.
- TTL(Time-To-Live) 설정으로 자동 해제 가능.
- 단점:
- 단일 노드 Redis 사용 시 SPOF(Single Point of Failure) 발생 가능.
- 클러스터 환경에서는 Redlock 알고리즘 필요.
4.3 ZooKeeper 기반
- 방법: 분산 락의 상태를 ZNode에 저장하여 관리.
- 예제:
# ZNode 생성 (락 획득)
create /locks/resource
# 락 해제
delete /locks/resource
- 장점:
- 강력한 일관성 보장.
- 분산 환경에 최적화.
- 단점:
- 설정 및 관리 복잡.
- ZooKeeper 설치 필요.
5. Redlock 알고리즘 (Redis 기반 분산 락)
- Redis 클러스터 환경에서 락의 일관성을 보장하는 알고리즘.
- 여러 Redis 노드에 동일한 키로 락 생성 요청.
- 과반수 이상의 노드에서 락 성공 시 락 획득.
- TTL을 설정하여 자동으로 락이 해제되도록 설정.
- 락 해제 시 모든 노드에서 락 해제.
6. 장단점
장점 | 단점 |
데이터 충돌 및 중복 작업 방지. | 락 구현 및 관리 복잡도 증가. |
고성능 분산 환경에서 데이터 무결성 보장. | 락 생성/해제 시 네트워크 지연으로 인한 성능 저하 가능. |
다양한 도구를 활용한 구현 가능(데이터베이스, Redis, ZooKeeper 등). | 잘못된 락 설정 시 데드락 발생 가능. |
7. 분산 락 사용 사례
- 동시에 한 번만 실행해야 하는 작업:
- 예: 배치 처리, 데이터 마이그레이션.
- 중복 실행 방지:
- 예: 주문 처리, 결제 처리.
- 리소스 공유:
- 예: 파일 업로드/다운로드, 재고 관리.
8. 학습하면 좋은 추가 주제
- 락 컨텐츠 관리 전략:
- TTL 설정, 자동 해제 로직 구현.
- ZooKeeper와 Redis의 비교:
- 각 기술의 장단점 및 사용 사례 분석.
- 분산 시스템에서의 일관성 모델:
- 강한 일관성, 최종적 일관성(Final Consistency) 개념 이해.
- Redlock 알고리즘 심화:
- 클러스터 환경에서의 락 일관성 보장 방법.
격리 수준
트랜잭션 격리 수준 (Transaction Isolation Levels)
1. 트랜잭션 격리 수준이란?
- 트랜잭션 간의 상호작용에서 발생할 수 있는 데이터 충돌 문제를 방지하기 위해 데이터베이스가 제공하는 동시성 제어 메커니즘.
- 각 격리 수준은 데이터 일관성과 동시성 성능 간의 트레이드오프를 제공.
2. 주요 격리 수준과 특징
격리 수준 | Dirty Read | Non-Repeatable Read | Phantom Read | 특징 |
READ UNCOMMITTED | 허용 | 허용 | 허용 | - 커밋되지 않은 데이터를 읽을 수 있음(Dirty Read). - 가장 낮은 격리 수준으로 동시성 성능은 높지만 데이터 일관성이 낮음. |
READ COMMITTED | 방지 | 허용 | 허용 | - 커밋된 데이터만 읽을 수 있음. - Oracle, SQL Server의 기본 격리 수준. - Non-Repeatable Read 가능. |
REPEATABLE READ | 방지 | 방지 | 허용 | - 동일 트랜잭션 내에서 항상 같은 데이터 읽기 보장. - MySQL(InnoDB)의 기본 격리 수준. |
SERIALIZABLE | 방지 | 방지 | 방지 | - 모든 트랜잭션을 순차적으로 실행하는 것처럼 동작. - 가장 높은 격리 수준으로, 동시성 성능이 낮지만 데이터 일관성 보장. |
3. 주요 개념
- Dirty Read:
- 다른 트랜잭션이 아직 커밋되지 않은 변경 사항을 읽는 것.
- 예시: A 트랜잭션이 데이터를 수정하고 커밋하지 않은 상태에서 B 트랜잭션이 수정된 데이터를 읽음.
- Non-Repeatable Read:
- 한 트랜잭션에서 같은 데이터를 두 번 읽었을 때, 값이 달라지는 현상.
- 원인: 다른 트랜잭션에서 데이터를 수정하고 커밋한 경우.
- Phantom Read:
- 한 트랜잭션에서 동일한 조건으로 데이터를 조회했을 때, 새로운 데이터가 추가되거나 삭제되는 현상.
- 원인: 다른 트랜잭션에서 데이터를 추가/삭제한 경우.
4. 격리 수준별 예제
4.1 READ UNCOMMITTED
-- A 트랜잭션
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- B 트랜잭션
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1; -- Dirty Read 발생
4.2 READ COMMITTED
-- A 트랜잭션
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- B 트랜잭션
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1; -- 커밋되지 않았으므로 읽을 수 없음
4.3 REPEATABLE READ
-- A 트랜잭션
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1; -- 1000
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- B 트랜잭션
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1; -- A 트랜잭션의 변경 사항 보이지 않음
4.4 SERIALIZABLE
-- A 트랜잭션
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1;
-- B 트랜잭션
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1; -- 대기 상태 (A 트랜잭션 종료 후 실행)
5. 장단점 요약
격리 수준 | 장점 | 단점 |
READ UNCOMMITTED | 높은 동시성, 성능 우수 | 데이터 무결성 저하, Dirty Read 발생 가능 |
READ COMMITTED | Dirty Read 방지 | Non-Repeatable Read 발생 가능 |
REPEATABLE READ | Non-Repeatable Read 방지 | Phantom Read 발생 가능 |
SERIALIZABLE | 데이터 일관성 보장, Phantom Read 방지 | 성능 저하, 동시성 처리 어려움 |
6. 사용 사례
- READ UNCOMMITTED:
- 로그 데이터 분석, 캐시 처리 등 데이터 일관성이 덜 중요한 경우.
- READ COMMITTED:
- 일반적인 OLTP 시스템에서 기본 격리 수준으로 적합.
- REPEATABLE READ:
- 은행 시스템, 재고 관리 등 데이터 수정이 빈번한 환경.
- SERIALIZABLE:
- 강력한 일관성이 필요한 환경. 예: 금융 거래 시스템, 예약 시스템.
7. 학습하면 좋은 추가 주제
- 트랜잭션 관리:
- Spring에서의 트랜잭션 관리 (@Transactional)와 격리 수준 설정.
- 트랜잭션과 락:
- 비관적 락과 낙관적 락의 적용 방식.
- MVCC (Multi-Version Concurrency Control):
- MySQL, PostgreSQL에서 사용되는 동시성 제어 방식.
- 데드락 방지 전략:
- 순서 지정, 타임아웃 설정 등을 통한 데드락 방지.
'DB > JPA ( Java Persistence API )' 카테고리의 다른 글
[JPA] N + 1 문제란 무엇인가 (0) | 2025.02.14 |
---|---|
[JPA] SpringData JPA 심화 (0) | 2025.01.28 |
[JPA] 테이블 객체 (0) | 2025.01.27 |
[JPA] 쿼리 파일 만들기 (QueryMapper) (0) | 2025.01.26 |
[JPA] 데이터베이스 연결 (Driver) (1) | 2025.01.25 |