낙관적 락, 비관적 락 + MVCC, Repeatable Read , Non Blocking

Dayeon myeong·2022년 3월 2일
3

면접

목록 보기
3/35

Optimistic Locking(낙관적 락) 과 Pessimistic Locking(비관적 락) 에 대해 설명해주세요.

낙관적 락이란 이름 그대로 트랜잭션 대부분은 충돌이 발생하지 않는다고 낙관적으로 가정하는 방법이다. 실제로 DB단에서 락을 거는 게 아니고, 버전 관리 기능을 통해 트랜잭션의 Isolation 격리성을 관리한다. JPA에서는 @Version을 통해 엔티티에 버전 관리용 필드를 추가하여 낙관적 락을 사용할 수 있다. 엔티티 수정(update)하면 버전이 하나씩 자동으로 증가하는데, 엔티티를 조회했을 때의 버전과 수정했을 때의 버전이 다르다면 다른 트랜잭션에서 중간에 이미 엔티티를 수정한 것이기 때문에 예외가 발생한다. 내부적으로는 db에서 udate 쿼리문에 version 조건절을 붙여서 버전을 확인한다.

update board
set
	title = ?,
	version = ? (버전 +1 증가)
where
	id = ?
	and version = ? (버전 비교)

예를 들어서, 데이터 베이스 버전과 엔티티 버전이 같으면 데이터를 수정하면서 동시에 버전도 하나 증가시킨다. 만약 데이터베이스에 버전이 이미 증가해서 수정 중인 엔티티의 버전과 다르면 update 쿼리의 where 문에서 version 값이 다르므로 수정할 대상이 없다. 이때는 버전이 이미 증가한 것으로 판단해서 jpa가 예외를 발생시킨다.

반면 비관적 락은 이름 그대로 트랜잭션 충돌이 발생한다고 가정하고 우선 락을 걸고 보는 방식이다. DB에서 제공하는 락 기능을 사용한다. 대표적으로 select for update 구문(읽기 잠금)이 있다. 데이터베이스에 select for update를 사용해서 다른 트랜잭션이 수정하지 못하도록 한다.

MVCC를 사용하면 되지 않을까?

낙관적락이나 비관적 락은 다른 트랜잭션이 수정하는 것 자체를 막아버린다. 두개의 트랜잭션이 동시에 수정할 때 처음의 수정사항만 반영하도록 하여 second lost update problem 을 예방할 수 있다.

second lost update problem 두번의 갱신 분실 문제는
예를 들어 사용자 A와 B가 동시에 제목이 같은 공지사항을 수정한다고 생각해보자. 둘이 동시에 수정 화면을 열어서 내용을 수정하는 중에 사용자 A가 먼저 수정완료 버튼을 눌렀다. 잠시 후에 사용자 B가 수정완료 버튼을 눌렀다. 결과적으로 먼저 완료한 사용자 A의 수정 사항은 사라지고, 나중에 완료한 사용자 B의 수정사항만 남게 된다. 이것을 두 번의 갱신 분실 문제 second lost updates problem이라 한다.

반면, MVCC는 다른 트랜잭션이 수정하는 것 자체는 막지 못하고, 항상 일관된 읽기를 위해서 사용된다.
MVCC는 하나의 레코드에 대해 여러 개의 버전이 동시에 관리된다. MVCC는 LOCK을 사용하지 않고 일관된 읽기를 할 수 있다.
예를 들어 Read Committed 격리레벨에서는 non-repeatable read란 문제가 있다.
non-repeatable read문제는 선행 트랜잭션이 select 문을 통해 읽은 레코드를 다른 트랜잭션이 update, delete할 수 있어서 선행 트랜잭션이 그 후에 다시 select 조회시에 같은 결과가 나오지 않는 것을 얘기한다.
이 때 MVCC를 사용하면 다른 트랜잭션이 데이터를 update 수정하는 것은 막지는 못하지만 항상 선행 트랜잭션이 select할 때 같은 조회결과가 나오도록 할 수 있다. 즉, mvcc를 쓰면 read committed 격리 레벨에서도 non-repeatable read 문제를 해결할 수 있는 것이다. 이는 select 시에 변경되기 이전의 내용을 보관하고 있는 언두 로그에서 데이터를 가져오기 때문이다.

낙관적 락보다는 DB 트랜잭션 격리 레벨을 Repeatable Read로 하면 되지 않을까?

Repeatable read는 선행 트랜잭션이 종료시까지 다른 트랜잭션이 update, delete하지 못하도록 아예 락을 건다. 반면에 낙관적 락은 애플리케이션 단에서 락을 걸지 않아 트랜잭션 자체를 blocking하지 않으면서도 다른 트랜잭션이 수정하는 것을 막아준다. lock을 거는 것은 성능에 영향을 줄 수 있다. 따라서 repeatable read 격리레벨을 설정하여 락을 거는 것보다는 낙관적 락을 통해 blocking하지 않고, 애플리케이션 단에서 트랜잭션 격리성을 보장하는 것이 낫다고 생각한다.

낙관적 락과 비관적 락에 대하여 설명하고, 경합성 측면에서 둘을 비교하시오.

낙관적 락과 비관적락은 트랜잭션 개념이지만, 어찌보면 멀티 스레드 환경에서의 동기화 문제에서의 synchronized와 같은 락을 거는 방식과 Atomic Variable을 통해 동기화 문제를 해결하는 것과 비슷하다고 볼 수 있다.

synchronized는 모니터 락을 가진 객체 인스턴스를 지정해서 모니터락을 통해 동기화 문제를 관리한다. 그래서 synchronized를 사용하면 다른 스레드에서 접근하지 않고 값을 갱신하지 않더라도 무조건 락부터 건다. 스레드가 아예 blocking 되는 것이다. 이는 비관적 락과 비슷하다고 볼 수 있다. 왜냐하면 비관적락도 무조건 일단 DB단에서 아예 락을 거는 것이기 때문이다. 비관적 락은 이름 그대로 트랜잭션 충돌이 발생한다고 가정하고 DB가 제공하는 락을 사용한다. 대표적으로 select for update 쿼리문이 있다.

Atomic Variable은 자바 기준으로 CAS (CompareAndSwap) 기반으로 동기화 문제를 해결한다. 예를 들어 AtomicInteger의 increment()라는 메서드는 내부적으로는 while문을 계속 돌면서 CPU busy wait 방식으로 CAS 메서드를 수행한다. CAS 메서드는 현재 스레드가 lock 변수 v를 획득해도 되는지 , 다른 스레드가 lock 변수 v를 획득해서 임계영역을 수행중인지 확인할 수 있다.

void increment(Atomic_vairable v) {
	int temp;
	do {
		temp = *v;
	} while (temp != compare_and_swap(v, temp,temp + 1));
} 


int compare_and_swap(int *value, int expected, int new_value) {
	int temp = *value;
	
	if (*value == expected)
		*value = new_value;

	return temp;

}

이 CPU busy wait 방식은 낙관적 락과 비슷하다고 볼 수 있다. 낙관적락은 이름 그대로 트랜잭션 대부분은 충돌이 발생하지 않는다고 낙관적으로 가정하는 것에서 출발한다. 그래서 JPA에서는 @Version을 통해 이 낙관적 락이 가능하다. 애플리케이션에서 락이 아닌 버전 관리 기능을 통해 트랜잭션 격리성을 관리한다. non-blocking 한다는 차원에서 둘을 비슷하다고 볼 수 있다.

각각의 락을 사용할 상황 또는 제품 사례를 말씀해주세요.

낙관적 락

  • 트랜잭션 충돌이 일어나지 않을 거라는 낙관적인 경우
  • 애플리케이션단에서 동시 수정을 막음
  • 트랜잭션을 커밋하는 시점에 충돌을 안다
  • 충돌이 나면 롤백 처리는 개발자의 몫

ex. 여러 사용자가 상품을 구매해서 동시에 상품 잔여 수량을 수정해야되는 상황에서 낙관적락을 통해 동시 수정을 막을 수 있다.

비관적 락

  • 트랜잭션 충돌이 자주 일어날 거라는 비관적인 경우
  • DB단에서 동시 수정을 막음
  • 데이터를 수정하는 즉시 트랜잭션 충돌을 감지할 수 있다.
  • 충돌이 나면 DB에서 롤백을 자동으로 처리

ex. 롤백을 개발자가 일일이 하는 것이 힘든 경우. 충돌일어났을 때 롤백 비용이 많이 드는 경우. 주문시에 쿠폰 사용, 알람 제공, 주문서 작성, 수량 수정 등 여러 기능이 한 트랜잭션에 엮어있을 때

참고

Real MySQL - MVCC, 트랜잭션과 잠금

클린코드 동시성 2 - 스레드를 차단하지 않는 non blocking 방법

자바 ORM 표준 JPA 프로그래밍 - 낙관적락 파트

profile
부족함을 당당히 마주하는 용기

0개의 댓글