하루한개/우아한 테크코스-10분테코톡

데이터베이스 락 (Database Lock, DB Lock)

ysk(0soo) 2022. 9. 3. 02:17

우아한 테크코스의 10분 테코톡을 정리한 글입니다

정의

Recored locking is the technique of preventing simultaneous access to data in a database,

to prevent inconsistent results.

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

  • Lock : 무엇인가 열리지 않도록 막는 장치

Lock이란?

DB에서는 Lock을 사용해 레코드(로우), 테이블, 테이블 전체의 접근을 막을 수 있다

  • 위 그림은 하나의 DB에 두 유저가 동시에 접근하는 것을 그린 예제
  • account는 계좌인데, 마이너스 통장이 아니다. 마이너스 통장이 아니면 계좌는 0원 밑으로 떨어지면 안된다.
  • 두명이 계좌에 동시에 접근해서 (동시성 문제) 0원 미만인 -20원으로 떨어지는 side effect 발생

위와 같은 문제를 방지하고 데이터의 일관성을 지키기위해 LOCK(잠금)을 걸고 관리하는 것을 Locking이라고 한다.

필요성

동시성

동시에 접근하려 수정하려할 때 DB의 일관성이 깨질 수 있음.

송금 시스템, 도서 관리 시스템, 수강 신청 시스템, 멀티 트랜잭션 환경에서 필요

ex)

  • 계좌 잔액이 3000원이고 A에게 5000원, B에게 3000원을 입금받았는데, 내 계좌에는 6000원만 남아있으면?
    • -> 다수가 동시에 요청해서 생긴 동시성 문제, lock이 필요
  • 책(토비의 스프링)은 한권인데 두명이 동시에 빌린 것으로 되어버리면?
    • -> 동시성 문제, 책을 빌리는 기능에 lock이 필요하다

동시에 접근하여 수정하려고 할 떄 데이터베이스의 일관성이 깨질 수 있으므로 Lock이 필요하다.

MySQL의 InnoDB Engine의 Lock의 종류

1. 배타 잠금(Exclusive Locks, X Lock)

  • write에 대한 Lock( 쓰기에 대한 락 )
  • SELECT ... FOR UPDATEUPDATE,DELETE 등의 수정, 삭제 쿼리 등에 exclusive Lock을 걸 수 있다.
  • exclusive lock이 걸려있을 경우 shared lock, exclusive lock 둘다 걸 수 없다. (쓰는 중 읽기, 쓰기, 수정, 삭제 불가능)
    • 락을 중복으로 걸 수 없다.

2. 공유 잠금(Shared Lock, S Lock)

  • READ에 대한 lock (읽기에 대한 락.
  • 일반적인 select 쿼리에 사용하는 것이 아닌 SELECT ... FOR SHARE(MySQl 8.0 버전 부터) 등 일부 쿼리는 READ 작업 수행시 lock을 건다.
    • 또는 SELECT ... LOCK IN SHARE MODE
  • shared lock이 걸려있을 경우 exclusive lock을 걸 수 없다. (읽는 중 수정, 삭제가 불가능하다)
  • 여러 transaction이 동시에 한 row에 shared lock을 걸 수 있다.

3. 레코드 락 (Record Lock, InnoDB에서 다른 DBMS와는 다르게 제공하는 락 )

  • InnoDB에서 Record Lock은 row가 아닌 DB index record에 걸리는 Lock
  • 여기도 동일하게 shard lock, exclusive lock 두종류가 존재한다.
  • 테이블에 index가 없다면?
    • 숨겨져 있는 clustered index를 사용하여 record를 잠근다.

4. 갭 락 (Gap Lock)

  • index record의 gap에 걸리는 lock
    • gap : index record가 없는 부분. 특정 범위의 레코드
    • 만약 id가 1,2,7,8인 칼럼이 존재하고 id 칼럼에 index가 걸려있다면 2 < id < 7 에 gap lock을 걸어 새로운 row가 추가되지 못하도록 막는다.
  • 조건에 해당하는 새로운 row가 추가되는 것을 방지하기 위한 것
  • record lock은 이미 존재하는 row가 변경되지 않도록 lock을 거는 반면,
  • gap lock은 새로운 row가 추가되는 것을 방지하기 위함

DB Deadlock 해결 방법

  • Deadlock이 걸렸을 때 Log를 확인하려면
    • SHOW ENGINE INNODB STATUS; 명령어
  • MySQL이나 오라클은 deadlock detection 이나 lock wait timeout을 사용하여 해결한다
  • deadlock detection 이 활성화 되어있으면 rollback할 작은 트랜잭션을 선택한다.
    • 트랜잭션의 크기는 insert, update, delete 된 행 수에 의해 결정하며, 오라클에서는 반드시 그렇지는 않다고 한다.
      • 즉 벤더사마다 다를 수가 있다.
  • deadlock detection 활성화 되어있지 않으면, lock wait timeout으로 해결한다

우리(백엔드 개발자들)가 Lock을 알아야 하는가?

  • DBA 있는데 왜 알아야해?
  • 난 안쓰고 개발 잘 했는데?
  • 그냥 알고만 있으면 되지? 쓸일은 없지

다음과 같은 프로그램 개발한다면 무조건 알아야 한다

  • 돈 관련 송금 시스템
  • 도서 관리 시스템 (재고 관리)
  • 수강 신청
  • 멀티 트랜잭션 환경

종류와 사용법

Optimistic Lock(낙관적 락)

  • 낙관적인 : 기본적으로 데이터 갱신시 충돌(동시성 문제)이 발생하지 않을 것이라고 낙관적으로 보는 것
  • 비선점적인 : 낙관적으로 예상하기 때문에 우선적으로 lock을 걸지 않음

그림을 통해 살펴보자.

  • 그림 - Optimistic Lock의 동작 원리
  • version 이라는 컬럼을 이용해서 관리
  • DB단에 관계 없이 어플리케이션 레벨에서 Lock을건다.
  • 충돌 방지 (충돌이 안나진 않는다. )
  • Optimistic Lock버전을 사용하여 관리를 한다.
  1. 그림과 같이 Client1Order에 접근을 할 때 Version 1Order에 접근한다.
  2. 동시에 Client2Version 1Order에 접근한다.
  3. Client1Order를 수정하거나 추가하고 Commit을 자신이 접근한 Version 1에 +1을 하여 Version 2로 날리게된다
    • 이제 DBOrder 버전은 '2'이다.
  4. Client2Order에 어떤 것을 추가하고 마찬가지로 Version 2Commit을 날리는데, 이미 Order의 버전은 2이기 때문에 OptimisticLockException을 발생시킨다.

JPA를 사용할 때의 예시 코드

@Entity
public class OptimisticLockingStudent {

  @Id
  private Long id;

  private String name;
  private String lastName;

  @Version
  private Integer version;
}
  • @Version 어노테이션사용. 알아서 동작한다.
  • 충돌이 일어나면 OptimisticLockException 이 발생한다

위와 같이 @Version을 사용한 필드를 두게되면 자동으로 commit시에 버전이 올라가고 충돌을 방지해준다.

데드락 가능성이 적고 성능의 이점을 가짐

하지만 충돌이 발생하면 오버헤드가 발생


Pessimistic Lock(비관적 락)

  • 비관적인 : 기본적으로 데이터 갱신시 충돌이 발생할 것이라고 비관적으로 보고 lock을 설정
  • 선점적인 : 비관적으로 보기 때문에, 우선적으로(조회할 때부터) lock을 검

조회를 할 때부터 lock을 걸어 다른 곳에서 접근할 시에 wait time이 발생

여기에는 두가지 동류가 존재하는데 Shared LockExclusive Lock이 존재한다.

  • Shared Lock (읽기 락이라고도 한다): 다른 사용자가 동시에 읽을 수는 있지만, 수정/삭제를 방지
  • Exclusive Lock (쓰기 락이라고도 한다.): 다른 사용자의 읽기/수정/삭제를 모두 방지
@Entity
public class PessimisticLockingStudent {

  @Id
  private Long id;

  private String name;
}

#----------------------------------

PessimisticLockingStudent student = entityManager.find(PessimisticLockingStudent.class, 1L);
entityManager.refresh(student, LockModeType.PESSIMISTIC_WRITE); // Exclusive Lock

PessimisticLockingStudent student2 = entityManager2.find(PessimisticLockingStudent.class, 1L);
entityManager2.refresh(student2, LockModeType.PESSIMISTIC_WRITE); // 조회에서 예외 발생

  • PESSIMISTIC_WRITE 모드 일때 Exclusive lock이 걸리고, 다른 트랜잭션이 조회하려고 하는 순간 바로 예외가 터진다
    • 다른트랜잭션이 find() 하는 순간 PerssimisticLockException 예외 발생

충돌에 대한 오버헤드가 줄어들고, 무결성을 지키기 용이

충돌이 없으면 오버헤드가 발생

간단 정리

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

적용

즉, 어떤 상황인지를 판단하는 것이 중요하다 . (Lock은 비용이 비싸니까)

  • 충돌이 자주 발생하는 상황인가?
  • 읽기와 수정의 비율이 어디에 가까운가?

일반적으로 웹 애플리케이션은 Optimistic Lock을 주로 사용한다. (읽기가 더 잦기 때문에)

추가로 공부하면 좋을 내용

  • 도메인에 대한 Lock 걸어야하나? 말아야하나?
  • PessimisticLock Scope - NORMAL/EXTENDED
    • 연관관계에 대해서 게시물에 대해 댓글을 가져올 때 같이 락을 걸 것인가?
    • 락이 결국 트랜잭션이랑 다 연관되서 사용된다.
  • Lock 관련 에외 처리 전략
  • 다양한 상황 연출해보기

참조