If at first you don't succeed, try again

[DB] 비관적 락(Pessimistic Lock) & 낙관적 락(Optimistic Lock) 본문

DB

[DB] 비관적 락(Pessimistic Lock) & 낙관적 락(Optimistic Lock)

웅지니어링 2025. 3. 28. 15:09

* 개요

저번 글에 lock에 대해 개념과 실습을 진행했었다. 

[DB] Lock의 개념 및 테스트

이번 글에서는 비관적 락과 낙관적 락에 대해 정리할 예정이다.

예시는 좋아요 수와 관련하여 들도록 하겠다!

 

* 비관적 락(Pessimistic Lock)

  • 비관적 락은 데이터 접근 시에 항상 충돌이 발생할 가능성이 있다고 가정한다.(비관적 관점)
  • 데이터를 보호하기 위해 항상 락을 걸어 다른 트랜잭션 접근을 방지한다.
  • 다른 트랜잭션은 락이 해제될 때까지 대기하는데, 락을 오래 점유하면 성능 저하 또는 deadlock으로 인한 장애가 발생한다.
  • 지난 글에서 살펴봤던 record lock은 비관적 락에 해당한다.
  • 비관적 락을 구현할 수 있는 방법은 크게 2가지로 나뉜다.

 

* 비관적 락 - 방법 1

1. 트랜잭션 시작

transaction start;

2. 좋아요 데이터 삽입

insert into article_like values(
{article_like_id}, {article_id}, {user_id}, {created_at}
);

3. 좋아요 수 데이터 갱신, 비관적 락 점유

update article_like_count set like_count = like_count + 1 
where article_id = {article_id};

4. COMMIT하여 비관적 락 해제

commit;

이 방법은 데이터베이스 저장된 데이터 기준으로 업데이트를 수행한다.

그리고 업데이트 시 락을 점유한다.

 

* 비관적 락 - 방법 2

1. 트랜잭션 시작

transaction start;

2. 좋아요 데이터 삽입

insert into article_like values(
{article_like_id}, {article_id}, {user_id}, {created_at}
);

3. for update 구문으로 데이터 조회

select * from article_like_count 
where article_id = {article_id} 
for update;
  • 조회된 데이터에 대해서 비관적 락 점유
  • 이 시점부터 다른 락은 점유될 수 없다.
  • 애플리케이션에서 JPA를 사용하는 경우, 객체로 조회할 수 있다.

4. 좋아요 수 데이터 갱신

update article_like_count set like_count = {updated_like_count} 
where article_id = {article_id};
  • 조회된 데이터를 기반으로 새로운 좋아요 수를 만들어준다. 이는 조회 시점부터 락을 점유하고 있기에 가능하다.
  • Client에서 JPA를 사용하는 경우, 엔티티로 위 과정을 수행할 수 있다.

5. COMMIT하여 비관적 락 해제

commit;

for update 구분으로 조회 겨로가에 대해 락을 점유하겠다고 명시한다.

그리고 트랜잭션에 조회된 데이터 기준으로 update 문을 수행한다.

 

* 방법 1(update 구문) vs 방법 2(select for update + update 구문)

- 락 점유

  • 방법 1 : update 문 수행하는 시점에 락을 점유한다. 따라서 락을 점유하는 시간이 상대적으로 짧다.
  • 방법 2 : 데이터 조회 시점부터 락을 점유한다. 락을 점유하는 시간이 상대적으로 길다. 데이터를 조회한 뒤 중간 과정을 수행해야 하기 때문에, 락 해제가 지연될 수 있다.

- 애플리케이션 개발

  • 방법 1 : 데이터베이스의 현재 저장된 데이터 기준으로 증감 처리하기 때문에 SQL문을 직접 전송한다.
  • 방법 2 : JPA를 사용하는 경우, 엔티티를 이용하여 좀 더 객체지향스럽게 개발할 수 있다.

 

* 낙관적 락(Optimistic Lock)

  • 낙관적 락은 데이터 접근 시에 항상 충돌이 발생할 가능성이 없다고 가정한다.(낙관적 관점)
  • 데이터의 변경 여부를 확인하여 충돌을 처리한다.
  • 데이터가 다른 트랜잭션에 의해 수정되었는지 확인한다.
  • 수정된 내역이 있으면 후처리를 진행한다.(ROLLBACK 또는 재처리 등)

그렇다면 데이터의 변경 여부는 어떻게 확인할까?

-> 각 테이블의 version 컬럼으로 데이터의 변경 여부를 추적한다.

 

충돌은 어떻게 확인할 수 있을까?

1. 각 트랜잭션에서 version을 함께 조회한다.

2. 레코드를 업데이트 한다.

  • 이 때, where 조건에 조회된 version을 넣고, version을 증가시킨다.

3. 충돌을 확인한다.

  • 데이터 변경이 성공했다면, 충돌이 발생하지 않은것이다.
  • 데이터 변경이 실패했다면, 충돌이 발생한 것이다. 다른 트랜잭션에서 version을 이미 증가시켰기 때문이다.

 

* 비관적 락 방법 1 적용 - 좋아요 수

테이블 article_like_count을 생성한다.

create table article_like_count (
 article_id bigint not null primary key,
 like_count bigint not null,
 version bigint not null
 );

비관적 락 방법 1은 데이터베이스에 저장된 데이터 기준 update문을 직접 수행해준다.

update article_like_count
set like_count = like_count + 1
where article_id = {article_id}

 

* 비관적 락 방법 2 적용 - 좋아요 수

비관적 락 방법 2는 트랜잭션에 조회된 데이터 기준 for update 구문으로 데이터를 조회한 뒤, 조회된 데이터 기반으로 좋아요 수를 갱신해준다.

select * from article_like_count
where article_id = {article_id} for update;
update article_like_count
set like_count = {updated_like_count}
where article_id = {article_id}

 

* 낙관적 락 방법 적용 - 좋아요 수

낙관적 락 방법은 version 컬럼을 추가하고, 애플리케이션에서 낙관적 락에 의한 충돌 처리 작업이 필요하다.

이러한 충돌 처리 작업은 단순히 ROLLBACK으로 처리한다.

충돌 시 ROLLBACK 처리는 어떻게 할까?

- JPA를 사용하지 않는 경우 : version을 조회한 다음, update 구문을 직접 작성하고 실행해야 한다.

- JPA를 사용하는 경우 : @Version 어노테이션을 추가하면 알아서 처리해준다.

'DB' 카테고리의 다른 글

[DB] Lock의 개념 및 테스트  (0) 2025.03.28
[DB] 페이징 조회 쿼리 성능 개선 - MySQL  (0) 2025.03.08
[DB] Index 실습(대용량 데이터 처리) - MySQL  (0) 2025.03.08
[DB] 트랜잭션  (0) 2025.02.07
[DB] SQL의 개념  (0) 2021.09.10