쏭의 개발 블로그

비관적 락(Pessimistic Lock) vs 낙관적 락(Optimistic Lock) 본문

DB

비관적 락(Pessimistic Lock) vs 낙관적 락(Optimistic Lock)

songu1 2025. 4. 13. 21:34

[1] 동시성 제어

  • 여러 사용자가 동시에 데이터에 접근할 때 데이터의 일관성과 무결성을 유지하기 위해 필수적이다.
  • 이를 통해 데이터 충돌을 방지하고 데이터 손실이나 오류 없이 시스템 안정성과 신뢰성을 확보할 수 있다.
  • 동시성 제어 전략 : 비관적 락, 낙관적 락

 

 

[2] 비관적 락 (Pessimistic Lock)

데이터 접근 시에 항상 충돌이 발생할 가능성이 있다고 가정하고, 데이터를 보호하가 위해 항상 락을 걸어 다른 트랜잭션 접근을 방지한다.

  • 데이터의 변경이 발생할 가능성을 미리 차단하여 데이터의 일관성을 보장
  • 다른 트랜잭션들은 락이 해제되기까지 대기한다. 락을 오래 점유하고 있으면 성능 저하나 deadlock 등으로 인한 장애 문제가 발생할 수 있다.
  • 비관적 락을 사용할 때는 락의 범위와 지속 시간을 신중하게 관리해야한다.
  • Record Lock은 비관적 락이다.

장단점

장점

  • 데이터의 무결성을 완벽히 지킬 수 있다. (수정할 데이터에 row level lock을 걸기 때문에 다른 요청에서 수정하는 것은 불가능)
  • 충돌 발생 가능성이 적다.
  • 구현이 비교적 간단하다

단점

  • lock으로 인하여 이후의 다른 요청은 대기 상태로 빠진다.
  • 락을 걸고 해제하는 과정에서 많은 자원이 소모되어 성능 저하를 초래할 수 있다
  • 다른 트랜잭션의 접근을 차단하므로 병목 현상이 발생할 수 있다.

사용

  • 데이터의 충돌 가능성이 높고, 데이터의 일관성을 유지해야하는 중요한 작업
  • 예시 : 금융 시스템, 재고 관리 시스템, 예약 시스템 등
    • 은행 계좌 잔액 업데이트 작업 : 비관적 락을 사용하여 동시에 여러 트랜잭션이 같은 계좌에 접근하는 것을 방지

구현 방법

(1) update

  • 데이터베이스에 저장된 데이터 기준으로 update문 수행한다.
  • Exclusive Lock(쓰기 락)으로 다른 트랜잭션은 이 row를 읽거나 쓸 수 없다.
  • 데이터의 기존 값을 확인할 필요 없이, 단순히 값을 증가/변경하는 경우에 적합하다.
-- 트랜잭션 시작
transaction start;

-- 좋아요 데이터 삽입
insert into article_like
	values({article_like_id}, {article_id}, {user_id}, {created_at});
	
-- 좋아요 수 데이터 갱신 (Pessimistic Lock 점유)
update article_like_count 
	set like_count = like_count + 1 
	where article_id= {article_id};

-- Pessimistic Lock 해제
commit;
  • 락 점유 시점 : UPDATE 실행 시점
  • 락 점유 시간 : UPDATE 시 바로 점유하고 COMMIT까지 유지하므로 상대적으로 짧음
  • 사용 : 단순한 카운트 증가, 조건 없는 갱신 작업
  • 주의할 점 : UPDATE가 실제로 값을 변경하지 않더라도 락이 점유되므로 경합을 주의해야 함

(2) select for update + update

  • 트랜잭션에 조회된 데이터 기준으로 update문 수행한다
  • 조회 결과에 대해 락을 점유한다
  • 값을 미리 확인하거나 조건에 따라 변경할지 말지를 판단해야하는 경우에 유리하다
-- 트랜잭션 시작
transaction start;

-- 좋아요 데이터 삽입
insert into article_like
	values({article_like_id}, {article_id}, {user_id}, {created_at});

-- for update 구문으로 데이터 조회
-- 조회된 데이터에 대해서 Pessimistic Lock 점유(이 시점부터 다른 Lock은 점유될 수 없다.)
-- 애플리케이션에서 JPA를 사용하는 경우, 객체(엔티티)로 조회할 수 있다.
select * from article_like_countwhere article_id= {article_id} for update;

-- 좋아요 수 데이터 갱신
-- 조회된 데이터를 기반으로새로운 좋아요 수를 만들어준다. (조회 시점부터 Lock을 점유하고 있기 때문에 가능)
-- Client(애플리케이션)에서 JPA를 사용하는 경우, 엔티티로 위 과정을 수행할 수 있다.
update article_like_countset like_count= {updated_like_count} where article_id= {article_id};

-- Pessimistic Lock 해제
commit;
  • 락 점유 시점 : SELECT FOR UPDATE 실행 시점
  • 락 점유 시간 : SELECT 시점부터 트랜잭션 종료까지이므로 상대적으로 길다.
  • 사용 : 현재 값 기반으로 계산이 필요한 경우
  • 주의할 점 : 락 점유 시간이 길어 경합, 데드락 가능성⬆️

 

 

[3] 낙관적 락 (Optimistic Lock)

데이터 접근 시에 항상 충돌이 발생할 가능성이 없다고 가정하rh, 데이터의 변경 여부를 확인하고 충돌을 처리한다.

  • 데이터가 다른 트랜잭션에 의해 수정되었는지 확인하고, 수정된 내역이 있으면 rollback이나 재처리를 수행한다.
  • 데이터의 변경 여부는 각 테이블의 version 컬럼으로 데이터 변경 여부를 추적한다.
  • 충돌을 감지하고 후처리를 위한 추가 작업이 필요하다. 충돌 발생 시 commit, rollback, 재시도를 수행해야하며, 이는 애플리케이션에서 구현해야한다.

장단점

장점

  • 성능이 향상될 수 있다
  • 다른 트랜잭션의 접근을 차단하지않으므로 병목 현상이 발생하지 않는다.
  • 락을 걸고 해제하는 과정에서 오버헤드가 발생하지 않는다.

단점

  • 데이터의 일관성을 보장하기 어렵다.
  • 충돌이 발생할 가능성이 있다.
  • 구현이 비교적 복잡하다.

사용

  • 충돌 가능성이 낮은 환경
  • 시스템의 성능을 최대한 유지해야하는 경우
  • 예시 : 소셜 네트워크, 블로그 시스템 ,전자 상거래 시스템 등
    • 소셜 미디어의 사용자 프로필 업데이트 : 동시에 많은 사용자가 데이터를 수정하지 않는 경우

작동 흐름

  1. 데이터 조회 (Read + Version 확인)
    • 트랜잭션 시작 시점에 레코드와 함께 version을 조회
    • version : 해당 레코드가 마지막으로 수정된 시점
  2. 데이터 수정 시도
    • 애플리케이션은 조회했던 version 값을 기반으로 데이터를 수정하려고 합니다.
    • UPDATE 시 WHERE 절에 version = 조회했던 값 조건을 포함합니다.
  3. 충돌 여부 검증
    • 데이터 변경 성공 : 충돌❌
      • 조회한 시점 이후로 다른 트랜잭션이 데이터를 변경하지 않았으면 version이 그대로이므로 수정이 성공. 이 때 version은 자동 증가
    • 데이터 변경 실패 : 충돌⭕
      • 다른 트랜잭션이 먼저 데이터를 수정하여 version이 변경되었다면 조건이 맞지 않아 UPDATE는 0건 수행되고, 예외가 발생하거나 실패로 처리

구현 방법

  • version 컬럼을 추가하고 애플리케이션에서 낙관적 락에 의한 충돌 처리 작업이 필요
  • JPA에서는 테이블에 버전 관리를 위한 컬럼을 추가하고 @Version 애노테이션을 달아주면 알아서 처리해준다.

사전 준비

테이블에 version 컬럼을 추가

ALTER TABLE article_like_count ADD COLUMN version INT DEFAULT 0;

트랜잭션 흐름

-- 트랜잭션 시작
BEGIN;

-- 현재 버전 조회
SELECT version FROM article_like_count WHERE article_id = 123;

-- 애플리케이션 로직 수행 (version 번호 조건)

-- 업데이트 시 version 조건 추가
UPDATE article_like_count
   SET like_count = like_count + 1,
       version = version + 1
 WHERE article_id = 123 AND version = 5;

-- 커밋
COMMIT;

 

 

 

비관적 락 vs 낙관적 락

  비관적 락 낙관적 락
개념 데이터 접근 시 즉시 락을 걸어 다른 트랜잭션의 접근 차단 락을 걸지 않고 트랜잭션 종료 시 충돌 여부 확인
충돌 처리 방식 충돌을 사전에 방지 충돌을 사후에 검증하고 처리
성능 동시성 저하 가능성 락 오버헤드 적어 성능에 유리
일관성 보장 수준 일관성이 높아 데이터 정합성을 강력 보장 일관성이 낮을 수 있으므로 충돌 발생시 처리 필요
장점 충돌 가능성이 높은 환경에서 안전하게 데이터를 보호 충돌이 적은 환경에서 높은 성능 발휘 (높은 동시성)
단점 락 경합, 데드락 위험, 성능 저하 가능 충돌 시 재시도 로직 필요
사용 데이터 경합이 심하고 정합성이 중요한 경우 읽기 중심이거나 충돌이 적은 웹 시스템
  • 비관적 락 : 충돌 가능성이 높거나 정합성 오류가 심각한 결과를 초래하는 경우
  • 낙관적 락 : 충돌 가능성이 낮거나 읽기 작업이 높을 때, 성능이 중요하고 재시도가 허용 가능한 로직일 때

 

 


참고 자료

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8%EB%A1%9C-%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%84%A4%EA%B3%84-%EA%B2%8C%EC%8B%9C%ED%8C%90

https://minjooig.tistory.com/147

https://chrisjune-13837.medium.com/db-동시성-문제-해결방법-f5e52e2e3

https://f-lab.kr/insight/pessimistic-lock-vs-optimistic-lock-20240707