development/database

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

bokshiri 2024. 10. 27. 02:05

개요

다중 요청을 처리하는 동시성 환경에서 데이터의 정합성과 일관성을 보장하기 위해 Lock을 사용한다.

이 글에서는 Lock의 종류와 스프링 애플리케이션에서 Jpa를 활용하여 Lock을 처리하는 방법에 대해 정리한다.


 

락 (LOCK)

데이터베이스에는 중요한 정보가 저장된다. 따라서 무결성과 일관성, 정합성이 반드시 지켜져야 한다. 하지만, 여러 사용자(Transaction)가 동시에 자원을 획득하게 되면 문제가 생기게 될 것이다. 이러한 문제를 방지하기 위해 데이터베이스는 Lock 기능을 제공한다. 한 트랜잭션이 특정 Row에 대한 Lock을 획득하면, 다른 트랜잭션이 이를 변경할 수 없는 것이다. 이는 데이터에 대한 처리 순서를 보장하여 정합성과 무결성을 지키고 안전한 데이터 처리를 가능하게 한다.

 

Lock에는 크게 낙관적 락(Optimistic Lock)비관적 락(Pessimistic Lock)이 존재하며 비관적 락은 다시 공유락(Shared Lock)배타락(exclusive Lock)으로 나뉜다.

 

낙관적 락 (OPTIMISTIC LOCK)

낙관적 락은 특정 데이터에 대해 DB Lock을 걸어 제어하는 방식이 아니라, 애플리케이션 또는 데이터베이스에서 버전 정보를 사용하여 다른 요청이 접근할 수 없도록 방지하는 방식이다. 

이 방식은 DB Lock을 사용하지 않기 때문에, 동시성 문제가 발생하였을 때 예외를 발생시킨다. 

1. 데이터베이스에 Version 컬럼을 추가하여 동시 접근을 제어한다. 스프링 배치를 사용하면 메타 테이블에 Version 컬럼이 존재하는데, 이 컬럼이 위 용도로 사용된다. 배치는 해당 Row에 낙관적 락을 적용하여 동시성을 제어한다.
-> 트랜잭션 A와 B가 1 이라는 값을 조회하였다고 가정해보자
- A는 1을 2로 변경한 후 Commit을 한다.
- B는 1을 2로 변경한 후 Commit을 시도하지만, Version 2는 이미 존재하므로 예외가 발생한다.

2. 애플리케이션에 Version 필드를 추가하여 동시 접근을 제어한다. 자바와 스프링에서는 낙관적 락을 위해 @Version 어노테이션을 지원한다. Jpa를 사용할 때 @Entity 클래스에 Long 타입의 필드를 선언한 후 @Version 어노테이션을 붙여준다.
-> 여러 트랜잭션이 동일한 데이터에 접근하여 변경을 시도할 경우 최초에 조회된 버전 정보와 현재 데이터베이스에서 조회된 버전 정보가 불일치할 경우 예외가 발생한다.

 

비관적 락 (PESSIMISTIC_LOCK)

비관적 락은 데이터베이스의 Lock을 사용하여 현재 사용중인 트랜잭션이 특정 데이터를 종료 시점까지 점유할 수 있도록 한다. 데이터의 정합성과 무결성을 보장하는 데 유용한 방식이지만 잘못 사용할 경우 Lock으로 인한 병목 현상이 발생할 수 있고, 서로 자원을 점유하려는 과정에서 데드락이 발생할 수도 있다.

병목: 무수히 많은 요청이 발생하였을 때 Lock으로 인해 처리 속도가 저하되는 현상

데드락: 서로 다른 두 트랜잭션이 각각 A, B 데이터에 소유권을 점유한 상태에서 상대 편의 데이터에 Lock을 획득하려 할 때 발생한다. A, B 데이터 모두 Lock이 종료되지 않았으므로 두 트랜잭션은 데이터에 대한 Lock을 획득하지 못하고 영원히 멈추게 된다.

 

공유 락 (Shared Lock)

공유 락은 데이터의 변경을 막고, 조회만 가능하도록 제어할 때 사용한다. 한 트랜잭션이 공유 락을 걸더라도 다른 트랜잭션이 공유 락을 획득하여 해당 레코드를 조회할 수 있다. 단, 데이터에 대한 쓰기 작업은 제한되며 락이 종료되기 전까지 레코드가 변경되지 않음이 보장된다.

SELECT * FROM Table WHERE id = 1 FOR SHARE;

 

배타 락 (Exclusive Lock)

공유 락이 조회를 위한 락이었다면, 배타 락은 쓰기 작업을 할 때 사용하는 락이다. 한 트랜잭션이 데이터에 대해 배타 락을 획득하게 되면, 다른 트랜잭션은 조회 및 쓰기 작업을 할 수 없다. 배타 락을 획득한 트랜잭션은 해당 트랜잭션이 종료될 때까지 데이터에 대한 배타적인 소유권을 가지게 된다. 

SELECT * FROM Table WHERE id = 1 FOR UPDATE;

 

@Lock

spring-data-jpa는 동시성을 제어하기 위해 @Lock 어노테이션을 제공한다. 어노테이션 속성을 활용하여 낙관적 락 또는 비관적 락을 적용할 수 있다.

package org.springframework.data.jpa.repository;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import jakarta.persistence.LockModeType;

/**
 * Annotation used to specify the {@link LockModeType} to be used when executing the query. It will be evaluated when
 * using {@link Query} on a query method or if you derive the query from the method name.
 *
 * @author Aleksander Blomskøld
 * @author Oliver Gierke
 */
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lock {

   /**
    * The {@link LockModeType} to be used when executing the annotated query or CRUD method.
    *
    * @return
    */
   LockModeType value();
}
public enum LockModeType
{
    /**
     *  Synonymous with <code>OPTIMISTIC</code>.
     *  <code>OPTIMISTIC</code> is to be preferred for new
     *  applications.
     *
     */
    READ,

    /**
     *  Synonymous with <code>OPTIMISTIC_FORCE_INCREMENT</code>.
     *  <code>OPTIMISTIC_FORCE_IMCREMENT</code> is to be preferred for new
     *  applications.
     *
     */
    WRITE,

    /**
     * Optimistic lock.
     *
     * @since 2.0
     */
    OPTIMISTIC,

    /**
     * Optimistic lock, with version update.
     *
     * @since 2.0
     */
    OPTIMISTIC_FORCE_INCREMENT,

    /**
     *
     * Pessimistic read lock.
     *
     * @since 2.0
     */
    PESSIMISTIC_READ,

    /**
     * Pessimistic write lock.
     *
     * @since 2.0
     */
    PESSIMISTIC_WRITE,

    /**
     * Pessimistic write lock, with version update.
     *
     * @since 2.0
     */
    PESSIMISTIC_FORCE_INCREMENT,

    /**
     * No lock.
     *
     * @since 2.0
     */
    NONE
}

 

사용 방법은 간단하다. Lock을 적용하려는 Repository 내 메서드에 어노테이션을 붙여주면 된다.

@Lock(LockModeType.PESSIMISTIC_WRITE)
Optional<Member> findById(String id);

 

https://velog.io/@bagt/Database-%EB%82%99%EA%B4%80%EC%A0%81-%EB%9D%BD-%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BD

 

[Database] 낙관적 락 / 비관적 락

이름 그대로 비관적 락은 자원 경쟁을 비관적으로, 낙관적 락은 낙관적으로 본다. 비관적 락은 Repeatable Read 또는 Serializable 정도의 격리성 수준을 제공한다.트랜잭션이 시작될 때 Shared Lock 또는 Ex

velog.io

https://systemdata.tistory.com/51

 

낙관적인 락 vs 비관적인 락

목차락이란?낙관적 락(Optimistic Lock)비관적 락(Pessimistic Lock)낙관적 락 vs 비관적 락결론1. 락이란?여러 개의 트랜잭션이 동시에 접근했을 때, 데이터의 일관성이 깨질 수 있기 때문에 락(잠금)을 걸

systemdata.tistory.com

https://pixx.tistory.com/352

 

[JPA] @Lock 어노테이션이란 무엇일까❓: 다양한 LockModeType 잠금 모드

@Lock 어노테이션이란❓@Lock 어노테이션은 Spring Data JPA에서 제공하는 기능으로, JPA 리포지토리 메소드에 적용하여 특정 데이터베이스 쿼리에 대한 잠금 모드를 지정하는 데 사용됩니다. 이 어

pixx.tistory.com

https://ksh-coding.tistory.com/121

 

[DB] DB Lock이란? (feat. Lock 종류, 블로킹, 데드락)

0. 락(Lock)이란? 여러 커넥션에서 동시에 동일한 자원을 요청할 경우 순서대로 하나의 커넥션만 변경할 수 있게 해주는 기능 동시성을 제어하기 위한 기능 저는 처음에 DB 락을 접했을 때, 락을 이

ksh-coding.tistory.com

https://devoong2.tistory.com/entry/JPA-%EC%97%90%EC%84%9C-%EB%82%99%EA%B4%80%EC%A0%81-%EB%9D%BDOptimistic-Lock%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%B4-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0

 

JPA 에서 낙관적 락(Optimistic-Lock)을 이용해 동시성 처리하기

예제 및 테스트 코드는 github 에서 확인 가능합니다. 낙관적 락과 비관적 락의 차이점 이번엔 낙관적 락(Optimistic Lock) 을 이용해 동시성 처리를 하는 방법에 대해 알아보려 합니다. 그전에 낙관적

devoong2.tistory.com