초이로그

데이터베이스 락 본문

우아한테크코스/테코톡 정리

데이터베이스 락

수연초이 2022. 11. 9. 15:49

요약

  Optimistic Lock Pessimistic Lock
정의 충돌이 없을 것이라 예상 충돌을 예상하고 미리 락을 건다
사용방법 JPA를 사용하면 @Version
동작원리가 단순해서 만들기 쉬움
Mode 설정 및 쿼리에 직접 사용
DB 단에서 설정가능
별명 낙관적인 락 / 비선점적인 락 비관적인 락 / 선점적인 락
장점 데드락 가능성이 적으며 성능의 이점 충돌에 대한 오버헤드가 줄어든다
무결성을 지키기 용이하다
단점 충돌이 발생하면 오버헤드 발생 충돌이 없으면 오버헤드 발생

정의

데이터베이스의 일관성무결성을 유지하기 위해 트랜잭션순차적 진행을 보장할 수 있는 직렬화 장치

데이터의 일관성을 지키기 위해 LOCK(잠금)을 걸고 이를 관리하는 것이 Locking

여러 개의 트랜재션에서 동시에 접근하여 수정하려고 할 때, 일관성이 깨질 수 있다. 잠금을 사용하자.

예) 두 명이 A에게 3,000원씩 입금했지만 총 입금된 금액은 3,000원

 

종류

Optimistic Lock

  • 낙관적 - 기본적으로 데이터 갱신 시 충돌이 발생하지 않을 것이라고 낙관적으로 보는 것
  • 비섬전적 - 데이터 갱신 시 충돌이 발생하지 않을 것이라고 예상하기 때문에 우선적으로 락을 걸지 않음
  • Version을 사용해 관리
  • 어플리케이션 레이어에서 거는 락(DB에서 관리하는 것은 없음)
    • 데드락 가능성이 적고 성능의 이점
  • 충돌 방지용
    • 커밋 시점에 충돌이 발생하면 이전에 실행된 것이 모두 롤백(오버헤드)
@Entity
public class Student {

    @Id
    private Long id;
    
    @Version // 버전 애너테이션을 통해 자동으로 관리된다
    private Integer version;
}
@Test(expected = OptimisticLockException.class)
void test() {
 try {
 	EntityManager em = getEntityManagerWithOpenTransaction(); 
    	Student student = em.findStudent(Student.class, 1L); // Version 1
    
 	EntityManager em2 = getEntityManagerWithOpenTransaction();
        Student student2 = em2.findStudent(Student.class, 1L); // Version 2
        student2.setName("페퍼");
        em2.persist(student2);
        em2.getTransaction()
            .commit(); // Version2
        em2.close();

        student1.setName("그린론");
        em.getTransaction()
            .commit(); // Version 1,2의 충돌 
        em.close();
 } catch (Exception e) {
 	// ...
 }
}

두 트랜잭션 모두 버전1의 Order를 조회. 트랜잭션 1이 커밋하면 버전이 2가 됨. 트랜잭션 2에서 커밋할 때 버전 2가 되었음을 확인하고 Optimistic Lock Exception 터짐

Pessimistic Lock

  • 비관적인 - 기본적으로 데이터 갱신 시 충돌이 발생할 것이라고 비관적으로 보고 미리 잠금을 거는 것
  • 선점적인 - 데이터 갱신 시 충돌이 발생할 것이라고 예상하므로 우선적으로 락을 건다(조회할 때부터 건다)
  • 조회할 때 DB에 락을 건다
  • 데이터베이스 락
  • 무결성의 장점
  • 락을 실제로 걸기 때문에  데드락의 위험성
    • 충돌이 없으면 락 자체가 비용이라 오버헤드 발생
  • 다양한 추가 설정
    • Shared Lock: 다른 사용자가 동시에 읽을 수는 있지만, Update Delete를 방지함. S Lock이 걸린 row에 S Lock은 걸 수 있지만 X Lock은 걸 수 없다.
    • Exclusive Lock(X Lock): 다른 사용자가 읽기, 수정, 삭제 모두를 불가능하게 함. X Lock이 걸리면 다른 트랜잭션은 S Lock, X Lock 둘 다 걸 수 없다.
    • Record Lock: MySQL의 InnoDB에서 제공하는 Lock. 락은 동일한 조건의 record(=row)에 걸릴 수 있는데 innoDB에서는 다른 rdbms와 달리 테이블 레코드가 아닌 인덱스 레코드에 락을 건다. (X Lock, S Lock 모두 인덱스 레코드에 걸린다) 인덱스가 없는 경우 테이블 내 숨겨져있는 clustered index를 사용하여 record 잠금
    • Gap Lock: 인덱스 레코드의 gap(인덱스 레코드에서 없는 부분)에 걸리는 Lock. 조건에 해당하는 새로운 row가 추가되는 것을 방지
    • 이 외에도 Next Key Lock, Table Lock 등이 존재

트랜잭션1이 이미 Order를 조회한 경우, 트랜잭션 2는 1이 끝날 때까지 waiting

@Test
void test() {
 try {
 	EntityManager em = getEntityManagerWithOpenTransaction();
        Student student = em.findStudent(Student.class, 1L);
        em.refresh(student, LockType.PESSIMISTIC_WITH); // Exclusive Lock
    
 	EntityManager em2 = getEntityManagerWithOpenTransaction();
        Student student2 = em2.findStudent(Student.class, 1L); // 조회에서 예외 발생
        // 이후 로직 모두 수행 X
 } catch (Exception e) {
 	// ...
 }
}
  • 버전을 사용할 수는 있지만 잘 사용하지 않음

Optimistic vs Pessimistic

언제 무엇을 사용할까?

락은 비용이 크기 때문에 어떤 상황인가가 중요하다. 

  • 충돌이 자주 발생하는 상황인가? ➡️ Pessimistic
  • 읽기와 수정하기의 비율은 어디에 가까운가?
    • 읽기 비율이 크다 ➡️ Optimistic 또는 락을 걸지 않는다
    • 수정 비율이 크다면 ➡️ Pessimistic
      • 요즘에는 오버헤드가 많이 줄어들어서 수정하기도 Optimistic도 사용한다고 함
  • 일반적인 웹 애플리케이션은 읽기 요청이 대부분이라 Optimistic 락 주로 사용

 

DB에서 발생하는 DeadLock

상황

트랜잭션 1은 트랜잭션 2에서 Lock이 걸린 자원을 원하고, 트랜잭션 2는 1에서 Lock이 걸린 자원을 원하는 경우 발생

해결 방법

  • deadlock detection 또는 lock wait timeout (mysql, oracle 기준)
  • Deadelock detection이 활성화 되어있으면 rollback할 작은 트랜잭션 선택
    • 트랜잭션 크기는 Insert, update, delete 된 행 수에 의해 결정. 하지만 oracle에서는 반드시 그렇지 않음. 벤더 사마다 다르다
  • 활성화 되어있지 않으면 설정된 lock wait timeout으로 해결

 

추가로 공부하면 좋을 내용

  • 도메인에 대한 Lock. 걸어야할까 말아야할까
    • 게시판, 좋아요에 LOCK?
  • PessimisticLock Scope - NORMAL / EXTENDED
    • 게시물을 가져올 때 댓글까지 LOCK?
  • LOCK 관련 예외 처리 전략
  • 다양한 상황 연출해보기
    • 트랜잭션 격리레벨에 따른 LOCK

 

'우아한테크코스 > 테코톡 정리' 카테고리의 다른 글

서블릿 필터 & 스프링 인터셉터  (0) 2022.11.15
JDK Dynamic Proxy vs CGLIB Proxy  (3) 2022.10.18
Spring AOP  (0) 2022.10.14
인덱스  (0) 2022.10.14
CI/CD와 무중단 배포  (0) 2022.10.09